Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[develop] Add datel cheat support from filesystem #204

Draft
wants to merge 30 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
db6a7b8
inital support for cheat parsing in frontend
XLuma Jan 12, 2025
bfc28b4
move cheat functions to separate file, move calls to load function
XLuma Jan 12, 2025
cd26c23
hardcode cheats for test, add new files to makefile, add proper error…
XLuma Jan 13, 2025
2b52a92
add extension checck for filetype detection
XLuma Jan 13, 2025
e9cb8d1
fix merge.
XLuma Jan 14, 2025
9ad2ec1
add cheat to develop
XLuma Jan 14, 2025
d308f67
strtol -> strtoul, change longs to uint32_t
XLuma Jan 14, 2025
fabe4b1
remove a lot of comments
XLuma Jan 14, 2025
9123c8e
small credit header
XLuma Jan 14, 2025
1afa136
fix indentation in files
XLuma Jan 14, 2025
c39142d
apply suggestion in menu.c
XLuma Jan 14, 2025
466684c
add EOF's
XLuma Jan 14, 2025
0c61055
convert indentation from tab to 4 spaces in cheat_load.c
XLuma Jan 14, 2025
85ad8d0
move includes to top
XLuma Jan 14, 2025
df26ee0
remove leftover tab in ft_split
XLuma Jan 14, 2025
5a73092
remove unused header files in cart_load.c
XLuma Jan 14, 2025
1bd32a3
Formatting improvements
networkfusion Jan 14, 2025
bde4a79
Merge branch 'develop' into develop-cheat-frontend
networkfusion Jan 14, 2025
fc23d60
Merge branch 'develop' into develop-cheat-frontend
networkfusion Jan 15, 2025
7f6630f
add menu option to rom settings, todo: handling code
XLuma Jan 15, 2025
10c1b0c
forgot semicolon
XLuma Jan 15, 2025
14a6af4
add Cheats option in rom settings screen
XLuma Jan 16, 2025
a1e9cc5
remove call to load_cheat in rom loading
XLuma Jan 16, 2025
d9c0a38
ignore lines starting with $ (pj64 cheats)
XLuma Jan 16, 2025
8181626
Code style fixes
networkfusion Jan 16, 2025
6f25047
Minor codestyle fixes
networkfusion Jan 16, 2025
8165d8c
Example documentation improvements
networkfusion Jan 16, 2025
8634424
Re-order context menu
networkfusion Jan 16, 2025
f293848
Update 13_datel_cheats.md
networkfusion Jan 16, 2025
b8f3bcc
Further document improvements
networkfusion Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ SRCS = \
menu/actions.c \
menu/bookkeeping.c \
menu/cart_load.c \
menu/cheat_load.c \
menu/disk_info.c \
menu/fonts.c \
menu/hdmi.c \
Expand Down
1 change: 1 addition & 0 deletions src/menu/cart_load.c
XLuma marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "path.h"
#include "utils/fs.h"
#include "utils/utils.h"
#include "cheat_load.h"

#ifndef SAVES_SUBDIRECTORY
#define SAVES_SUBDIRECTORY "saves"
Expand Down
198 changes: 198 additions & 0 deletions src/menu/cheat_load.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/**
* @brief Cheat file support
*
* @authors Mena and XLuma
*/

#include "cheat_load.h"
#include "../utils/fs.h"

#include <string.h>
#include <libdragon.h>
#include <stdio.h>
#include <sys/stat.h>
#include "views/views.h"


