diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..8aa9bde
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d055958..dc181dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,7 +15,6 @@ add_subdirectory(cpu)
add_subdirectory(ppu)
add_subdirectory(mappers)
add_subdirectory(rom)
-add_subdirectory(utils)
add_subdirectory(gui)
list(APPEND EXTRA_INCLUDES
@@ -23,8 +22,6 @@ list(APPEND EXTRA_INCLUDES
"${PROJECT_SOURCE_DIR}/ppu"
"${PROJECT_SOURCE_DIR}/mappers"
"${PROJECT_SOURCE_DIR}/rom"
- "${PROJECT_SOURCE_DIR}/debugger"
- "${PROJECT_SOURCE_DIR}/utils"
"${PROJECT_SOURCE_DIR}/gui")
set(HEADERS include/system.h include/types.h)
@@ -32,7 +29,7 @@ set(SOURCE main.c system.c)
add_executable(nes_emulator ${HEADERS} ${SOURCE})
-target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_utils nes_gui log.c)
+target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_gui log.c)
target_include_directories(nes_emulator PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES})
\ No newline at end of file
diff --git a/README.md b/README.md
index 3df2e58..d7d5229 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ only tested on Linux. Here is how to run the project:
- PPU
- Registers: Done
- VRAM: Done
- - Background rendering: In Progress
+ - Background rendering: Done
- Sprite rendering: To Do
- Input: To Do
- APU: To Do
diff --git a/debugger/program_view.h b/debugger/program_view.h
index 4646556..e1e59bc 100644
--- a/debugger/program_view.h
+++ b/debugger/program_view.h
@@ -10,7 +10,7 @@
#include "../cpu/decoding.h"
#include "cursor.h"
#include "window.h"
-#include "../utils/linked_list.h"
+#include "components/linked_list.h"
#define PROGRAM_VIEW_HEIGHT 19
#define PROGRAM_VIEW_WIDTH 42
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt
index 06bb855..d93b2e4 100644
--- a/gui/CMakeLists.txt
+++ b/gui/CMakeLists.txt
@@ -1,5 +1,5 @@
-set(HEADERS gui.h window.h main_window.h)
-set(SOURCE gui.c window.c main_window.c)
+set(HEADERS gui.h main_window.h)
+set(SOURCE gui.c main_window.c)
if (NES_DEBUG)
list(APPEND HEADERS char_map.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h dbg_palette.h)
@@ -8,7 +8,6 @@ endif (NES_DEBUG)
add_library(nes_gui ${SOURCE} ${HEADERS})
-find_package(SDL2 REQUIRED)
-include_directories(nes_gui ${SDL2_INCLUDE_DIRS})
+add_subdirectory(components)
-target_link_libraries(nes_gui log.c ${SDL2_LIBRARIES} SDL2_ttf)
\ No newline at end of file
+target_link_libraries(nes_gui nes_gui_components log.c SDL2_ttf)
\ No newline at end of file
diff --git a/gui/components/CMakeLists.txt b/gui/components/CMakeLists.txt
new file mode 100644
index 0000000..5427b72
--- /dev/null
+++ b/gui/components/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(HEADERS component.h linked_list.h window.h window_menu.h)
+set(SOURCE linked_list.c window.c window_menu.c)
+
+add_library(nes_gui_components ${SOURCE} ${HEADERS})
+
+find_package(SDL2 REQUIRED)
+include_directories(nes_gui_components ${SDL2_INCLUDE_DIRS})
+
+target_link_libraries(nes_gui_components ${SDL2_LIBRARIES})
\ No newline at end of file
diff --git a/gui/components/component.h b/gui/components/component.h
new file mode 100644
index 0000000..debe5c0
--- /dev/null
+++ b/gui/components/component.h
@@ -0,0 +1,20 @@
+//
+// Created by william on 8/24/24.
+//
+
+#ifndef NES_EMULATOR_COMPONENT_H
+#define NES_EMULATOR_COMPONENT_H
+
+typedef struct component {
+ void *ref;
+
+ void (*render)(void *ref);
+
+ void (*destroy)(void *ref);
+
+ bool (*mouse_motion)(void *ref, int x, int y);
+
+ bool (*mouse_click)(void *ref);
+} Component;
+
+#endif //NES_EMULATOR_COMPONENT_H
diff --git a/gui/components/linked_list.c b/gui/components/linked_list.c
new file mode 100644
index 0000000..7f2e9fb
--- /dev/null
+++ b/gui/components/linked_list.c
@@ -0,0 +1,87 @@
+//
+// Created by william on 1/16/24.
+//
+
+#include
+#include
+#include
+#include
+#include "linked_list.h"
+
+LinkedList linked_list_create(bool circular) {
+ LinkedList list;
+
+ list.circular = circular;
+ list.size = 0;
+ list.head = NULL;
+ list.end = NULL;
+
+ return list;
+}
+
+void linked_list_add(LinkedList *list, void *data) {
+ assert(list != NULL);
+ assert(data != NULL);
+
+ LinkedListNode *node = malloc(sizeof(LinkedListNode));
+ if (node == NULL) {
+ perror("Failed to allocate memory for linked list node");
+ exit(EXIT_FAILURE);
+ }
+
+ node->data = data;
+ node->previous = list->end;
+
+ if (list->head == NULL) {
+ list->head = node;
+ }
+
+ if (list->end != NULL) {
+ list->end->next = node;
+ }
+
+ if (list->circular) {
+ node->next = list->head;
+ } else {
+ node->next = NULL;
+ }
+
+ list->end = node;
+ list->size++;
+}
+
+void linked_list_destroy(LinkedList *list) {
+ assert(list != NULL);
+
+ LinkedListNode *node = list->head;
+ while (node != NULL) {
+ LinkedListNode *current_node = node;
+ node = node->next;
+
+ free(current_node);
+
+ if (node == list->head) {
+ // The list may be circular, we don't want an infinite free loop
+ break;
+ }
+ }
+}
+
+LinkedListCursor linked_list_cursor_create(LinkedList *list) {
+ LinkedListCursor cursor = {list->head};
+ return cursor;
+}
+
+LinkedListNode *linked_list_cursor_next(LinkedListCursor *cursor) {
+ if (cursor->current == NULL) {
+ return NULL;
+ }
+
+ LinkedListNode *next_node = cursor->current->next;
+ cursor->current = next_node;
+ return next_node;
+}
+
+bool linked_list_cursor_has_next(LinkedListCursor *cursor) {
+ return cursor->current != NULL && cursor->current->next != NULL;
+}
\ No newline at end of file
diff --git a/gui/components/linked_list.h b/gui/components/linked_list.h
new file mode 100644
index 0000000..cf29b10
--- /dev/null
+++ b/gui/components/linked_list.h
@@ -0,0 +1,71 @@
+//
+// Created by william on 1/16/24.
+//
+
+#include
+
+#ifndef NESEMULATOR_LINKED_LIST_H
+#define NESEMULATOR_LINKED_LIST_H
+
+typedef struct linked_list_node {
+ struct linked_list_node *previous;
+ struct linked_list_node *next;
+ void *data;
+} LinkedListNode;
+
+typedef struct linked_list {
+ bool circular;
+ unsigned int size;
+ LinkedListNode *head;
+ LinkedListNode *end;
+} LinkedList;
+
+typedef struct linked_list_cursor {
+ LinkedListNode *current;
+} LinkedListCursor;
+
+/**
+ * Initializes a new linked list.
+ *
+ * @param circular If the list is circular, meaning that the last node is linked to the first node.
+ * @return The linked list instance
+ */
+LinkedList linked_list_create(bool circular);
+
+/**
+ * Adds data to a linked list.
+ *
+ * @param list The linked list
+ * @param data The data to add
+ */
+void linked_list_add(LinkedList *list, void *data);
+
+/**
+ * Destroys a linked list. The data must be freed before.
+ *
+ * @param list The list to destroy
+ */
+void linked_list_destroy(LinkedList *list);
+
+/**
+ * Creates a read cursor for a linked list.
+ * @param list The list
+ * @return A cursor initialized to the first item in the list.
+ */
+LinkedListCursor linked_list_cursor_create(LinkedList *list);
+
+/**
+ * Gets the next node in the list.
+ *
+ * @param cursor A read cursor for the list
+ * @return The next node in the list. Can be NULL if the list is empty or depleted.
+ */
+LinkedListNode *linked_list_cursor_next(LinkedListCursor *cursor);
+
+/**
+ * Checks if there is nodes remaining after a cursor.
+ * @param cursor A cursor
+ */
+bool linked_list_cursor_has_next(LinkedListCursor *cursor);
+
+#endif //NESEMULATOR_LINKED_LIST_H
diff --git a/gui/components/window.c b/gui/components/window.c
new file mode 100644
index 0000000..a2d86ad
--- /dev/null
+++ b/gui/components/window.c
@@ -0,0 +1,134 @@
+//
+// Created by william on 17/05/24.
+//
+
+#include
+#include "window.h"
+#include "log.h"
+#include "component.h"
+
+/**
+ * Easy declaration of commonly used component loop. The current component is in the 'component' variable.
+ * Put the loop's body between FOR_EACH_COMPONENT and END_FOR_EACH_COMPONENT.
+ */
+#define FOR_EACH_COMPONENT(window) \
+ Component *component; \
+ LinkedListCursor cursor = window_create_component_cursor(window, &component); \
+ while (component != NULL) {
+
+#define END_FOR_EACH_COMPONENT \
+ component = get_next_component(&cursor); \
+ }
+
+static inline LinkedListCursor window_create_component_cursor(Window *window, Component **first_component) {
+ LinkedListCursor cursor = linked_list_cursor_create(&window->components);
+ if (cursor.current != NULL) {
+ *first_component = cursor.current->data;
+ } else {
+ *first_component = NULL;
+ }
+
+ return cursor;
+}
+
+static inline Component *get_next_component(LinkedListCursor *cursor) {
+ if (!linked_list_cursor_has_next(cursor)) {
+ return NULL;
+ }
+
+ LinkedListNode *next_node = linked_list_cursor_next(cursor);
+ return next_node->data;
+}
+
+Window window_create(char *title, int width, int height, int scale) {
+ WindowSdlContext context;
+
+ int renderer_flags = SDL_RENDERER_ACCELERATED;
+ int window_flags = 0;
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ log_error("Couldn't initialize SDL: %s", SDL_GetError());
+ exit(-1);
+ }
+
+ int actual_width = width * scale;
+ int actual_height = height * scale;
+ context.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, actual_width,
+ actual_height, window_flags);
+ if (!context.window) {
+ log_error("Failed to open %d x %d SDL window: %s", actual_width, actual_height, SDL_GetError());
+ exit(-1);
+ }
+
+ context.renderer = SDL_CreateRenderer(context.window, -1, renderer_flags);
+ if (!context.renderer) {
+ log_error("Failed to create renderer: %s", SDL_GetError());
+ SDL_DestroyWindow(context.window);
+ exit(-1);
+ }
+
+ Window window;
+ window.width = width;
+ window.height = height;
+ window.scale = scale;
+ window.sdl_context = context;
+ window.components = linked_list_create(false);
+
+ return window;
+}
+
+SDL_Texture *window_create_texture(Window *window, int access) {
+ SDL_Renderer *renderer = window->sdl_context.renderer;
+ return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, access, window->width, window->height);
+}
+
+void window_add_component(Window *window, Component *component) {
+ LinkedList *components = &window->components;
+ linked_list_add(components, component);
+}
+
+void window_render_components(Window *window) {
+ FOR_EACH_COMPONENT(window)
+ component->render(component->ref);
+ END_FOR_EACH_COMPONENT
+}
+
+void window_render_texture(Window *window, SDL_Texture *texture) {
+ SDL_Renderer *renderer = window->sdl_context.renderer;
+ SDL_RenderClear(renderer);
+ SDL_RenderCopy(renderer, texture, NULL, NULL);
+}
+
+void window_present(Window *window) {
+ SDL_RenderPresent(window->sdl_context.renderer);
+}
+
+void window_destroy(Window *window) {
+ FOR_EACH_COMPONENT(window)
+ component->destroy(component->ref);
+ free(component);
+ END_FOR_EACH_COMPONENT
+
+ SDL_DestroyRenderer(window->sdl_context.renderer);
+ SDL_DestroyWindow(window->sdl_context.window);
+}
+
+void window_mouse_motion(Window *window, int x, int y) {
+ FOR_EACH_COMPONENT(window)
+ bool matching = component->mouse_motion(component->ref, x, y);
+ if (matching) {
+ // If a component has matched, we don't pass the event to the next ones
+ // This will prevent, for example, clicking on a UI element behind another one.
+ break;
+ }
+ END_FOR_EACH_COMPONENT
+}
+
+void window_mouse_click(Window *window) {
+ FOR_EACH_COMPONENT(window)
+ bool matching = component->mouse_click(component->ref);
+ if (matching) {
+ break;
+ }
+ END_FOR_EACH_COMPONENT
+}
\ No newline at end of file
diff --git a/gui/components/window.h b/gui/components/window.h
new file mode 100644
index 0000000..78be6aa
--- /dev/null
+++ b/gui/components/window.h
@@ -0,0 +1,91 @@
+//
+// Created by william on 17/05/24.
+//
+
+#ifndef NES_EMULATOR_WINDOW_H
+#define NES_EMULATOR_WINDOW_H
+
+#include
+#include "linked_list.h"
+#include "component.h"
+
+typedef struct window_sdl_context {
+ SDL_Renderer *renderer;
+ SDL_Window *window;
+} WindowSdlContext;
+
+typedef struct window {
+ int width;
+ int height;
+ int scale;
+
+ WindowSdlContext sdl_context;
+ LinkedList components;
+} Window;
+
+/**
+ * Creates a window.
+ * @param title The title of the window
+ * @param width The width in pixels
+ * @param height The height in pixels
+ * @param scale The scale of pixels of this window (number of real pixels per drawn pixel)
+ * @return The created window
+ */
+Window window_create(char *title, int width, int height, int scale);
+
+/**
+ * Creates a background_texture for a window. (same width/height)
+ * @param window A reference to the window
+ * @param access The SDL background_texture access type
+ * @return An SDL background_texture for the window
+ */
+SDL_Texture *window_create_texture(Window *window, int access);
+
+/**
+ * Adds a component to a window.
+ * @param window A reference to the window
+ * @param component A reference to the component to add
+ */
+void window_add_component(Window *window, Component *component);
+
+/**
+ * Renders the components of a window (but not the window itself)
+ * @param window The window to render components
+ */
+void window_render_components(Window *window);
+
+/**
+ * Renders a background_texture in the window.
+ * @param window A reference to the window
+ * @param texture The background_texture to render
+ */
+void window_render_texture(Window *window, SDL_Texture *texture);
+
+/**
+ * Presents a window.
+ * @param window A reference to the window
+ */
+void window_present(Window *window);
+
+/**
+ * Destroys a window.
+ * Free the ressources of the window and its components.
+ * @param window The window to destroy
+ */
+void window_destroy(Window *window);
+
+/**
+ * Handles mouse motion events in the window.
+ * @param window A reference to the window
+ * @param x The x position of the mouse
+ * @param y The y position of the mouse
+ */
+void window_mouse_motion(Window *window, int x, int y);
+
+/**
+ * Handles mouse click events in the window.
+ * @param window A reference to the window
+ */
+void window_mouse_click(Window *window);
+
+#endif //NES_EMULATOR_WINDOW_H
\ No newline at end of file
diff --git a/gui/components/window_menu.c b/gui/components/window_menu.c
new file mode 100644
index 0000000..cbe2b95
--- /dev/null
+++ b/gui/components/window_menu.c
@@ -0,0 +1,251 @@
+//
+// Created by william on 8/23/24.
+//
+
+#include
+#include
+#include "window_menu.h"
+#include "../../include/types.h"
+#include "log.h"
+
+/**
+ * Easy declaration of commonly used item loop. The current item is in the 'item' variable.
+ * Put the loop's body between FOR_EACH_ITEM and END_FOR_EACH_ITEM.
+ */
+#define FOR_EACH_ITEM_(list)\
+ MenuItemComponent *item; \
+ LinkedListCursor cursor = menu_create_item_cursor(&(list), &item); \
+ while (item != NULL) {
+
+#define FOR_EACH_ITEM(menu) FOR_EACH_ITEM_((menu)->items)
+#define FOR_EACH_SUBITEM(menu_item) FOR_EACH_ITEM_((menu_item)->sub_items)
+
+#define END_FOR_EACH_ITEM \
+ item = menu_get_next_item(&cursor); \
+ }
+
+#define CREATE_PIXEL_TEXTURE(texture, menu, color) \
+ pixel texture ## _buffer[1] = {color}; \
+ (menu)->texture = SDL_CreateTexture((menu)->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 1, 1); \
+ SDL_UpdateTexture((menu)->texture, NULL, &texture ## _buffer, sizeof(pixel))
+
+static inline LinkedListCursor menu_create_item_cursor(LinkedList *list, MenuItemComponent **first_component) {
+ LinkedListCursor cursor = linked_list_cursor_create(list);
+ if (cursor.current != NULL) {
+ *first_component = cursor.current->data;
+ } else {
+ *first_component = NULL;
+ }
+
+ return cursor;
+}
+
+static inline MenuItemComponent *menu_get_next_item(LinkedListCursor *cursor) {
+ if (!linked_list_cursor_has_next(cursor)) {
+ return NULL;
+ }
+
+ LinkedListNode *next_node = linked_list_cursor_next(cursor);
+ return next_node->data;
+}
+
+MenuComponent *menu_create(Window *window, TTF_Font *font) {
+ MenuComponent *menu = malloc(sizeof(MenuComponent));
+
+ menu->window_width = window->width * window->scale;
+ menu->visible = false;
+
+ menu->items = linked_list_create(false);
+ menu->highlight_item = NULL;
+
+ menu->renderer = window->sdl_context.renderer;
+ menu->font = font;
+ CREATE_PIXEL_TEXTURE(background_texture, menu, MENU_BACKGROUND_COLOR);
+ CREATE_PIXEL_TEXTURE(highlight_texture, menu, MENU_HIGHLIGHT_COLOR);
+
+ return menu;
+}
+
+MenuItemComponent *menu_item_create(char *label, on_click_callback on_click) {
+ MenuItemComponent *menu_item = malloc(sizeof(MenuItemComponent));
+
+ menu_item->label = label;
+ menu_item->on_click = on_click;
+ menu_item->sub_items = linked_list_create(false);
+ menu_item->is_highlighted = false;
+
+ return menu_item;
+}
+
+void menu_append(MenuComponent *menu, MenuItemComponent *menu_item) {
+ LinkedList *menu_items = &menu->items;
+ linked_list_add(menu_items, menu_item);
+}
+
+void menu_item_append(MenuItemComponent *menu_item, MenuItemComponent *sub_item) {
+ LinkedList *sub_items = &menu_item->sub_items;
+ linked_list_add(sub_items, sub_item);
+}
+
+void menu_item_build(MenuItemComponent *menu_item, SDL_Renderer *renderer, TTF_Font *font, bool top_level) {
+ SDL_Color label_color = MENU_TEXT_COLOR;
+ int base_x = menu_item->collision_rect.x;
+ int base_y = MENU_HEIGHT + MENU_ITEM_MARGIN_Y;
+
+ if (!top_level) {
+ base_x += menu_item->collision_rect.w;
+ base_y = menu_item->collision_rect.y + MENU_ITEM_MARGIN_Y;
+ }
+
+ FOR_EACH_SUBITEM(menu_item)
+ SDL_Surface *label_surface = TTF_RenderText_Solid(font, item->label, label_color);
+
+ item->label_texture = SDL_CreateTextureFromSurface(renderer, label_surface);
+ SDL_Rect draw_rect = {base_x + MENU_ITEM_MARGIN_X, base_y, label_surface->w, label_surface->h};
+ item->draw_rect = draw_rect;
+
+ SDL_Rect menu_item_col = {base_x, base_y - MENU_ITEM_MARGIN_Y, label_surface->w + MENU_ITEM_MARGIN_X * 2,
+ MENU_HEIGHT};
+ item->collision_rect = menu_item_col;
+ base_y += item->draw_rect.h + MENU_ITEM_MARGIN_Y * 2;
+
+ SDL_FreeSurface(label_surface);
+
+ menu_item_build(item, renderer, font, false);
+ END_FOR_EACH_ITEM
+}
+
+void menu_build(MenuComponent *menu) {
+ assert(menu->font != NULL);
+
+ SDL_Color label_color = MENU_TEXT_COLOR;
+ int next_x = MENU_ITEM_MARGIN_X;
+ FOR_EACH_ITEM(menu)
+ SDL_Surface *label_surface = TTF_RenderText_Solid(menu->font, item->label, label_color);
+
+ item->label_texture = SDL_CreateTextureFromSurface(menu->renderer, label_surface);
+ SDL_Rect draw_rect = {next_x, MENU_ITEM_MARGIN_Y, label_surface->w, label_surface->h};
+ item->draw_rect = draw_rect;
+
+ SDL_Rect menu_item_col = {next_x - MENU_ITEM_MARGIN_X, 0, label_surface->w + MENU_ITEM_MARGIN_X * 2,
+ MENU_HEIGHT};
+ item->collision_rect = menu_item_col;
+ next_x += item->draw_rect.w + MENU_ITEM_MARGIN_X * 2;
+
+ SDL_FreeSurface(label_surface);
+
+ menu_item_build(item, menu->renderer, menu->font, true);
+ END_FOR_EACH_ITEM
+}
+
+void menu_item_render(MenuComponent *menu, MenuItemComponent *menu_item) {
+ SDL_Texture *back_texture = menu_item->is_highlighted ? menu->highlight_texture : menu->background_texture;
+ SDL_RenderCopy(menu->renderer, back_texture, NULL, &menu_item->collision_rect);
+ SDL_RenderCopy(menu->renderer, menu_item->label_texture, NULL, &menu_item->draw_rect);
+
+ if (!menu_item->is_highlighted) {
+ return;
+ }
+
+ FOR_EACH_SUBITEM(menu_item)
+ menu_item_render(menu, item);
+ END_FOR_EACH_ITEM
+}
+
+void menu_render(MenuComponent *menu) {
+ if (!menu->visible) {
+ return;
+ }
+
+ int display_width = menu->window_width;
+ int display_height = MENU_HEIGHT;
+ SDL_Rect menu_rect = {0, 0, display_width, display_height};
+ SDL_RenderCopy(menu->renderer, menu->background_texture, NULL, &menu_rect);
+
+ FOR_EACH_ITEM(menu)
+ menu_item_render(menu, item);
+ END_FOR_EACH_ITEM
+}
+
+void menu_item_list_destroy(LinkedList *list) {
+ MenuItemComponent *menu_item;
+ LinkedListCursor cursor = menu_create_item_cursor(list, &menu_item);
+ while (menu_item != NULL) {
+ menu_item_destroy(menu_item);
+
+ menu_item = menu_get_next_item(&cursor);
+ }
+
+ linked_list_destroy(list);
+}
+
+void menu_destroy(MenuComponent *menu) {
+ menu_item_list_destroy(&menu->items);
+
+ SDL_DestroyTexture(menu->background_texture);
+ free(menu);
+}
+
+void menu_item_destroy(MenuItemComponent *menu_item) {
+ SDL_DestroyTexture(menu_item->label_texture);
+
+ menu_item_list_destroy(&menu_item->sub_items);
+ free(menu_item);
+}
+
+/**
+ * Checks if a point is over a menu item.
+ * This function will return true if the point is in an item or one of its sub-items.
+ */
+void menu_item_hover(MenuComponent *menu, MenuItemComponent *menu_item, SDL_Point *pos) {
+ if (SDL_PointInRect(pos, &menu_item->collision_rect)) {
+ menu_item->is_highlighted = true;
+ menu->highlight_item = menu_item;
+ } else if (menu_item->is_highlighted) { // We can't hover a sub-item if this item is not highlighted because it is not visible
+ bool any_sub_highlighted = false;
+ FOR_EACH_SUBITEM(menu_item)
+ menu_item_hover(menu, item, pos);
+
+ if (item->is_highlighted) {
+ any_sub_highlighted = true;
+ }
+ END_FOR_EACH_ITEM
+
+ // If any sub-item is highlighted, the current item also is
+ menu_item->is_highlighted = any_sub_highlighted;
+ }
+}
+
+bool menu_mouse_motion(MenuComponent *menu, int x, int y) {
+ menu->visible = y <= MENU_VISIBLE_HEIGHT;
+ SDL_Point pos = {x, y};
+
+ menu->highlight_item = NULL;
+
+ FOR_EACH_ITEM(menu)
+ menu_item_hover(menu, item, &pos);
+ END_FOR_EACH_ITEM
+
+ return menu->highlight_item != NULL;
+}
+
+bool menu_mouse_click(MenuComponent *menu) {
+ if (menu->highlight_item == NULL) {
+ return false;
+ }
+
+ log_info("%s", menu->highlight_item->label);
+ return true;
+}
+
+Component *menu_as_component(MenuComponent *menu) {
+ Component *component = malloc(sizeof(Component));
+
+ component->ref = menu;
+ component->render = (void (*)(void *)) &menu_render;
+ component->destroy = (void (*)(void *)) &menu_destroy;
+ component->mouse_motion = (bool (*)(void *, int, int)) &menu_mouse_motion;
+ component->mouse_click = (bool (*)(void *)) &menu_mouse_click;
+
+ return component;
+}
\ No newline at end of file
diff --git a/gui/components/window_menu.h b/gui/components/window_menu.h
new file mode 100644
index 0000000..1b33715
--- /dev/null
+++ b/gui/components/window_menu.h
@@ -0,0 +1,110 @@
+//
+// Created by william on 8/23/24.
+//
+
+#ifndef NES_EMULATOR_WINDOW_MENU_H
+#define NES_EMULATOR_WINDOW_MENU_H
+
+#include "window.h"
+#include "linked_list.h"
+#include "component.h"
+
+#define MENU_HEIGHT 24
+#define MENU_VISIBLE_HEIGHT 64;
+#define MENU_BACKGROUND_COLOR 0xff353535
+#define MENU_HIGHLIGHT_COLOR 0xff4d4d4d
+#define MENU_TEXT_COLOR {0xff, 0xff, 0xff}
+#define MENU_ITEM_MARGIN_X 20
+#define MENU_ITEM_MARGIN_Y 4
+
+typedef void (*on_click_callback)();
+
+typedef struct menu_item_component {
+ char *label;
+ on_click_callback on_click;
+
+ bool is_highlighted;
+
+ LinkedList sub_items;
+ SDL_Texture *label_texture;
+ SDL_Rect draw_rect;
+ SDL_Rect collision_rect;
+} MenuItemComponent;
+
+typedef struct menu_component {
+ int window_width;
+ bool visible;
+
+ LinkedList items;
+ MenuItemComponent *highlight_item;
+
+ SDL_Renderer *renderer;
+ TTF_Font *font;
+ SDL_Texture *background_texture;
+ SDL_Texture *highlight_texture;
+} MenuComponent;
+
+/**
+ * Creates a menu fow a window.
+ * @param window A reference to the window
+ * @param font A reference to the TTF font to use to render text
+ * @return A reference to the menu component
+ */
+MenuComponent *menu_create(Window *window, TTF_Font *font);
+
+/**
+ * Creates a menu item. Can be configured with a callback function which will be called when the menu item is clicked.
+ * Note that the callback function will be overridden if the menu item has sub items.
+ * @param label The label of the menu item
+ * @param on_click The callback function to call when clicked
+ * @return A reference to the menu item
+ */
+MenuItemComponent *menu_item_create(char *label, on_click_callback on_click);
+
+/**
+ * Adds an item to a menu.
+ * @param menu A reference to the menu
+ * @param menu_item A reference to the menu item to add to the menu
+ */
+void menu_append(MenuComponent *menu, MenuItemComponent *menu_item);
+
+/**
+ * Adds an sub-item to a menu item.
+ * Note that this will have the effect of preventing the callback to be called.
+ * @param menu_item A reference to the menu item
+ * @param sub_item A reference to the sub item to add to the menu item
+ */
+void menu_item_append(MenuItemComponent *menu_item, MenuItemComponent *sub_item);
+
+/**
+ * Builds the ressources needed for a menu to be displayed. (ex: textures)
+ * @param menu The menu to build
+ */
+void menu_build(MenuComponent *menu);
+
+/**
+ * Renders a menu to its window.
+ * @param menu A reference to the menu to render
+ */
+void menu_render(MenuComponent *menu);
+
+/**
+ * Destroys a menu, freeing its memory.
+ * @param menu A reference to the menu to destroy
+ */
+void menu_destroy(MenuComponent *menu);
+
+/**
+ * Destroys a menu item to free its memory.
+ * @param menu_item A reference to the menu item to destroy
+ */
+void menu_item_destroy(MenuItemComponent *menu_item);
+
+/**
+ * Returns a menu as a component.
+ * @param menu The menu
+ * @return
+ */
+Component *menu_as_component(MenuComponent *menu);
+
+#endif //NES_EMULATOR_WINDOW_MENU_H
diff --git a/gui/gui.c b/gui/gui.c
index 823c8ee..49b3a9c 100644
--- a/gui/gui.c
+++ b/gui/gui.c
@@ -14,6 +14,12 @@
#include "char_map.h"
#include "dbg_palette.h"
+#if DEBUG
+#define WINDOW_ID_MAIN 3
+#else
+#define WINDOW_ID_MAIN 1
+#endif
+
typedef struct nes_gui {
NesMainWindow main_window;
NesPatternWindow pattern_window;
@@ -43,7 +49,7 @@ bool gui_init() {
pattern_window_init(&gui.pattern_window);
nametable_window_init(&gui.nametable_window);
- char_map_init(gui.main_window.sdl_context.renderer, gui.font);
+ char_map_init(gui.main_window.window.sdl_context.renderer, gui.font);
#endif
main_window_init(&gui.main_window);
@@ -70,7 +76,7 @@ void gui_post_sysinit() {
dbg_pattern_table_init();
dbg_nametable_init();
- // TODO: The texture is rendered before the palette data is in the PPU memory, so the only color is grey
+ // TODO: The background_texture is rendered before the palette data is in the PPU memory, so the only color is grey
pattern_window_build_table(&gui.pattern_window);
nametable_window_update(&gui.nametable_window);
#endif
@@ -103,21 +109,34 @@ int gui_input() {
system_toggle_pause(lctrl);
break;
#if DEBUG
- case SDLK_t:
- ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger;
- break;
- case SDLK_n:
- ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3;
- break;
- default:
- pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym);
- break;
-#else
- default:
+ case SDLK_t:
+ ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger;
break;
+ case SDLK_n:
+ ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3;
+ break;
+ default:
+ pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym);
+ break;
+#else
+ default:
+ break;
#endif
}
}
+
+ if (event.type == SDL_MOUSEMOTION) {
+ int x = event.motion.x;
+ int y = event.motion.y;
+
+ if (event.window.windowID == WINDOW_ID_MAIN) {
+ main_window_mouse_motion(&gui.main_window, x, y);
+ }
+ }
+
+ if (event.type == SDL_MOUSEBUTTONUP && event.window.windowID == WINDOW_ID_MAIN) {
+ main_window_mouse_click(&gui.main_window);
+ }
}
return 1;
@@ -142,8 +161,6 @@ void gui_present() {
pattern_window_present(&gui.pattern_window);
nametable_window_present(&gui.nametable_window);
#endif
-
- main_window_present(&gui.main_window);
}
void gui_delay() {
@@ -159,6 +176,10 @@ void gui_delay() {
gui.last_frame_tick = SDL_GetTicks();
}
+TTF_Font *gui_get_font() {
+ return gui.font;
+}
+
unsigned int gui_get_frame_delay() {
return gui.frame_delay;
}
\ No newline at end of file
diff --git a/gui/gui.h b/gui/gui.h
index 608e4e0..6c0a96b 100644
--- a/gui/gui.h
+++ b/gui/gui.h
@@ -6,6 +6,7 @@
#define NES_EMULATOR_GUI_H
#include
+#include
bool gui_init();
void gui_uninit();
@@ -17,6 +18,7 @@ void gui_render();
void gui_present();
void gui_delay();
+TTF_Font* gui_get_font();
unsigned int gui_get_frame_delay();
#endif //NES_EMULATOR_GUI_H
diff --git a/gui/main_window.c b/gui/main_window.c
index 063d103..2b824c7 100644
--- a/gui/main_window.c
+++ b/gui/main_window.c
@@ -7,19 +7,37 @@
#include "log.h"
#include "char_map.h"
#include "gui.h"
+#include "components/window_menu.h"
void main_window_init(NesMainWindow *window) {
- window->sdl_context = window_init("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
- window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
+ window->window = window_create("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
+ window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
+
+ MenuComponent *menu = menu_create(&window->window, gui_get_font());
+
+ MenuItemComponent *mi_file = menu_item_create("FILE", NULL);
+ menu_append(menu, mi_file);
+ MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL);
+ menu_item_append(mi_file, mi_file_open);
+
+ MenuItemComponent *mi_debug = menu_item_create("DEBUG", NULL);
+ menu_append(menu, mi_debug);
+ MenuItemComponent *mi_debug_nametable = menu_item_create("NAMETABLE", NULL);
+ menu_item_append(mi_debug, mi_debug_nametable);
+ MenuItemComponent *mi_debug_pattern = menu_item_create("PATTERN TABLE", NULL);
+ menu_item_append(mi_debug, mi_debug_pattern);
+
+ menu_build(menu);
+ window_add_component(&window->window, menu_as_component(menu));
}
void main_window_uninit(NesMainWindow *window) {
SDL_DestroyTexture(window->texture);
- window_uninit(window->sdl_context);
+ window_destroy(&window->window);
}
#if DEBUG
+
void main_window_render_delay(SDL_Renderer *renderer) {
Uint32 delay = gui_get_frame_delay();
@@ -32,18 +50,22 @@ void main_window_render_delay(SDL_Renderer *renderer) {
char_map_render(renderer, buffer);
}
+
#endif
void main_window_render(NesMainWindow *window, pixel *pixels) {
- SDL_RenderClear(window->sdl_context.renderer);
SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel));
- SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
-#if DEBUG
- main_window_render_delay(window->sdl_context.renderer);
-#endif
+ window_render_texture(&window->window, window->texture);
+ window_render_components(&window->window);
+
+ window_present(&window->window);
}
-void main_window_present(NesMainWindow *window) {
- SDL_RenderPresent(window->sdl_context.renderer);
+void main_window_mouse_motion(NesMainWindow *window, int x, int y) {
+ window_mouse_motion(&window->window, x, y);
+}
+
+void main_window_mouse_click(NesMainWindow *window) {
+ window_mouse_click(&window->window);
}
\ No newline at end of file
diff --git a/gui/main_window.h b/gui/main_window.h
index 57ce4e3..60a08be 100644
--- a/gui/main_window.h
+++ b/gui/main_window.h
@@ -7,14 +7,14 @@
#include
#include "../include/ppu.h"
-#include "window.h"
+#include "components/window.h"
#define MAIN_WINDOW_WIDTH 256
#define MAIN_WINDOW_HEIGHT 240
#define MAIN_WINDOW_SCALE 3
typedef struct nes_main_window {
- NesSdlContext sdl_context;
+ Window window;
SDL_Texture *texture;
} NesMainWindow;
@@ -23,10 +23,7 @@ void main_window_init(NesMainWindow *window);
void main_window_uninit(NesMainWindow *window);
void main_window_render(NesMainWindow *window, pixel* pixels);
-void main_window_present(NesMainWindow *window);
-
-#if DEBUG
-void main_window_render_delay(SDL_Renderer *renderer);
-#endif
+void main_window_mouse_motion(NesMainWindow *window, int x, int y);
+void main_window_mouse_click(NesMainWindow *window);
#endif //NES_EMULATOR_MAIN_WINDOW_H
diff --git a/gui/nametable_window.c b/gui/nametable_window.c
index 531ddf5..1d3189e 100644
--- a/gui/nametable_window.c
+++ b/gui/nametable_window.c
@@ -10,15 +10,13 @@
#define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE)
void nametable_window_init(NesNametableWindow *window) {
- window->sdl_context = window_init("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
-
- window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STREAMING, NW_WIDTH, NW_HEIGHT);
+ window->window = window_create("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
+ window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
}
void nametable_window_uninit(NesNametableWindow *window) {
SDL_DestroyTexture(window->texture);
- window_uninit(window->sdl_context);
+ window_destroy(&window->window);
}
void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *buffer) {
@@ -44,10 +42,9 @@ void nametable_window_update(NesNametableWindow *window) {
}
void nametable_window_render(NesNametableWindow *window) {
- SDL_RenderClear(window->sdl_context.renderer);
- SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
+ window_render_texture(&window->window, window->texture);
}
void nametable_window_present(NesNametableWindow *window) {
- SDL_RenderPresent(window->sdl_context.renderer);
+ window_present(&window->window);
}
\ No newline at end of file
diff --git a/gui/nametable_window.h b/gui/nametable_window.h
index 3063c89..1233c22 100644
--- a/gui/nametable_window.h
+++ b/gui/nametable_window.h
@@ -5,7 +5,7 @@
#ifndef NES_EMULATOR_NAMETABLE_WINDOW_H
#define NES_EMULATOR_NAMETABLE_WINDOW_H
-#include "window.h"
+#include "components/window.h"
#include "../include/types.h"
#define NW_SCALE 1
@@ -13,16 +13,18 @@
#define NW_ROW_TILE_COUNT 64
typedef struct nes_nametable_window {
- NesSdlContext sdl_context;
+ Window window;
SDL_Texture *texture;
} NesNametableWindow;
void nametable_window_init(NesNametableWindow *window);
+
void nametable_window_uninit(NesNametableWindow *window);
void nametable_window_update(NesNametableWindow *window);
void nametable_window_render(NesNametableWindow *window);
+
void nametable_window_present(NesNametableWindow *window);
#endif //NES_EMULATOR_NAMETABLE_WINDOW_H
diff --git a/gui/pattern_window.c b/gui/pattern_window.c
index e87a137..c1043fc 100644
--- a/gui/pattern_window.c
+++ b/gui/pattern_window.c
@@ -10,16 +10,14 @@
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
void pattern_window_init(NesPatternWindow *window) {
- window->sdl_context = window_init("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
-
- window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT);
+ window->window = window_create("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
+ window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC);
window->palette = 0;
}
void pattern_window_uninit(NesPatternWindow *window) {
SDL_DestroyTexture(window->texture);
- window_uninit(window->sdl_context);
+ window_destroy(&window->window);
}
void pattern_window_build_table(NesPatternWindow *window) {
@@ -44,10 +42,9 @@ void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode) {
}
void pattern_window_render(NesPatternWindow *window) {
- SDL_RenderClear(window->sdl_context.renderer);
- SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
+ window_render_texture(&window->window, window->texture);
}
void pattern_window_present(NesPatternWindow *window) {
- SDL_RenderPresent(window->sdl_context.renderer);
+ window_present(&window->window);
}
\ No newline at end of file
diff --git a/gui/pattern_window.h b/gui/pattern_window.h
index 88f6550..caa8349 100644
--- a/gui/pattern_window.h
+++ b/gui/pattern_window.h
@@ -6,14 +6,14 @@
#define NES_EMULATOR_PATTERN_WINDOW_H
#include "../include/types.h"
-#include "window.h"
+#include "components/window.h"
#define PW_SCALE 2
#define PW_ROW_TILE_COUNT 16
#define PW_PALETTE_MAX 3
typedef struct nes_pattern_window {
- NesSdlContext sdl_context;
+ Window window;
SDL_Texture *texture;
byte palette;
} NesPatternWindow;
diff --git a/gui/window.c b/gui/window.c
deleted file mode 100644
index 4852d85..0000000
--- a/gui/window.c
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// Created by william on 17/05/24.
-//
-
-#include
-#include "window.h"
-#include "log.h"
-
-NesSdlContext window_init(char *title, int width, int height, int scale) {
- NesSdlContext context;
-
- int renderer_flags = SDL_RENDERER_ACCELERATED;
- int window_flags = 0;
-
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
- log_error("Couldn't initialize SDL: %s", SDL_GetError());
- exit(-1);
- }
-
- int actual_width = width * scale;
- int actual_height = height * scale;
- context.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, actual_width,
- actual_height, window_flags);
- if (!context.window) {
- log_error("Failed to open %d x %d SDL window: %s", actual_width, actual_height, SDL_GetError());
- exit(-1);
- }
-
- context.renderer = SDL_CreateRenderer(context.window, -1, renderer_flags);
- if (!context.renderer) {
- log_error("Failed to create renderer: %s", SDL_GetError());
- SDL_DestroyWindow(context.window);
- exit(-1);
- }
-
- return context;
-}
-
-void window_uninit(NesSdlContext context) {
- SDL_DestroyRenderer(context.renderer);
- SDL_DestroyWindow(context.window);
-}
-
-//NesWindow window_init(int width, int height, char *title) {
-// NesWindow win;
-// win.width = width;
-// win.height = height;
-//
-// int renderer_flags = SDL_RENDERER_ACCELERATED;
-// int window_flags = 0;
-//
-// if (SDL_Init(SDL_INIT_VIDEO) < 0) {
-// log_error("Couldn't initialize SDL: %s", SDL_GetError());
-// exit(-1);
-// }
-//
-// win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height,
-// window_flags);
-// if (!win.window) {
-// log_error("Failed to open %d x %d sdl_window: %s", win.width, win.height, SDL_GetError());
-// exit(-1);
-// }
-//
-// win.renderer = SDL_CreateRenderer(win.window, -1, renderer_flags);
-// if (!win.renderer) {
-// log_error("Failed to create renderer: %s\n", SDL_GetError());
-// exit(-1);
-// }
-//
-// return win;
-//}
-//
-//void window_uninit(NesWindow *window) {
-// SDL_DestroyRenderer(window->renderer);
-// SDL_DestroyWindow(window->window);
-//}
-//
-//void window_render(NesWindow *window) {
-// SDL_RenderClear(window->renderer);
-//
-//// for (int y = 0; y < window->canvas.height; y++) {
-//// for (int x = 0; x < window->canvas.width; x++) {
-//// int pixel_index = x + y * window->canvas.width;
-//// Pixel pixel = window->canvas.pixels[pixel_index];
-////
-//// SDL_SetRenderDrawColor(window->renderer, pixel.r, pixel.g, pixel.b, 255);
-////
-//// for (int i = 0; i < window->scaling; i++) {
-//// for (int j = 0; j < window->scaling; j++) {
-//// int scaled_x = x * window->scaling + i;
-//// int scaled_y = y * window->scaling + j;
-//// SDL_RenderDrawPoint(window->renderer, scaled_x, scaled_y);
-//// }
-//// }
-//// }
-//// }
-//}
-//
-//void window_present(NesWindow *window) {
-// SDL_RenderPresent(window->renderer);
-//}
\ No newline at end of file
diff --git a/gui/window.h b/gui/window.h
deleted file mode 100644
index 7392e27..0000000
--- a/gui/window.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// Created by william on 17/05/24.
-//
-
-#ifndef NES_EMULATOR_WINDOW_H
-#define NES_EMULATOR_WINDOW_H
-
-#include
-
-typedef struct nes_sdl_context {
- SDL_Renderer *renderer;
- SDL_Window *window;
-} NesSdlContext;
-
-NesSdlContext window_init(char *title, int width, int height, int scale);
-void window_uninit(NesSdlContext context);
-
-#endif //NES_EMULATOR_WINDOW_H
\ No newline at end of file
diff --git a/main.c b/main.c
index 536f926..125b706 100644
--- a/main.c
+++ b/main.c
@@ -50,7 +50,7 @@ FILE *add_log_file(char *name, int level) {
}
void init_logging() {
- log_set_level(LOG_DEBUG);
+ log_set_level(LOG_INFO);
// Print current working directory
char cwd[256];
@@ -81,7 +81,10 @@ void init_logging() {
void close_logging() {
fclose(log_files.info);
+
+#if DEBUG
fclose(log_files.debug);
+#endif
}
int main() {
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
deleted file mode 100644
index 7806562..0000000
--- a/utils/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-set(HEADERS linked_list.h)
-set(SOURCE linked_list.c)
-
-add_library(nes_utils ${HEADERS} ${SOURCE})
\ No newline at end of file
diff --git a/utils/linked_list.c b/utils/linked_list.c
deleted file mode 100644
index 0955ae6..0000000
--- a/utils/linked_list.c
+++ /dev/null
@@ -1,126 +0,0 @@
-//
-// Created by william on 1/16/24.
-//
-
-#include
-#include
-#include
-#include
-#include "linked_list.h"
-
-LinkedList linked_list_init(bool circular) {
- LinkedList list;
-
- list.circular = circular;
- list.size = 0;
- list.head = NULL;
- list.end = NULL;
- list.current = NULL;
-
- return list;
-}
-
-void linked_list_add(LinkedList *list, void *data) {
- assert(list != NULL);
-
- LinkedListNode *node = malloc(sizeof(LinkedListNode));
- if (node == NULL) {
- perror("Failed to allocate memory for linked list node");
- exit(EXIT_FAILURE);
- }
-
- node->data = data;
- node->previous = list->end;
-
- if (list->head == NULL) {
- list->head = node;
- list->current = node;
- }
-
- if (list->end != NULL) {
- list->end->next = node;
- }
-
- if (list->circular) {
- node->next = list->head;
- } else {
- node->next = NULL;
- }
-
- list->end = node;
- list->size++;
-}
-
-LinkedListNode *linked_list_next(LinkedList *list) {
- assert(list != NULL);
-
- if (list->head == NULL) {
- return NULL;
- }
-
- LinkedListNode *next = list->current->next;
- list->current = next;
- return next;
-}
-
-void linked_list_cursor_reset(LinkedList *list) {
- assert(list != NULL);
-
- list->current = list->head;
-}
-
-LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void *userdata) {
- assert(list != NULL);
- assert(predicate != NULL);
-
- LinkedListNode *node = list->head;
-
- while (node != NULL) {
- if (predicate(node->data, userdata)) {
- return node;
- }
-
- node = node->next;
- }
-
- return NULL;
-}
-
-LinkedListNode *linked_list_get_near(LinkedList *list, int(*compute_distance)(void *, void *), void *userdata) {
- assert(list != NULL);
- assert(compute_distance != NULL);
-
- LinkedListNode *near_node = list->head;
-
- int current_distance = 0x7fffffff;
- while (near_node->next != NULL && current_distance != 0) {
- int next_distance = compute_distance(near_node->next->data, userdata);
- if (next_distance > current_distance) {
- break;
- }
-
- near_node = near_node->next;
- current_distance = next_distance;
- }
-
- // After the loop, we have found the nearest node in the list, assuming there is only one point of convergence
- return near_node;
-}
-
-void linked_list_uninit(LinkedList *list) {
- assert(list != NULL);
-
- LinkedListNode *node = list->head;
- while (node != NULL) {
- LinkedListNode *current_node = node;
- node = node->next;
-
- free(current_node->data);
- free(current_node);
-
- if (node == list->head) {
- // The list may be circular, we don't want an infinite free loop
- break;
- }
- }
-}
\ No newline at end of file
diff --git a/utils/linked_list.h b/utils/linked_list.h
deleted file mode 100644
index 6e293d3..0000000
--- a/utils/linked_list.h
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Created by william on 1/16/24.
-//
-
-#include
-
-#ifndef NESEMULATOR_LINKED_LIST_H
-#define NESEMULATOR_LINKED_LIST_H
-
-typedef struct linked_list_node {
- struct linked_list_node *previous;
- struct linked_list_node *next;
- void *data;
-} LinkedListNode;
-
-typedef struct linked_list {
- bool circular;
- unsigned int size;
- LinkedListNode *head;
- LinkedListNode *end;
- LinkedListNode *current;
-} LinkedList;
-
-/**
- * Initializes a new linked list.
- *
- * @param circular If the list is circular, meaning that the last node is linked to the first node.
- * @return The linked list instance
- */
-LinkedList linked_list_init(bool circular);
-
-/**
- * Adds data to a linked list.
- *
- * @param list The linked list
- * @param data The data to add
- */
-void linked_list_add(LinkedList *list, void *data);
-
-/**
- * Gets the next node in the list.
- *
- * @param list The linked list
- * @return The next node in the list. Can be NULL if the list is empty or depleted.
- */
-LinkedListNode *linked_list_next(LinkedList *list);
-
-/**
- * Resets the position of the cursor to the head of the list.
- */
-void linked_list_cursor_reset(LinkedList *list);
-
-/**
- * Searches for data corresponding to a predicate.
- * The search will stop after reaching the first node matching the given predicate.
- *
- * @param list The list to search in
- * @param predicate The predicate to match the data
- * @param userdata Parameter to pass to the predicate
- * @return The first node in the list matching the predicate
- */
-LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void *userdata);
-
-/**
- * Searches for data with the smallest distance computed from a function.
- * The search will stop when a node increasing the distance is found. For this reason, the distance computing function should have a single minimum.
- *
- * @param list The list to search in
- * @param compute_distance The function to compute the distance of a node's data
- * @param userdata Parameter to pass to the function
- * @return The node with the smallest distance
- */
-LinkedListNode *linked_list_get_near(LinkedList *list, int(*compute_distance)(void *, void *), void *userdata);
-
-/**
- * Deinitializes a linked list.
- *
- * @param list The list to deinitialize
- */
-void linked_list_uninit(LinkedList *list);
-
-#endif //NESEMULATOR_LINKED_LIST_H