mirror of
https://github.com/ApacheThunder/SCKILL.git
synced 2025-06-19 03:25:32 -04:00
446 lines
11 KiB
C
446 lines
11 KiB
C
#include <gba.h>
|
|
#include <fat.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <dirent.h>
|
|
|
|
#include "Save.h"
|
|
#include "WhiteScreenPatch.h"
|
|
|
|
char *stpcpy(char*, char*);
|
|
|
|
void tryAgain() {
|
|
iprintf("Critical failure.\nPress A to restart.");
|
|
for (;;) {
|
|
scanKeys();
|
|
if (keysDown() & KEY_A)
|
|
for (;;) {
|
|
scanKeys();
|
|
if (keysUp() & KEY_A)
|
|
((void(*)()) 0x02000000)();
|
|
}
|
|
VBlankIntrWait();
|
|
}
|
|
}
|
|
|
|
__attribute__((packed)) struct settings {
|
|
u8 autosave;
|
|
};
|
|
struct settings settings = {
|
|
.autosave = 1
|
|
};
|
|
FILE *settings_file;
|
|
|
|
union paging_index {
|
|
s32 abs;
|
|
struct {
|
|
u32 row : 4;
|
|
s32 page : 28;
|
|
};
|
|
};
|
|
|
|
#define GBA_ROM ((vu32*) 0x08000000)
|
|
#define GBA_BUS ((vu16*) 0x08000000)
|
|
#define GBA_SRAM ((vu8*) 0x0e000000)
|
|
|
|
#define SC_FLASH_MAGIC_ADDR_1 (*(vu16*) 0x08000b92)
|
|
#define SC_FLASH_MAGIC_ADDR_2 (*(vu16*) 0x0800046c)
|
|
#define SC_FLASH_MAGIC_1 ((u16) 0xaa)
|
|
#define SC_FLASH_MAGIC_2 ((u16) 0x55)
|
|
#define SC_FLASH_ERASE ((u16) 0x80)
|
|
#define SC_FLASH_ERASE_BLOCK ((u16) 0x30)
|
|
#define SC_FLASH_ERASE_CHIP ((u16) 0x10)
|
|
#define SC_FLASH_PROGRAM ((u16) 0xA0)
|
|
#define SC_FLASH_IDLE ((u16) 0xF0)
|
|
#define SC_FLASH_IDENTIFY ((u16) 0x90)
|
|
|
|
enum
|
|
{
|
|
SC_RAM_RO = 0x1,
|
|
SC_MEDIA = 0x3,
|
|
SC_FLASH_RW,
|
|
SC_RAM_RW = 0x5,
|
|
};
|
|
|
|
void sc_mode(u32 mode)
|
|
{
|
|
u32 ime = REG_IME;
|
|
REG_IME = 0;
|
|
*(vu16*)0x9FFFFFE = 0xA55A;
|
|
*(vu16*)0x9FFFFFE = 0xA55A;
|
|
*(vu16*)0x9FFFFFE = mode;
|
|
*(vu16*)0x9FFFFFE = mode;
|
|
REG_IME = ime;
|
|
}
|
|
|
|
EWRAM_DATA u8 filebuf[0x4000];
|
|
|
|
u32 pressed;
|
|
bool savingAllowed;
|
|
|
|
void setLastPlayed(char *path) {
|
|
/*
|
|
FILE *lastPlayed = fopen("/scfw/lastplayed.txt", "rb");
|
|
char old_path[PATH_MAX];
|
|
fread(old_path, PATH_MAX, 1, lastPlayed);
|
|
if (strcmp(path, old_path)) {
|
|
freopen("/scfw/lastplayed.txt", "wb", lastPlayed);
|
|
fwrite(path, strlen(path), 1, lastPlayed);
|
|
}
|
|
fclose(lastPlayed);
|
|
*/
|
|
FILE *lastPlayed = fopen("/scfw/lastplayed.txt", "wb");
|
|
fwrite(path, strlen(path), 1, lastPlayed);
|
|
fclose(lastPlayed);
|
|
}
|
|
|
|
void selectFile(char *path) {
|
|
u32 pathlen = strlen(path);
|
|
if (pathlen > 4 && !strcmp(path + pathlen - 4, ".gba")) {
|
|
FILE *rom = fopen(path, "rb");
|
|
fseek(rom, 0, SEEK_END);
|
|
u32 romsize = ftell(rom);
|
|
fseek(rom, 0, SEEK_SET);
|
|
|
|
u32 total_bytes = 0;
|
|
u32 bytes = 0;
|
|
iprintf("Loading ROM:\n\n");
|
|
do {
|
|
bytes = fread(filebuf, 1, sizeof filebuf, rom);
|
|
sc_mode(SC_RAM_RW);
|
|
for (u32 i = 0; i < bytes; i += 4) {
|
|
GBA_ROM[(i + total_bytes) >> 2] = *(vu32*) &filebuf[i];
|
|
if (GBA_ROM[(i + total_bytes) >> 2] != *(vu32*) &filebuf[i]) {
|
|
iprintf("SDRAM write failed!\n");
|
|
tryAgain();
|
|
}
|
|
}
|
|
sc_mode(SC_MEDIA);
|
|
total_bytes += bytes;
|
|
iprintf("\x1b[1A\x1b[K0x%x/0x%x\n", total_bytes, romsize);
|
|
} while (bytes);
|
|
fclose(rom);
|
|
|
|
if (settings.autosave) {
|
|
char savname[PATH_MAX];
|
|
strcpy(savname, path);
|
|
strcpy(savname + pathlen - 4, ".sav");
|
|
|
|
FILE *sav = fopen(savname, "ab");
|
|
freopen(savname, "rb", sav);
|
|
iprintf("Loading SRAM:\n\n");
|
|
total_bytes = 0;
|
|
bytes = 0;
|
|
do {
|
|
bytes = fread(filebuf, 1, sizeof filebuf, sav);
|
|
sc_mode(SC_RAM_RO);
|
|
for (int i = 0; i < bytes; ++i)
|
|
GBA_SRAM[total_bytes + i] = filebuf[i];
|
|
sc_mode(SC_MEDIA);
|
|
total_bytes += bytes;
|
|
iprintf("\x1b[1A\x1b[K0x%x/0x10000\n", total_bytes);
|
|
} while (bytes);
|
|
fclose(sav);
|
|
|
|
FILE *lastSaved = fopen("/scfw/lastsaved.txt", "wb");
|
|
fwrite(savname, pathlen, 1, lastSaved);
|
|
fclose(lastSaved);
|
|
}
|
|
|
|
iprintf("Applying patches...\n");
|
|
sc_mode(SC_RAM_RW);
|
|
patchGeneralWhiteScreen();
|
|
patchSpecificGame();
|
|
|
|
printf("White Screen patch done!\nNow patching Save\n");
|
|
|
|
const struct save_type* saveType = savingAllowed ? save_findTag() : NULL;
|
|
if (saveType != NULL && saveType->patchFunc != NULL){
|
|
bool done = saveType->patchFunc(saveType);
|
|
if(!done)
|
|
printf("Save Type Patch Error\n");
|
|
}else{
|
|
printf("No need to patch\n");
|
|
}
|
|
|
|
iprintf("Let's go.\n");
|
|
setLastPlayed(path);
|
|
|
|
sc_mode(SC_RAM_RO);
|
|
SoftReset(ROM_RESTART);
|
|
} else if (pathlen > 4 && !strcmp(path + pathlen - 4, ".frm")) {
|
|
u32 ime = REG_IME;
|
|
REG_IME = 0;
|
|
|
|
iprintf("Probing flash ID.\n");
|
|
sc_mode(SC_FLASH_RW);
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_MAGIC_1;
|
|
SC_FLASH_MAGIC_ADDR_2 = SC_FLASH_MAGIC_2;
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_IDENTIFY;
|
|
u32 flash_id = SC_FLASH_MAGIC_ADDR_1;
|
|
flash_id |= *GBA_BUS << 16;
|
|
*GBA_BUS = SC_FLASH_IDLE;
|
|
iprintf("Flash ID is 0x%x\n", flash_id);
|
|
if (((flash_id >> 8) & 0xff) != 0x22) {
|
|
iprintf("Unrecognised flash ID.");
|
|
goto fw_end;
|
|
}
|
|
REG_IME = ime;
|
|
|
|
iprintf("Flash the Supercard firmware?\n"
|
|
"It may brick your Supercard!\n"
|
|
"Press A to flash.\n"
|
|
"Press any other key to cancel.\n");
|
|
do {
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
VBlankIntrWait();
|
|
} while (!pressed);
|
|
if (pressed & KEY_A) {
|
|
sc_mode(SC_MEDIA);
|
|
iprintf("Opening firmware\n");
|
|
FILE *fw = fopen(path, "rb");
|
|
fseek(fw, 0, SEEK_END);
|
|
u32 fwsize = ftell(fw);
|
|
fseek(fw, 0, SEEK_SET);
|
|
if (fwsize > 0x80000) {
|
|
iprintf("Firmware too large!\n");
|
|
goto fw_flash_end;
|
|
}
|
|
|
|
ime = 0;
|
|
iprintf("Erasing flash.\n");
|
|
sc_mode(SC_FLASH_RW);
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_MAGIC_1;
|
|
SC_FLASH_MAGIC_ADDR_2 = SC_FLASH_MAGIC_2;
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_ERASE;
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_MAGIC_1;
|
|
SC_FLASH_MAGIC_ADDR_2 = SC_FLASH_MAGIC_2;
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_ERASE_CHIP;
|
|
|
|
while (*GBA_BUS != *GBA_BUS) {
|
|
}
|
|
*GBA_BUS = SC_FLASH_IDLE;
|
|
|
|
u32 total_bytes = 0;
|
|
u32 bytes = 0;
|
|
iprintf("Programming flash.\n\n");
|
|
do {
|
|
sc_mode(SC_MEDIA);
|
|
bytes = fread(filebuf, 1, sizeof filebuf, fw);
|
|
if (ferror(fw)) {
|
|
iprintf("Error reading file!\n");
|
|
goto fw_flash_end;
|
|
}
|
|
sc_mode(SC_FLASH_RW);
|
|
for (u32 i = 0; i < bytes; i += 2) {
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_MAGIC_1;
|
|
SC_FLASH_MAGIC_ADDR_2 = SC_FLASH_MAGIC_2;
|
|
SC_FLASH_MAGIC_ADDR_1 = SC_FLASH_PROGRAM;
|
|
GBA_BUS[(total_bytes + i)>>1] = filebuf[i] | (filebuf[i+1] << 8);
|
|
|
|
while (*GBA_BUS != *GBA_BUS) {
|
|
}
|
|
*GBA_BUS = SC_FLASH_IDLE;
|
|
}
|
|
sc_mode(SC_MEDIA);
|
|
total_bytes += bytes;
|
|
iprintf("\x1b[1A\x1b[K0x%x/0x%x\n", total_bytes, fwsize);
|
|
} while (bytes);
|
|
|
|
iprintf("Done!\n");
|
|
fw_flash_end:
|
|
if (fw)
|
|
fclose(fw);
|
|
}
|
|
fw_end:
|
|
REG_IME = ime;
|
|
iprintf("Press A to continue.\n");
|
|
do {
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
VBlankIntrWait();
|
|
} while (!(pressed & KEY_A));
|
|
} else {
|
|
iprintf("Unrecognised file extension!\n");
|
|
do {
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
VBlankIntrWait();
|
|
} while (!(pressed & KEY_A));
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
irqInit();
|
|
irqEnable(IRQ_VBLANK);
|
|
|
|
scanKeys();
|
|
keysDownRepeat();
|
|
|
|
consoleDemoInit();
|
|
|
|
iprintf("SCFW Kernel v0.2 GBA-mode\n\n");
|
|
|
|
if (fatInitDefault()) {
|
|
iprintf("FAT system initialised\n");
|
|
} else {
|
|
iprintf("FAT initialisation failed!\n");
|
|
tryAgain();
|
|
}
|
|
|
|
settings_file = fopen("/scfw/settings.bin", "rb");
|
|
if (settings_file) {
|
|
struct settings loaded_settings = settings;
|
|
fread(&loaded_settings, sizeof loaded_settings, 1, settings_file);
|
|
if (memcmp(&loaded_settings, &settings, sizeof settings)) {
|
|
settings = loaded_settings;
|
|
freopen("/scfw/settings.bin", "wb", settings_file);
|
|
fwrite(&settings, sizeof settings, 1, settings_file);
|
|
}
|
|
} else {
|
|
settings_file = fopen("/scfw/settings.bin", "wb");
|
|
fwrite(&settings, sizeof settings, 1, settings_file);
|
|
}
|
|
fclose(settings_file);
|
|
|
|
if (settings.autosave) {
|
|
FILE *lastSaved = fopen("/scfw/lastsaved.txt", "rb");
|
|
if (lastSaved) {
|
|
char path[PATH_MAX];
|
|
fread(path, PATH_MAX, 1, lastSaved);
|
|
iprintf("Saving SRAM to %s\n\n", path);
|
|
FILE *sav = fopen(path, "wb");
|
|
for (int i = 0; i < 0x00010000; i += sizeof filebuf) {
|
|
sc_mode(SC_RAM_RO);
|
|
for (int j = 0; j < sizeof filebuf; ++j)
|
|
filebuf[j] = GBA_SRAM[i + j];
|
|
sc_mode(SC_MEDIA);
|
|
fwrite(filebuf, sizeof filebuf, 1, sav);
|
|
iprintf("\x1b[1A\x1b[K0x%x/0x10000\n", i);
|
|
}
|
|
fclose(sav);
|
|
remove("/scfw/lastsaved.txt");
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
char cwd[PATH_MAX];
|
|
getcwd(cwd, PATH_MAX);
|
|
u32 cwdlen = strlen(cwd);
|
|
DIR *dir = opendir(".");
|
|
u32 diroffs[0x200];
|
|
union paging_index diroffs_len;
|
|
diroffs_len.abs = 0;
|
|
for (;;) {
|
|
long diroff = telldir(dir);
|
|
struct dirent *dirent = readdir(dir);
|
|
if (!dirent)
|
|
break;
|
|
if (!strcmp(dirent->d_name, "."))
|
|
continue;
|
|
diroffs[diroffs_len.abs++] = diroff;
|
|
}
|
|
if (!diroffs_len.abs) {
|
|
iprintf("No directory entries!\n");
|
|
tryAgain();
|
|
}
|
|
|
|
for (union paging_index cursor = { .abs = 0 };;) {
|
|
iprintf("\x1b[2J");
|
|
iprintf("%s\n%d/%d\n", cwdlen > 28 ? cwd + cwdlen - 28 : cwd, 1 + cursor.page, (union paging_index){ .abs = 15 + diroffs_len.abs }.page);
|
|
|
|
for (union paging_index i = { .page = cursor.page }; i.abs < diroffs_len.abs && i.page == cursor.page; ++i.abs) {
|
|
seekdir(dir, diroffs[i.abs]);
|
|
struct dirent *dirent = readdir(dir);
|
|
u32 dirent_namelen = strlen(dirent->d_name);
|
|
|
|
char arrow = i.abs == cursor.abs ? '>' : ' ';
|
|
if (dirent->d_type == DT_DIR)
|
|
if (dirent_namelen > 27)
|
|
iprintf("%c%.20s*%s/\n", arrow, dirent->d_name, dirent->d_name + dirent_namelen - 6);
|
|
else
|
|
iprintf("%c%s/\n", arrow, dirent->d_name);
|
|
else
|
|
if (dirent_namelen > 28)
|
|
iprintf("%c%.20s*%s\n", arrow, dirent->d_name, dirent->d_name + dirent_namelen - 7);
|
|
else
|
|
iprintf("%c%s\n", arrow, dirent->d_name);
|
|
}
|
|
|
|
do {
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
VBlankIntrWait();
|
|
} while (!(pressed & (KEY_A | KEY_B | KEY_START | KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT)));
|
|
|
|
seekdir(dir, diroffs[cursor.abs]);
|
|
struct dirent *dirent = readdir(dir);
|
|
if (pressed & KEY_A) {
|
|
if (dirent->d_type == DT_DIR) {
|
|
chdir(dirent->d_name);
|
|
break;
|
|
} else {
|
|
char path[PATH_MAX];
|
|
char *ptr = stpcpy(path, cwd);
|
|
if (ptr[-1] != '/')
|
|
ptr = stpcpy(ptr, "/");
|
|
ptr = stpcpy(ptr, dirent->d_name);
|
|
selectFile(path);
|
|
}
|
|
}
|
|
if (pressed & KEY_B) {
|
|
chdir("..");
|
|
break;
|
|
}
|
|
if (pressed & KEY_START) {
|
|
FILE *lastPlayed = fopen("/scfw/lastplayed.txt", "rb");
|
|
if (lastPlayed) {
|
|
char path[PATH_MAX];
|
|
fread(path, PATH_MAX, 1, lastPlayed);
|
|
fclose(lastPlayed);
|
|
selectFile(path);
|
|
} else {
|
|
iprintf("Could not open last played.\n");
|
|
do {
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
VBlankIntrWait();
|
|
} while (!(pressed & KEY_A));
|
|
}
|
|
}
|
|
if (pressed & KEY_DOWN) {
|
|
++cursor.row;
|
|
if (cursor.abs >= diroffs_len.abs)
|
|
cursor.row = 0;
|
|
}
|
|
if (pressed & KEY_UP) {
|
|
--cursor.row;
|
|
if (cursor.abs >= diroffs_len.abs)
|
|
cursor.row = diroffs_len.row - 1;
|
|
}
|
|
if (pressed & KEY_LEFT) {
|
|
--cursor.page;
|
|
if (cursor.abs < 0) {
|
|
u32 row = cursor.row;
|
|
cursor.abs = diroffs_len.abs - 1;
|
|
if (row < cursor.row)
|
|
cursor.row = row;
|
|
}
|
|
}
|
|
if (pressed & KEY_RIGHT) {
|
|
++cursor.page;
|
|
if (cursor.abs >= diroffs_len.abs) {
|
|
cursor.page = 0;
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
|
|
tryAgain();
|
|
} |