char *cheat_load_convert_error_message (cheat_load_err_t err) {
switch (err) {
case CHEAT_LOAD_OK: return "Cheats loaded OK";
case CHEAT_LOAD_ERR_NO_CHEAT_FILE: return "No cheat file found";
case CHEAT_LOAD_ERR_SIZE_FAILED: return "Error occured acquiring cheat size";
case CHEAT_LOAD_ERR_CHEAT_EMPTY: return "Cheat file is empty";
case CHEAT_LOAD_ERR_CHEAT_TOO_LARGE: return "Cheat file is too large (over 128KiB)";
case CHEAT_LOAD_ERR_MALLOC_FAILED: return "Error occured allocating memory for file";
case CHEAT_LOAD_ERR_READ_FAILED: return "Error occured during file read";
case CHEAT_LOAD_ERR_CLOSE_FAILED: return "Error occured during file close";
default: return "Unknown error [CHEAT_LOAD]";
}
}

static int find_str (char const *s, char c) {
int i;
int nb_str;

i = 0;
nb_str = 0;
if (!s[0]) {
return (0);
}
while (s[i] && s[i] == c) {
i++;
}
while (s[i]) {
if (s[i] == c) {
nb_str++;
while (s[i] && s[i] == c) {
i++;
}
continue;
}
i++;
}
if (s[i - 1] != c) {
nb_str++;
}
return (nb_str);
}

static void get_next_str (char **next_str, size_t *next_strlen, char c) {
size_t i;

*next_str += *next_strlen;
*next_strlen = 0;
i = 0;
while (**next_str && **next_str == c) {
(*next_str)++;
}
while ((*next_str)[i]) {
if ((*next_str)[i] == c) {
return;
}
(*next_strlen)++;
i++;
}
}

static char **free_tab (char **tab) {
int i;

i = 0;
while (tab[i]) {
free(tab[i]);
i++;
}
free(tab);
return (NULL);
}

char **ft_split (char const *s, char c) {
char **tab;
char *next_str;
size_t next_strlen;
int i;

i = -1;
if (!s) {
return (NULL);
}
tab = malloc(sizeof(char *) * (find_str(s, c) + 1));
if (!tab) {
return (NULL);
}
next_str = (char *)s;
next_strlen = 0;
while (++i < find_str(s, c)) {
get_next_str(&next_str, &next_strlen, c);
tab[i] = (char *)malloc(sizeof(char) * (next_strlen + 1));
if (!tab[i]) {
return (free_tab(tab));
}
strlcpy(tab[i], next_str, next_strlen + 1);
}
tab[i] = NULL;
return (tab);
}

