mirror of
https://github.com/nitraiolo/CfgUSBLoader.git
synced 2024-11-27 13:44:17 +01:00
634 lines
13 KiB
C
634 lines
13 KiB
C
|
|
// by oggzee
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <ogcsys.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <wctype.h>
|
|
#include <wchar.h>
|
|
#include <locale.h>
|
|
#include <asndlib.h>
|
|
|
|
#include "disc.h"
|
|
#include "fat.h"
|
|
#include "cache.h"
|
|
#include "gui.h"
|
|
#include "menu.h"
|
|
#include "restart.h"
|
|
#include "sys.h"
|
|
#include "util.h"
|
|
#include "utils.h"
|
|
#include "video.h"
|
|
#include "wbfs.h"
|
|
#include "wpad.h"
|
|
#include "patchcode.h"
|
|
#include "cfg.h"
|
|
#include "http.h"
|
|
#include "dns.h"
|
|
#include "wdvd.h"
|
|
#include "music.h"
|
|
#include "subsystem.h"
|
|
#include "net.h"
|
|
#include "menu.h"
|
|
#include "gettext.h"
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Cheats
|
|
//
|
|
////////////////////////////////////////
|
|
|
|
bool get_line_buf(char **buf, char *line, int linesize)
|
|
{
|
|
char *p, *nl;
|
|
int len;
|
|
|
|
*line = 0;
|
|
|
|
if (buf == NULL) return false;
|
|
if (*buf == NULL) return false;
|
|
if (**buf == 0) return false;
|
|
p = *buf;
|
|
nl = strchr(p, '\n'); // find LF
|
|
if (nl == NULL) {
|
|
len = strlen(p);
|
|
*buf = NULL;
|
|
} else {
|
|
len = nl - p;
|
|
*buf = nl + 1;
|
|
if (**buf == 0) *buf = NULL;
|
|
}
|
|
if (len >= linesize) len = linesize - 1;
|
|
strcopy(line, p, len+1);
|
|
// remove any CR in case it's a msdos CRLF format
|
|
len = strlen(line);
|
|
if (len > 0 && line[len-1] == '\r') line[len-1] = 0;
|
|
return true;
|
|
}
|
|
|
|
#define CHEAT_MAX_TITLE 40
|
|
#define CHEAT_MAX_NOTES 3
|
|
#define CHEAT_MAX_LINES 3000
|
|
#define CODE_LINE_LEN 18
|
|
#define CHEAT_MAX 512
|
|
|
|
struct Cheat
|
|
{
|
|
char title[CHEAT_MAX_TITLE];
|
|
int num_codes;
|
|
int line_idx; // index to code line
|
|
int num_notes;
|
|
char note[CHEAT_MAX_NOTES][CHEAT_MAX_TITLE];
|
|
bool enabled;
|
|
bool editable;
|
|
};
|
|
|
|
struct Cheats
|
|
{
|
|
char id[8];
|
|
char title[CHEAT_MAX_TITLE];
|
|
int num_lines;
|
|
char line[CHEAT_MAX_LINES][CODE_LINE_LEN];
|
|
int num_cheats;
|
|
struct Cheat cheat[CHEAT_MAX];
|
|
};
|
|
|
|
struct Cheats cheats;
|
|
|
|
int line_buf_count;
|
|
char *line_buf;
|
|
char line[200];
|
|
|
|
bool get_line()
|
|
{
|
|
bool ret;
|
|
line_buf_count++;
|
|
ret = get_line_buf(&line_buf, D_S(line));
|
|
//printf("%d %s\n", line_buf_count, line);
|
|
return ret;
|
|
}
|
|
|
|
// sample:
|
|
//P2 No Laps (Finish right away)
|
|
//48000000 809BD730 [memorris]
|
|
//DE000000 80008180
|
|
|
|
bool is_code(char *line)
|
|
{
|
|
int i;
|
|
if (strlen(line) < 17) return false;
|
|
if (strlen(line) > 17 && line[17] != ' ') return false;
|
|
if (line[8] != ' ') return false;
|
|
// check if address is valid
|
|
for (i=0; i<8; i++) {
|
|
if (!ISXDIGIT(line[i])) return false;
|
|
}
|
|
// check if value is hex
|
|
for (i=9; i<17; i++) {
|
|
if (!ISALNUM(line[i])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// sample:
|
|
//14000000 000000XX
|
|
|
|
bool is_editable(char *line)
|
|
{
|
|
int i;
|
|
if (!is_code(line)) return false;
|
|
for (i=9; i<17; i++) {
|
|
// is hex digit?
|
|
if (!ISXDIGIT(line[i])) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool parse_cheats(char *buf)
|
|
{
|
|
struct Cheat *cur_cheat;
|
|
|
|
memset(&cheats, 0, sizeof(cheats));
|
|
|
|
line_buf_count = 0;
|
|
line_buf = buf;
|
|
|
|
// first line
|
|
if (!get_line()) goto err;
|
|
|
|
// remove utf8 bom
|
|
if (memcmp(line, "\xEF\xBB\xBF", 3) == 0) {
|
|
memmove(line, line+3, strlen(line+3)+1);
|
|
}
|
|
|
|
// game id
|
|
if (strlen(line) < 4 || strlen(line) > 6) goto err;
|
|
STRCOPY(cheats.id, line);
|
|
|
|
// game title
|
|
if (!get_line()) goto err;
|
|
if (strlen(line) == 0) goto err;
|
|
STRCOPY(cheats.title, line);
|
|
|
|
// empty line
|
|
if (!get_line()) goto err;
|
|
if (strlen(line) > 0) goto err;
|
|
|
|
// loop cheats
|
|
for (;;)
|
|
{
|
|
// cheat title
|
|
if (!get_line()) break;
|
|
if (strlen(line) == 0) return false;
|
|
if (cheats.num_cheats >= CHEAT_MAX) {
|
|
printf(gt("Too many cheats! (%d)"), cheats.num_cheats);
|
|
printf("\n");
|
|
goto err2;
|
|
}
|
|
cheats.num_cheats++;
|
|
cur_cheat = &cheats.cheat[cheats.num_cheats-1];
|
|
STRCOPY(cur_cheat->title, line);
|
|
// loop codes
|
|
for (;;)
|
|
{
|
|
if (!get_line()) break;
|
|
if (strlen(line) == 0) break; // empty line = end
|
|
if (is_code(line))
|
|
{
|
|
// it's a code.
|
|
if (cheats.num_lines >= CHEAT_MAX_LINES) {
|
|
printf(gt("Too many code lines!"));
|
|
printf("\n");
|
|
goto err2;
|
|
}
|
|
cheats.num_lines++;
|
|
STRCOPY(cheats.line[cheats.num_lines-1], line);
|
|
if (cur_cheat->num_codes == 0) {
|
|
cur_cheat->line_idx = cheats.num_lines - 1;
|
|
}
|
|
cur_cheat->num_codes++;
|
|
if (is_editable(line)) {
|
|
cur_cheat->editable = true;
|
|
}
|
|
} else {
|
|
// it's a note.
|
|
for (;;) {
|
|
cur_cheat->num_notes++;
|
|
if (cur_cheat->num_notes <= CHEAT_MAX_NOTES) {
|
|
STRCOPY(cur_cheat->note[cur_cheat->num_notes-1], line);
|
|
} else {
|
|
// ignore the rest of notes
|
|
}
|
|
if (!get_line()) break;
|
|
if (strlen(line) == 0) break; // empty line = end
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
return true;
|
|
|
|
err:
|
|
printf(gt("Unknown syntax!"));
|
|
printf("\n");
|
|
printf("%d: '%s'\n", line_buf_count, line);
|
|
err2:
|
|
printf(gt("Press any button..."));
|
|
printf("\n");
|
|
Wpad_WaitButtons();
|
|
// reset cheats on error.
|
|
memset(&cheats, 0, sizeof(cheats));
|
|
return false;
|
|
}
|
|
|
|
int Load_Cheats_TXT(char *id)
|
|
{
|
|
char filepath[200];
|
|
char *buf = NULL;
|
|
int size = 0;
|
|
bool ret;
|
|
|
|
// reset all to 0
|
|
memset(&cheats, 0, sizeof(cheats));
|
|
|
|
snprintf(D_S(filepath), "%s/codes/%.6s.txt", USBLOADER_PATH, id);
|
|
size = Fat_ReadFile(filepath, (void*)&buf);
|
|
if (size <= 0 || buf == NULL) return 2; // no file
|
|
dbg_printf("\n%s : %d\n", filepath, size);
|
|
|
|
// null terminate
|
|
char *newbuf;
|
|
newbuf = realloc(buf, size+4);
|
|
if (newbuf) {
|
|
newbuf[size] = 0;
|
|
buf = (void*)newbuf;
|
|
} else {
|
|
SAFE_FREE(buf);
|
|
return 1;
|
|
}
|
|
// parse
|
|
ret = parse_cheats(buf);
|
|
SAFE_FREE(buf);
|
|
if (!ret) return 1; // parse error
|
|
if (strncmp(id, cheats.id, strlen(cheats.id)) != 0) return 1;
|
|
|
|
return 0; // ok
|
|
}
|
|
|
|
int Save_Cheats_GCT(char *id)
|
|
{
|
|
char filepath[200];
|
|
char gct_head[] = { 0x00, 0xD0, 0xC0, 0xDE, 0x00, 0xD0, 0xC0, 0xDE };
|
|
char gct_tail[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
int i, j, k, count;
|
|
struct Cheat *cheat = NULL;
|
|
FILE *f;
|
|
|
|
count = 0;
|
|
for (i=0; i<cheats.num_cheats; i++) {
|
|
if (cheats.cheat[i].enabled) count++;
|
|
}
|
|
if (count == 0) return -1;
|
|
|
|
snprintf(D_S(filepath), "%s/codes/%.6s.gct", USBLOADER_PATH, id);
|
|
printf("\n");
|
|
printf_(gt("Saving: %s"), filepath);
|
|
printf("\n");
|
|
|
|
f = fopen(filepath, "wb");
|
|
if (!f) {
|
|
printf_(gt("Error opening: %s"), filepath);
|
|
printf("\n");
|
|
sleep(2);
|
|
return -1;
|
|
}
|
|
// gct header
|
|
fwrite(gct_head, 1, sizeof(gct_head), f);
|
|
// codes
|
|
for (i=0; i<cheats.num_cheats; i++) {
|
|
cheat = &cheats.cheat[i];
|
|
if (cheat->enabled) {
|
|
for (j=0; j<cheat->num_codes; j++) {
|
|
char *cp;
|
|
unsigned int ci = 0;
|
|
unsigned char c = 0;
|
|
cp = cheats.line[cheat->line_idx + j];
|
|
//printf("[%d %d] %s\n ", i, j, cp);
|
|
for (k=0; k<8; k++) {
|
|
if (k == 4) cp++; // skip space
|
|
sscanf(cp, "%2x", &ci);
|
|
c = ci;
|
|
//printf("%02x ", c);
|
|
fwrite(&c, 1, 1, f);
|
|
cp += 2;
|
|
}
|
|
//printf("\n");
|
|
}
|
|
}
|
|
}
|
|
// gct tail
|
|
fwrite(gct_tail, 1, sizeof(gct_tail), f);
|
|
fclose(f);
|
|
printf_(gt("Done."));
|
|
printf("\n");
|
|
sleep(2);
|
|
//Wpad_WaitButtons();
|
|
|
|
return 0; // ok
|
|
}
|
|
|
|
bool Download_Cheats_TXT(char *id)
|
|
{
|
|
char filepath[200];
|
|
//char *url_base = "http://www.usbgecko.com/codes/codes";
|
|
char *url_base = "http://www.geckocodes.org/codes";
|
|
char url[200];
|
|
struct block file;
|
|
FILE *f;
|
|
|
|
file.data = NULL;
|
|
|
|
DefaultColor();
|
|
printf("\n");
|
|
printf_(gt("Downloading cheats..."));
|
|
printf("\n");
|
|
|
|
extern bool Init_Net();
|
|
if (!Init_Net()) goto err;
|
|
|
|
// 6 letters ID
|
|
//snprintf(D_S(url), "%s/%c/%.6s.txt", url_base, id[0], id);
|
|
snprintf(D_S(url), "%s/R/%.6s.txt", url_base, id);
|
|
dbg_printf("url: %s\n", url);
|
|
file = downloadfile_noreferer(url);
|
|
if (file.data == NULL || file.size == 0) {
|
|
|
|
snprintf(D_S(url), "%s/G/%.6s.txt", url_base, id);
|
|
dbg_printf("url: %s\n", url);
|
|
file = downloadfile_noreferer(url);
|
|
if (file.data == NULL || file.size == 0) {
|
|
// 4 letters ID
|
|
snprintf(D_S(url), "%s/%c/%.4s.txt", url_base, id[0], id);
|
|
dbg_printf("url: %s\n", url);
|
|
file = downloadfile_noreferer(url);
|
|
if (file.data == NULL || file.size == 0) {
|
|
printf_(gt("Error downloading."));
|
|
printf("\n");
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf_(gt("Saving cheats..."));
|
|
printf("\n");
|
|
snprintf(D_S(filepath), "%s/codes", USBLOADER_PATH);
|
|
mkpath(filepath, 0777);
|
|
snprintf(D_S(filepath), "%s/codes/%.6s.txt", USBLOADER_PATH, id);
|
|
printf_("%s\n", filepath);
|
|
f = fopen(filepath, "wb");
|
|
if (!f) {
|
|
printf("\n");
|
|
printf_(gt("Error opening: %s"), filepath);
|
|
printf("\n");
|
|
goto err;
|
|
}
|
|
fwrite(file.data, 1, file.size, f);
|
|
fclose(f);
|
|
printf_(gt("OK"));
|
|
printf("\n");
|
|
SAFE_FREE(file.data);
|
|
sleep(2);
|
|
|
|
return true;
|
|
|
|
err:
|
|
printf("\n");
|
|
printf(gt("Press any button..."));
|
|
printf("\n");
|
|
Wpad_WaitButtons();
|
|
SAFE_FREE(file.data);
|
|
return false;
|
|
}
|
|
|
|
void print_game_info(struct discHdr *header, int cols)
|
|
{
|
|
int len;
|
|
len = cols - strlen(CFG.menu_plus_s) - 10;
|
|
printf_("");
|
|
printf("(%.6s) ", header->id);
|
|
printf("%.*s\n", len, get_title(header));
|
|
}
|
|
|
|
|
|
/*
|
|
Ocarina Cheat Manager
|
|
RHAP Wii Play
|
|
Cheats: 20 found / no file / parse error
|
|
|
|
<download txt>
|
|
<save gct>
|
|
<select all>
|
|
+
|
|
[ ] cheat 1
|
|
[x] cheat 2
|
|
[x] cheat 3
|
|
[x] cheat 4
|
|
+
|
|
Notes...
|
|
Notes...
|
|
Notes...
|
|
(more notes) / Press button B to go back
|
|
*/
|
|
|
|
void Menu_Cheats(struct discHdr *header)
|
|
{
|
|
struct Menu menu;
|
|
char active[CHEAT_MAX + 5];
|
|
int cheat_state = 3;
|
|
// 3: loading
|
|
// 2: no file
|
|
// 1: parse err
|
|
// 0: ok
|
|
int cols, rows;
|
|
int cols_opt;
|
|
int cols_note;
|
|
int rows_note;
|
|
int window_size;
|
|
int current_cheat;
|
|
struct Cheat *cheat = NULL;
|
|
int i;
|
|
|
|
menu_init(&menu, 0);
|
|
memset(&cheats, 0, sizeof(cheats));
|
|
|
|
CON_GetMetrics(&cols, &rows);
|
|
rows_note = rows > 15 ? 3 : 0;
|
|
cols_note = cols - strlen(CFG.menu_plus_s) -1;
|
|
cols_opt = cols - strlen(CFG.cursor) - 2 -1;
|
|
window_size = rows - 8 - rows_note - 2 - 1;
|
|
|
|
for (;;) {
|
|
menu.num_opt = 3 + cheats.num_cheats;
|
|
// update active state
|
|
menu_init_active(&menu, active, sizeof(active));
|
|
for (i=0; i<cheats.num_cheats; i++) {
|
|
cheat = &cheats.cheat[i];
|
|
if (cheat->num_codes == 0) {
|
|
active[i+3] = 0;
|
|
} else if (cheat->editable) {
|
|
active[i+3] = 0;
|
|
} else {
|
|
active[i+3] = 1;
|
|
}
|
|
}
|
|
|
|
Con_Clear();
|
|
FgColor(CFG.color_header);
|
|
printf_x(gt("Ocarina Cheat Manager"));
|
|
printf("\n");
|
|
DefaultColor();
|
|
print_game_info(header, cols);
|
|
printf_x(gt("Cheats: "));
|
|
switch (cheat_state) {
|
|
case 3:
|
|
printf(gt("Loading ..."));
|
|
printf("\n");
|
|
__console_flush(0);
|
|
cheat_state = Load_Cheats_TXT((char*)header->id);
|
|
usleep(100000);
|
|
continue;
|
|
case 2:
|
|
printf(gt("no file"));
|
|
printf("\n");
|
|
break;
|
|
case 1:
|
|
printf(gt("parse error"));
|
|
printf("\n");
|
|
break;
|
|
case 0:
|
|
printf(gt("%d available"), cheats.num_cheats);
|
|
printf("\n");
|
|
break;
|
|
}
|
|
printf("\n");
|
|
|
|
menu.line_count = 0;
|
|
// allow moving on all lines, so no active check & jump
|
|
//menu_jump_active(&menu);
|
|
|
|
MENU_MARK();
|
|
printf("<%s>\n", gt("Download .txt"));
|
|
MENU_MARK();
|
|
printf("<%s>\n", gt("Save .gct"));
|
|
MENU_MARK();
|
|
printf("<%s>\n", gt("Select all"));
|
|
|
|
// cheats
|
|
DefaultColor();
|
|
menu_window_begin(&menu, window_size, cheats.num_cheats);
|
|
for (i=0; i<cheats.num_cheats; i++) {
|
|
if (!menu_window_mark(&menu)) continue;
|
|
cheat = &cheats.cheat[i];
|
|
if (cheat->num_codes == 0) {
|
|
printf(" ");
|
|
} else if (cheat->editable) {
|
|
printf("(E) ");
|
|
} else if (cheat->enabled) {
|
|
printf("[x] ");
|
|
} else {
|
|
printf("[ ] ");
|
|
}
|
|
printf("%.*s\n", cols_opt-4, cheat->title);
|
|
}
|
|
DefaultColor();
|
|
menu_window_end(&menu, cols);
|
|
// notes
|
|
FgColor(CFG.color_inactive);
|
|
current_cheat = menu.current - 3;
|
|
int printed_notes = 0;
|
|
if (current_cheat < 0) {
|
|
cheat = NULL;
|
|
} else {
|
|
cheat = &cheats.cheat[current_cheat];
|
|
int n = MIN(cheat->num_notes, CHEAT_MAX_NOTES);
|
|
n = MIN(n, rows_note);
|
|
for (i=0; i<n; i++) {
|
|
printf_("");
|
|
printf("%.*s\n", cols_note, cheat->note[i]);
|
|
}
|
|
printed_notes = n;
|
|
if (cheat->num_notes > n) {
|
|
printf_("");
|
|
printf("(");
|
|
printf(gt("%d more notes"), cheat->num_notes - n);
|
|
printf(")\n");
|
|
printed_notes++;
|
|
}
|
|
}
|
|
// help
|
|
for (i=0; i < 3 - printed_notes; i++) {
|
|
printf("\n");
|
|
}
|
|
if (printed_notes <= 3) {
|
|
printf_h(gt("Press %s to return"), (button_names[CFG.button_cancel.num]));
|
|
printf("\n");
|
|
}
|
|
__console_flush(0);
|
|
|
|
// move
|
|
u32 buttons = Wpad_WaitButtonsRpt();
|
|
menu_move(&menu, buttons);
|
|
|
|
// change
|
|
int change = 0;
|
|
if (buttons & CFG.button_confirm.mask) change = 1;
|
|
if (buttons & WPAD_BUTTON_LEFT) change = -1;
|
|
if (buttons & WPAD_BUTTON_RIGHT) change = 1;
|
|
|
|
if (change)
|
|
{
|
|
switch (menu.current) {
|
|
case 0:
|
|
Download_Cheats_TXT((char*)header->id);
|
|
cheat_state = 3;
|
|
continue;
|
|
case 1:
|
|
Save_Cheats_GCT((char*)header->id);
|
|
break;
|
|
case 2:
|
|
for (i=0; i<cheats.num_cheats; i++) {
|
|
cheat = &cheats.cheat[i];
|
|
if (cheat->num_codes > 0 && !cheat->editable) {
|
|
cheat->enabled = change > 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (menu.current >= 3 && cheat) {
|
|
if (cheat->num_codes > 0 && !cheat->editable) {
|
|
if (buttons & CFG.button_confirm.mask) {
|
|
cheat->enabled = !cheat->enabled;
|
|
} else if (change < 0) {
|
|
cheat->enabled = false;
|
|
} else {
|
|
cheat->enabled = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buttons & CFG.button_exit.mask) {
|
|
Handle_Home(0);
|
|
}
|
|
if (buttons & CFG.button_cancel.mask) break;
|
|
}
|
|
printf("\n");
|
|
}
|