cheat_load_err_t load_cheats (menu_t *menu) {
FILE *cheatsFile;
struct stat st;
size_t cheatsLength;
path_t *path = path_clone(menu->load.rom_path);

// Parse cheats from file
path_ext_replace(path, "cht");
if((cheatsFile = fopen(path_get(path), "rb")) == NULL) {
path_free(path);
return CHEAT_LOAD_OK; // no file is not an error.
}

if (fstat(fileno(cheatsFile), &st)){
path_free(path);
return CHEAT_LOAD_ERR_SIZE_FAILED;
}

cheatsLength = st.st_size;
if (cheatsLength <= 0) {
path_free(path);
return CHEAT_LOAD_ERR_CHEAT_EMPTY;
}
if (cheatsLength > KiB(128)) {
path_free(path);
return CHEAT_LOAD_ERR_CHEAT_TOO_LARGE;
}

char *cheatsContent = NULL;
if((cheatsContent = malloc((cheatsLength + 1) * sizeof(char))) == NULL) {
path_free(path);
return CHEAT_LOAD_ERR_MALLOC_FAILED;
}
if(fread(cheatsContent, cheatsLength, 1, cheatsFile) != 1) {
path_free(path);
return CHEAT_LOAD_ERR_READ_FAILED;
}
Comment on lines +150 to +153
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix memory leak when fread fails

If fread fails, cheatsContent is not freed before returning, leading to a memory leak.

Apply this diff to fix the issue:

 if(fread(cheatsContent, cheatsLength, 1, cheatsFile) != 1) {
     path_free(path);
+    free(cheatsContent);
     return CHEAT_LOAD_ERR_READ_FAILED;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(fread(cheatsContent, cheatsLength, 1, cheatsFile) != 1) {
path_free(path);
return CHEAT_LOAD_ERR_READ_FAILED;
}
if(fread(cheatsContent, cheatsLength, 1, cheatsFile) != 1) {
path_free(path);
free(cheatsContent);
return CHEAT_LOAD_ERR_READ_FAILED;
}
🧰 Tools
🪛 cppcheck (2.10-2)

[error] 152-152: Memory leak

(memleak)


cheatsContent[cheatsLength] = '\0';
if(fclose(cheatsFile) != 0){
path_free(path);
return CHEAT_LOAD_ERR_CLOSE_FAILED;
}
Comment on lines +156 to +159
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix memory leak when fclose fails

If fclose fails, cheatsContent is not freed before returning, leading to a memory leak.

Apply this diff to fix the issue:

 if(fclose(cheatsFile) != 0){
     path_free(path);
+    free(cheatsContent);
     return CHEAT_LOAD_ERR_CLOSE_FAILED;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(fclose(cheatsFile) != 0){
path_free(path);
return CHEAT_LOAD_ERR_CLOSE_FAILED;
}
if(fclose(cheatsFile) != 0){
path_free(path);
free(cheatsContent);
return CHEAT_LOAD_ERR_CLOSE_FAILED;
}
🧰 Tools
🪛 cppcheck (2.10-2)

[error] 158-158: Memory leak

(memleak)

cheatsFile = NULL;

char **tab = ft_split(cheatsContent, '\n');
size_t lines = 1;
for (size_t i = 0; tab[i] != NULL; i++) {
lines++;
}
Comment on lines +164 to +166
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the initialization of lines

Initializing lines to 1 may lead to an incorrect count of cheat entries.

Initialize lines to 0 to accurately count the number of lines:

-size_t lines = 1;
+size_t lines = 0;
 for (size_t i = 0; tab[i] != NULL; i++) {
     lines++;
 }

Committable suggestion skipped: line range outside the PR's diff.


free(cheatsContent);

uint32_t *cheats = (uint32_t*)malloc(((lines * sizeof(uint32_t)) * 2) + 2);
memset(cheats, 0, ((lines * sizeof(uint32_t)) * 2) + 2);
Comment on lines +170 to +171
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Adjust the allocation size for cheats array

The calculation for the size of the cheats array may lead to insufficient memory allocation.

Update the allocation size to accommodate all cheat entries and the terminating zeros:

-uint32_t  *cheats = (uint32_t*)malloc(((lines * sizeof(uint32_t)) * 2) + 2);
-memset(cheats, 0, ((lines * sizeof(uint32_t)) * 2) + 2);
+uint32_t *cheats = (uint32_t *)malloc(((lines + 1) * 2 * sizeof(uint32_t)));
+memset(cheats, 0, ((lines + 1) * 2 * sizeof(uint32_t)));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uint32_t *cheats = (uint32_t*)malloc(((lines * sizeof(uint32_t)) * 2) + 2);
memset(cheats, 0, ((lines * sizeof(uint32_t)) * 2) + 2);
uint32_t *cheats = (uint32_t *)malloc(((lines + 1) * 2 * sizeof(uint32_t)));
memset(cheats, 0, ((lines + 1) * 2 * sizeof(uint32_t)));

size_t cheatIndex = 0;
for(size_t i = 0; tab[i] != NULL; i++) {
// ignore titles
if (tab[i][0] == '#' || tab[i][0] == '$') {
continue;
}
// ignore empty, too small or too big lines
if (strlen(tab[i]) < 12 || strlen(tab[i]) > 15) {
continue;
}
char **splitCheat = ft_split(tab[i], ' ');
// thank you mena for checking my fucky wucky
networkfusion marked this conversation as resolved.
Show resolved Hide resolved
uint32_t cheatValue1 = strtoul(splitCheat[0], NULL, 16);
uint32_t cheatValue2 = strtoul(splitCheat[1], NULL, 16);
cheats[cheatIndex] = cheatValue1;
cheats[cheatIndex + 1] = cheatValue2;
free_tab(splitCheat);
cheatIndex += 2;
}
free_tab(tab);

cheats[cheatIndex] = 0;
cheats[cheatIndex + 1] = 0;
menu->boot_params->cheat_list = cheats;

return CHEAT_LOAD_OK;
}
26 changes: 26 additions & 0 deletions src/menu/cheat_load.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/***
* @file cheat_load.h
* @brief Cheat loading functions
*/

#include "path.h"
#include "utils/fs.h"
#include "utils/utils.h"
#include "menu_state.h"

/** @brief Cheat code loading enum */

typedef enum {
CHEAT_LOAD_OK,
CHEAT_LOAD_ERR_NO_CHEAT_FILE,
CHEAT_LOAD_ERR_SIZE_FAILED,
CHEAT_LOAD_ERR_CHEAT_EMPTY,
CHEAT_LOAD_ERR_CHEAT_TOO_LARGE,
CHEAT_LOAD_ERR_MALLOC_FAILED,
CHEAT_LOAD_ERR_READ_FAILED,
CHEAT_LOAD_ERR_CLOSE_FAILED,
CHEAT_LOAD_ERR_UNKNOWN_ERROR
} cheat_load_err_t;

cheat_load_err_t load_cheats(menu_t *menu);
networkfusion marked this conversation as resolved.
Show resolved Hide resolved
char *cheat_load_convert_error_message (cheat_load_err_t err);
7 changes: 7 additions & 0 deletions src/menu/rom_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,8 @@ static rom_err_t save_override (path_t *path, const char *id, int value, int def

if (value == default_value) {
mini_err = mini_delete_value(rom_info_ini, "custom_boot", id);
} else if (strncmp(id, "cheat_codes", strlen("cheat_codes"))) {
mini_err = mini_set_bool(rom_info_ini, NULL, id, value);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something funky going on here... need to check

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current thinking is that this block

    if (value == default_value) {
        mini_err = mini_delete_value(rom_info_ini, "custom_boot", id);
    } else if (strncmp(id, "cheat_codes", strlen("cheat_codes"))) {
        mini_err = mini_set_bool(rom_info_ini, NULL, id, value);
    } else {
        mini_err = mini_set_int(rom_info_ini, "custom_boot", id, value);
    }

is in the wrong place and should be seperate from the ROM info file.

Though I have not looked into where it should actually be.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So i wrote this before I had this next idea:
Ideally, the .ini file stores an array or something like that) of cheat titles/indexes (whatever ends up working best), so that rather than having an on-off cheat, the menu will parse previously enabled cheats.
This approach would ensure errors would ahve been shown to the user at some point, and thus only working/enabled cheats are included

Comment on lines +860 to +861
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the string comparison logic.

The strncmp condition is incorrect. It returns 0 for equal strings, so the current logic is inverted.

Apply this diff to fix the logic:

-    } else if (strncmp(id, "cheat_codes", strlen("cheat_codes"))) {
+    } else if (strncmp(id, "cheat_codes", strlen("cheat_codes")) == 0) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (strncmp(id, "cheat_codes", strlen("cheat_codes"))) {
mini_err = mini_set_bool(rom_info_ini, NULL, id, value);
} else if (strncmp(id, "cheat_codes", strlen("cheat_codes")) == 0) {
mini_err = mini_set_bool(rom_info_ini, NULL, id, value);

} else {
mini_err = mini_set_int(rom_info_ini, "custom_boot", id, value);
}
Expand Down Expand Up @@ -962,6 +964,11 @@ rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_
return save_override(path, "tv_type", rom_info->boot_override.tv_type, ROM_TV_TYPE_AUTOMATIC);
}

rom_err_t rom_setting_set_cheats (path_t *path, rom_info_t *rom_info, bool enabled) {
rom_info->settings.cheats_enabled = enabled;
return save_override(path, "cheat_codes", enabled, false);
}

rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) {
FILE *f;
rom_header_t rom_header;
Expand Down
2 changes: 2 additions & 0 deletions src/menu/rom_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,6 @@ rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_s
rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info);
rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type);

rom_err_t rom_setting_set_cheats (path_t *path, rom_info_t *rom_info, bool enabled);

#endif
4 changes: 4 additions & 0 deletions src/menu/views/file_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ static const char *image_extensions[] = { "png", "jpg", "gif", NULL };
static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL };
static const char *controller_pak_extensions[] = { "mpk", "pak", NULL };
static const char *emulator_extensions[] = { "nes", "smc", "gb", "gbc", "sms", "gg", "chf", NULL };
static const char *cheat_extensions[] = {"cht", NULL};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we happy with this extension name?!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows users to kinda drag-and-drop files from other sources(PJ64 for ex) with only some reformatting. Totally open to other extensions though



static struct stat st;
Expand Down Expand Up @@ -44,6 +45,9 @@ static char *format_file_type (char *name, bool is_directory) {
} else if (file_has_extensions(name, emulator_extensions)) {
return " Type: Emulator ROM file\n";
}
else if (file_has_extensions(name, cheat_extensions)) {
return " Type: Cheats\n";
}
return " Type: Unknown file\n";
}

Expand Down
26 changes: 25 additions & 1 deletion src/menu/views/load_rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string.h>
#include "utils/fs.h"
#include "../bookkeeping.h"
#include "../cheat_load.h"

static bool show_extra_info_message = false;
static component_boxart_t *boxart;
Expand Down Expand Up @@ -166,6 +167,23 @@ static void add_favorite (menu_t *menu, void *arg) {
bookkeeping_favorite_add(&menu->bookkeeping, menu->load.rom_path, NULL, BOOKKEEPING_TYPE_ROM);
}

static void set_cheat_option(menu_t *menu, void *arg) {
bool enabled = (bool)arg;
if (enabled == true) {
cheat_load_err_t err = load_cheats(menu);
if (err != CHEAT_LOAD_OK) {
menu_show_error(menu, cheat_load_convert_error_message(err));
}
}
if (enabled == false) {
if (menu->boot_params->cheat_list != NULL) {
free(menu->boot_params->cheat_list);
}
}
rom_setting_set_cheats(menu->load.rom_path, &menu->load.rom_info, enabled);
menu->browser.reload = true;
}

static component_context_menu_t set_cic_type_context_menu = { .list = {
{.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) },
{.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) },
Expand Down Expand Up @@ -204,11 +222,18 @@ static component_context_menu_t set_tv_type_context_menu = { .list = {
COMPONENT_CONTEXT_MENU_LIST_END,
}};

static component_context_menu_t set_cheat_options_menu = { .list = {
{ .text = "Enable", .action = set_cheat_option, .arg = (void *) (true)},
{ .text = "Disable", .action = set_cheat_option, .arg = (void *) (false)},
COMPONENT_CONTEXT_MENU_LIST_END,
}};

static component_context_menu_t options_context_menu = { .list = {
{ .text = "Set CIC Type", .submenu = &set_cic_type_context_menu },
{ .text = "Set Save Type", .submenu = &set_save_type_context_menu },
{ .text = "Set TV Type", .submenu = &set_tv_type_context_menu },
{ .text = "Set ROM to autoload", .action = set_autoload_type },
{ .text = "Cheats", .submenu = &set_cheat_options_menu },
{ .text = "Add to favorites", .action = add_favorite },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
Expand Down Expand Up @@ -367,7 +392,6 @@ static void load (menu_t *menu) {
case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break;
default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break;
}
menu->boot_params->cheat_list = NULL;
}

static void deinit (void) {
Expand Down
Loading