mirror of
https://github.com/ACreTeam/ac-decomp
synced 2026-05-23 06:34:18 -04:00
2818 lines
96 KiB
C++
2818 lines
96 KiB
C++
#include "Famicom/famicomPriv.h"
|
|
#include "Famicom/famicomInternal.hpp"
|
|
|
|
#include "libc/string.h"
|
|
#include "dolphin/gx.h"
|
|
#include "dolphin/os.h"
|
|
#include "dolphin/mtx.h"
|
|
#include "dolphin/card.h"
|
|
#include "_mem.h"
|
|
#include "libultra/libultra.h"
|
|
#include "m_personal_id.h"
|
|
#include "JSystem/JUtility/JUTDbPrint.h"
|
|
#include "JSystem/JKernel/JKRFileFinder.h"
|
|
#include "JSystem/JKernel/JKRFileLoader.h"
|
|
#include "JSystem/JKernel/JKRArchive.h"
|
|
#include "JSystem/JKernel/JKRAram.h"
|
|
#include "JSystem/ResTIMG.h"
|
|
#include "JSystem/J2D/J2DGrafContext.h"
|
|
#include "libc64/sprintf.h"
|
|
#include "libjsys/jsyswrapper.h"
|
|
#include "jsyswrap_cpp.h"
|
|
#include "MSL_C/printf.h"
|
|
#include "jaudio_NES/emusound.h"
|
|
#include "JSystem/JUtility/JUTProcBar.h"
|
|
#include "JSystem/JUtility/JUTGamePad.h"
|
|
#include "nintendo_hi_0.h"
|
|
#include "m_nmibuf.h"
|
|
// #include "JSystem/J2D/J2DGrafContext.h" // needed for ~J2DOrthoGraph
|
|
|
|
/* For some reason, there are a bunch of unused implicit one-byte 4-byte-aligned bss variables */
|
|
#ifdef MUST_MATCH
|
|
static u8 __unused_implicit[0x40] ATTRIBUTE_ALIGN(32);
|
|
#endif
|
|
|
|
static u8 commentImageBuffer[CARD_COMMENT_SIZE + 0x5800];
|
|
u8 save_game_image = false;
|
|
FamicomCommon famicomCommon;
|
|
u8 famicomCommonSave[0x1980 + sizeof(FamicomSaveDataHeader)];
|
|
u8** nesrom_filename_ptrs = nullptr;
|
|
static char* nesrom_filename_strbuf = nullptr;
|
|
u8 InputValid[4];
|
|
u32 InputData[4];
|
|
u32 InputButton[4];
|
|
u32 InputTrigger[4];
|
|
u32 InputRepeat[4];
|
|
u32 InputRepeatCount[4];
|
|
static u8 filer_mode_enable = FALSE;
|
|
static u8 filer_mode = 0;
|
|
static u8 filer_demo_mode = FILER_DEMO_MODE_NORMAL;
|
|
static int filer_demo_count = 0;
|
|
static int nesrom_idx_loaded = 0;
|
|
static int nesrom_idx = 0;
|
|
static int nesrom_count = 0;
|
|
static int rapid_count = 0;
|
|
static u8 rapid_mode_a = FALSE;
|
|
static u8 rapid_mode_b = FALSE;
|
|
static u8 nines_over_double_click = FALSE;
|
|
static u8 nines_over_mode = FALSE;
|
|
static u8 hispeed_mode = FALSE;
|
|
static u8 slow_mode = FALSE;
|
|
static u8 slow_mode_sub = FALSE;
|
|
static u8 speed_show = FALSE;
|
|
static FAMICOM_GETSAVECHAN_PROC famicom_getSaveChanCallback = nullptr;
|
|
Famicom_MallocInfo* my_malloc_current = nullptr;
|
|
static JKRArchive* famicom_arc = nullptr;
|
|
static bool famicom_arc_mounting = FALSE;
|
|
static OSThread ksnes_thread;
|
|
static s8 errorChan = -1;
|
|
|
|
/* @unused decode_szs__FPUcPUc - static void decode_szs(u8*, u8*) */
|
|
|
|
static u8 calcSum(u8* data, size_t len) {
|
|
u8 checksum = 0;
|
|
size_t i;
|
|
|
|
for (i = 0; i != len; i++) {
|
|
checksum += *data++;
|
|
}
|
|
|
|
return checksum;
|
|
}
|
|
|
|
extern int famicom_getErrorChan() {
|
|
return errorChan;
|
|
}
|
|
|
|
static void famicom_save_data_setup(FamicomSaveDataHeader* header, u32 size, u8* identifier) {
|
|
if (strncmp((char*)header->name, (char*)identifier, FAMICOM_SAVE_DATA_NAME_LEN) == 0 &&
|
|
calcSum((u8*)header, FAMICOM_SAVE_HEADER_SIZE) == 0 &&
|
|
header->_08 == 1 &&
|
|
header->_09 == 0 &&
|
|
header->headerSize == FAMICOM_SAVE_HEADER_SIZE &&
|
|
header->size == size) {
|
|
// The header ID & checksum are correct.
|
|
OSReport("ヘッダID&チェックサムは正しいです\n");
|
|
}
|
|
else {
|
|
// Repairing the header.
|
|
OSReport("ヘッダを修復します\n");
|
|
header->_08 = 1;
|
|
header->_09 = 0;
|
|
header->headerSize = FAMICOM_SAVE_HEADER_SIZE;
|
|
header->size = size;
|
|
memcpy(header->name, identifier, FAMICOM_SAVE_DATA_NAME_LEN);
|
|
int checksum = calcSum((u8*)header, FAMICOM_SAVE_HEADER_SIZE);
|
|
header->checksum -= checksum;
|
|
}
|
|
|
|
u8* data = ((u8*)header) + header->headerSize;
|
|
for (u32 i = 0; i < PLAYER_NUM; i++) {
|
|
int versionTag = data[1] >> 4;
|
|
int versionNo = data[1] & 0xF;
|
|
|
|
if (data[0] == 'D' && calcSum(data, size) == 0 &&
|
|
versionTag == 3 && versionNo <= 1 && // check version character is either '0' or '1'
|
|
data[3] == (u8)i) {
|
|
// The personal data checksum %d is correct.
|
|
OSReport("個人データチェックサム%dは正しいです\n", i);
|
|
}
|
|
else {
|
|
// Repairing personal data %d.
|
|
OSReport("個人データ%dを修復します\n", i);
|
|
data[0] = 'D';
|
|
data[1] = '1';
|
|
data[3] = i;
|
|
data[2] -= calcSum(data, size);
|
|
}
|
|
|
|
data += size;
|
|
}
|
|
}
|
|
|
|
static void famicom_save_data_init(FamicomSaveDataHeader* header, u32 size, u8* identifier) {
|
|
bzero(header, FAMICOM_SAVE_HEADER_SIZE + size * PLAYER_NUM);
|
|
header->no_save = TRUE;
|
|
famicom_save_data_setup(header, size, identifier);
|
|
}
|
|
|
|
static int famicom_save_data_check(const FamicomSaveDataHeader* header, int ignore_player_no, const u8* identifier) {
|
|
if (strncmp((char*)header->name, (char*)identifier, FAMICOM_SAVE_DATA_NAME_LEN) == 0) {
|
|
// The header ID is correct.
|
|
OSReport("ヘッダIDは正しいです\n");
|
|
|
|
if (calcSum((u8*)header, FAMICOM_SAVE_HEADER_SIZE) == 0) {
|
|
// The header checksum is correct.
|
|
OSReport("ヘッダチェックサムは正しいです\n");
|
|
|
|
if (((header->_08 == 1 && header->_09 == 0) || (header->_08 == 0 && header->_09 == 1)) && header->headerSize == FAMICOM_SAVE_HEADER_SIZE) {
|
|
// The version and header size match.
|
|
OSReport("バージョンとヘッダサイズが合致しました\n");
|
|
|
|
int i;
|
|
u8* data = ((u8*)header) + header->headerSize;
|
|
|
|
for (i = 0; i < PLAYER_NUM; i++) {
|
|
if (i != ignore_player_no) {
|
|
if (data[0] != 'D' || calcSum(data, header->size) != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
// The personal ID & data checksum %d is correct.
|
|
OSReport("個人ID&データチェックサム%dは正しいです\n", i);
|
|
}
|
|
|
|
data += header->size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
fail:
|
|
// The Famicom common save is invalid.
|
|
OSReport("ファミコン共通セーブは不正です\n");
|
|
return -1;
|
|
}
|
|
|
|
static void process_filer_mode() {
|
|
static const Vec camPt = { 0.0f, 0.0f, 800.0f };
|
|
static const Vec at = { 0.0f, 0.0f, -100.0f };
|
|
static const Vec up = { 0.0f, 1.0f, 0.0f };
|
|
Mtx44 m;
|
|
Mtx34 lookat_m;
|
|
|
|
GXInvalidateTexAll();
|
|
GXInvalidateVtxCache();
|
|
GXSetClipMode(GX_CLIP_DISABLE);
|
|
GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
|
GXSetCopyFilter(GX_FALSE, nullptr, GX_FALSE, nullptr);
|
|
GXSetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
|
|
GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0);
|
|
GXSetZCompLoc(GX_FALSE);
|
|
GXSetColorUpdate(GX_TRUE);
|
|
GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
|
|
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
|
GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
|
GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
|
GXSetTevSwapMode(GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
|
C_MTXOrtho(m, 0.0, -480.0, 0.0, 640.0, 0.0, 2000.0);
|
|
GXSetProjection(m, GX_ORTHOGRAPHIC);
|
|
C_MTXLookAt(lookat_m, &camPt, &up, &at);
|
|
GXSetNumChans(1);
|
|
GXSetNumTexGens(0);
|
|
GXSetNumTevStages(1);
|
|
GXSetNumIndStages(0);
|
|
GXSetTevDirect(GX_TEVSTAGE0);
|
|
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
|
GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
|
|
GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
|
|
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
|
|
GXClearVtxDesc();
|
|
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
|
|
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
|
|
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
|
|
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
|
GXSetCurrentMtx(GX_PNMTX0);
|
|
GXLoadPosMtxImm(lookat_m, GX_PNMTX0);
|
|
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
|
|
|
|
// Vertex 0
|
|
GXPosition2s16(0, 0);
|
|
GXColor1u32(0x00500000);
|
|
// Vertex 1
|
|
GXPosition2s16(640, 0);
|
|
GXColor1u32(0x00000000);
|
|
// Vertex 2
|
|
GXPosition2s16(640, -480);
|
|
GXColor1u32(0x00005000);
|
|
// Vertex 3
|
|
GXPosition2s16(0, -480);
|
|
GXColor1u32(0x00000000);
|
|
|
|
int start_idx;
|
|
if (nesrom_idx < 10 || nesrom_count < 18) {
|
|
start_idx = 0;
|
|
}
|
|
else if ((nesrom_idx + 10) > nesrom_count) {
|
|
start_idx = nesrom_count - 18;
|
|
}
|
|
else {
|
|
start_idx = nesrom_idx - 9;
|
|
}
|
|
|
|
JUTReport(20, 60, 1, "QFC ver.011012 (C)2001 Nintendo");
|
|
if (filer_demo_mode != FILER_DEMO_MODE_NORMAL) {
|
|
JUTReport(30, 80, 1, "[%d/%d] -auto demo- R: cancel", nesrom_idx + 1, nesrom_count);
|
|
}
|
|
else {
|
|
JUTReport(30, 80, 1, "[%d/%d] R: back B: demo", nesrom_idx + 1, nesrom_count);
|
|
}
|
|
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
for (u32 i = 0; i < 18; i++) {
|
|
u32 idx = start_idx + i;
|
|
|
|
if (idx < nesrom_count) {
|
|
JUTReport(30, 100 + i * 20, 1, "%s%s", (nesrom_idx == idx && (filer_demo_mode == FILER_DEMO_MODE_NORMAL || (filer_demo_count & 0x20) == 0)) ? "->" : " ", nesrom_filename_ptrs[idx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
u32 cur_idx;
|
|
|
|
if ((InputTrigger[0] & (JUTGamePad::MAINSTICK_LEFT | JUTGamePad::DPAD_LEFT))) {
|
|
cur_idx = nesrom_idx;
|
|
nesrom_idx--;
|
|
|
|
do {
|
|
nesrom_idx--;
|
|
|
|
while (nesrom_idx < 0) {
|
|
nesrom_idx += nesrom_count;
|
|
}
|
|
} while (nesrom_filename_ptrs[nesrom_idx][0] != ':' && cur_idx != nesrom_idx);
|
|
}
|
|
|
|
if ((InputTrigger[0] & (JUTGamePad::MAINSTICK_RIGHT | JUTGamePad::DPAD_RIGHT))) {
|
|
cur_idx = nesrom_idx;
|
|
do {
|
|
nesrom_idx++;
|
|
|
|
if (nesrom_idx >= nesrom_count) {
|
|
nesrom_idx = 0;
|
|
}
|
|
} while (nesrom_filename_ptrs[nesrom_idx][0] != ':' && cur_idx != nesrom_idx);
|
|
}
|
|
|
|
if ((InputRepeat[0] & (JUTGamePad::MAINSTICK_UP | JUTGamePad::DPAD_UP))) {
|
|
cur_idx = nesrom_idx;
|
|
do {
|
|
if (nesrom_idx > 0) {
|
|
nesrom_idx--;
|
|
}
|
|
else {
|
|
nesrom_idx = nesrom_count - 1;
|
|
}
|
|
} while (nesrom_filename_ptrs[nesrom_idx][0] == ':' && cur_idx != nesrom_idx);
|
|
}
|
|
|
|
if ((InputRepeat[0] & (JUTGamePad::MAINSTICK_DOWN | JUTGamePad::DPAD_DOWN)) != 0) {
|
|
if (nesrom_idx < nesrom_count - 1) {
|
|
nesrom_idx++;
|
|
}
|
|
else {
|
|
nesrom_idx = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((InputTrigger[0] & BUTTON_DLEFT)) {
|
|
filer_mode = 0;
|
|
filer_demo_mode = FILER_DEMO_MODE_AUTO;
|
|
filer_demo_count = 0;
|
|
}
|
|
|
|
if ((InputTrigger[0] & BUTTON_START)) {
|
|
filer_mode = 0;
|
|
filer_demo_mode = FILER_DEMO_MODE_NORMAL;
|
|
}
|
|
}
|
|
|
|
extern void famicom_setCallback_getSaveChan(FAMICOM_GETSAVECHAN_PROC proc) {
|
|
famicom_getSaveChanCallback = proc;
|
|
}
|
|
|
|
static s32 memcard_mount(int, void*);
|
|
|
|
static const s32 checkTableNG[] = {
|
|
CARD_RESULT_FATAL_ERROR,
|
|
CARD_RESULT_IOERROR,
|
|
CARD_RESULT_BROKEN,
|
|
CARD_RESULT_ENCODING,
|
|
CARD_RESULT_WRONGDEVICE
|
|
};
|
|
|
|
static s32 famicom_getSaveChan(int nesrom_memcard, s32* result) {
|
|
s32 chan_result[2];
|
|
s32 card_err;
|
|
int chan;
|
|
int mounted = FALSE;
|
|
void* workArea = nullptr;
|
|
char* file_name;
|
|
|
|
chan_result[0] = CARD_RESULT_READY;
|
|
chan_result[1] = CARD_RESULT_READY;
|
|
|
|
if (nesrom_memcard == FALSE) {
|
|
if (famicom_getSaveChanCallback != NULL) {
|
|
chan = famicom_getSaveChanCallback(nullptr, chan_result);
|
|
|
|
if (chan >= 0) {
|
|
errorChan = chan;
|
|
}
|
|
}
|
|
else {
|
|
chan = -1;
|
|
card_err = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if (chan < 0 && famicomCommon._8C) {
|
|
nesrom_memcard = TRUE;
|
|
}
|
|
}
|
|
|
|
if (nesrom_memcard != FALSE) {
|
|
file_name = (char*)famicomCommon.mura_save_name;
|
|
workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
if (workArea == nullptr) {
|
|
card_err = CARD_RESULT_FATAL_ERROR;
|
|
chan = -1;
|
|
goto exit;
|
|
}
|
|
|
|
for (chan = 0; chan < CARD_NUM_CHANS; chan++) {
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
s32 mount_res = memcard_mount(chan, workArea);
|
|
chan_result[chan] = mount_res;
|
|
mounted = mount_res == CARD_RESULT_READY;
|
|
|
|
if (mounted) {
|
|
if (file_name != nullptr) {
|
|
CARDFileInfo file_info;
|
|
card_err = CARDOpen(chan, file_name, &file_info);
|
|
|
|
chan_result[chan] = card_err;
|
|
if (card_err == CARD_RESULT_READY) {
|
|
CARDClose(&file_info);
|
|
OSReport("チェックファイル存在\n"); // Check file exists.
|
|
errorChan = chan;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
CARDUnmount(chan);
|
|
mounted = FALSE;
|
|
}
|
|
}
|
|
|
|
chan = -1;
|
|
card_err = CARD_RESULT_NOFILE;
|
|
}
|
|
|
|
if (chan < 0) {
|
|
for (int i = 0; i < ARRAY_COUNT(checkTableNG); i++) {
|
|
if (chan_result[0] == checkTableNG[i]) {
|
|
card_err = *(s32*)(chan_result + 0);
|
|
errorChan = 0;
|
|
goto exit;
|
|
}
|
|
|
|
if (chan_result[1] == checkTableNG[i]) {
|
|
card_err = *(s32*)(chan_result + 1);
|
|
errorChan = 1;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if ((chan_result[0] == CARD_RESULT_NOFILE || chan_result[0] == CARD_RESULT_NOCARD || chan_result[0] == CARD_RESULT_READY) &&
|
|
(chan_result[1] == CARD_RESULT_NOFILE || chan_result[1] == CARD_RESULT_NOCARD || chan_result[1] == CARD_RESULT_READY)) {
|
|
card_err = nesrom_memcard ? CARD_RESULT_NOFILE : CARD_RESULT_NOCARD;
|
|
errorChan = CARD_NUM_CHANS;
|
|
}
|
|
else {
|
|
card_err = CARD_RESULT_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
if (result != nullptr) {
|
|
*result = card_err;
|
|
}
|
|
|
|
return chan;
|
|
}
|
|
|
|
static s32 memcard_mount(int chan, void* workArea) {
|
|
s32 result;
|
|
s32 sector_size;
|
|
|
|
// Checking whether the memory card is inserted in the slot.
|
|
OSReport("メモリーカードが、スロットに挿入されているかどうかを調べます。\n");
|
|
|
|
do {
|
|
result = CARDProbeEx(chan, nullptr, §or_size);
|
|
} while (result == CARD_RESULT_BUSY);
|
|
|
|
if (result == CARD_RESULT_READY) {
|
|
if (sector_size != 0x2000) {
|
|
// The sector size is not 8KB.
|
|
OSReport("セクターサイズが8KBではありません。\n");
|
|
result = CARD_RESULT_WRONGDEVICE;
|
|
}
|
|
else {
|
|
// Mounting the memory card.
|
|
OSReport("メモリーカードをマウントする\n");
|
|
result = CARDMount(chan, workArea, nullptr);
|
|
|
|
if (result == CARD_RESULT_READY || result == CARD_RESULT_BROKEN || result == CARD_RESULT_ENCODING) {
|
|
// The memory card has been mounted.
|
|
OSReport("メモリーカードはマウント状態になった\n");
|
|
|
|
if (result == CARD_RESULT_READY || result == CARD_RESULT_BROKEN) {
|
|
if (result == CARD_RESULT_BROKEN) {
|
|
// The memory card is damaged.
|
|
OSReport("メモリーカードは壊れています\n");
|
|
// Attempting to repair the memory card.
|
|
OSReport("メモリーカードの修復を試みます\n");
|
|
}
|
|
|
|
result = CARDCheck(chan);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Failed to repair the memory card.
|
|
OSReport("メモリーカードの修復に失敗しました\n");
|
|
CARDUnmount(chan);
|
|
}
|
|
}
|
|
else {
|
|
// The character encoding of the memory card does not match.
|
|
OSReport("メモリーカードの文字コードがあっていません\n");
|
|
CARDUnmount(chan);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (result != CARD_RESULT_NOCARD) {
|
|
// The memory card is not inserted.
|
|
OSReport("メモリーカードは刺さってない\n");
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_NOCARD && result != CARD_RESULT_FATAL_ERROR) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static s32 memcard_getFreeBlocks(s32 chan, s32* unused_bytes, s32* unused_files) {
|
|
s32 result;
|
|
int mounted = FALSE;
|
|
void* workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
|
|
if (workArea == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
}
|
|
else {
|
|
result = memcard_mount(chan, workArea);
|
|
mounted = result == CARD_RESULT_READY;
|
|
|
|
if (mounted) {
|
|
result = CARDFreeBlocks(chan, unused_bytes, unused_files);
|
|
}
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_NOCARD && result != CARD_RESULT_FATAL_ERROR) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY) {
|
|
// Error code = %d
|
|
OSReport("エラーコード=%d\n", result);
|
|
}
|
|
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* @unused @fabricated - strings necessary for matching */
|
|
static s32 memcard_list(int chan) {
|
|
OSReport("No. Name Co B FileName (YYYY/MM/DD hh:mm:ss) IconAddr IFmt ISpd Pm Cp StB Lng Com\n");
|
|
OSReport("%3d %.4s %.2s %d %-32.32s (%04d/%02d/%02d %02d:%02d:%02d) %03x %04x %04x %02x %2d %3d %3d %04x\n");
|
|
}
|
|
|
|
static size_t getBannerSizeFromFormat(u8 format) {
|
|
switch (format) {
|
|
case CARD_STAT_BANNER_C8:
|
|
return 0xE00;
|
|
case CARD_STAT_BANNER_RGB5A3:
|
|
return 0x1800;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static size_t getIconSizeFromFormat(u16 format) {
|
|
size_t size = 0;
|
|
int has_palette = FALSE;
|
|
|
|
for (int i = 0; i < CARD_ICON_MAX; i++) {
|
|
switch ((format >> (i * 2)) & CARD_STAT_ICON_MASK) {
|
|
case CARD_STAT_ICON_C8:
|
|
{
|
|
has_palette = TRUE;
|
|
size += 0x400;
|
|
break;
|
|
}
|
|
|
|
case CARD_STAT_ICON_RGB5A3:
|
|
{
|
|
size += 0x800;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_palette) {
|
|
size += 0x200;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int SetupResBanner(const ResTIMG*, u8*, size_t, size_t*, u8*);
|
|
static int SetupResIcon(const ResTIMG*, u8*, size_t, size_t*, u16*, u16*);
|
|
|
|
static void SetupExternCommentImage(u8* embedded_save_comment_img, u8* dst, u8* rom_file_comment_img) {
|
|
u32 size;
|
|
|
|
switch (famicomCommon.memcard_game_header.flags0.comment_type) {
|
|
case MEMCARD_COMMENT_TYPE_NONE:
|
|
break;
|
|
case MEMCARD_COMMENT_TYPE_COPY_ROM: {
|
|
memcpy(dst, rom_file_comment_img, CARD_COMMENT_SIZE);
|
|
u8 temp = dst[CARD_COMMENT_SIZE];
|
|
dst[CARD_COMMENT_SIZE] = '\0';
|
|
char* image_name_end = strstr((char*)dst + 32, "] ROM ");
|
|
dst[CARD_COMMENT_SIZE] = temp;
|
|
|
|
if (image_name_end != nullptr) {
|
|
strncpy(image_name_end, "] SAVE", 6);
|
|
dst += CARD_COMMENT_SIZE;
|
|
break;
|
|
}
|
|
// Fallthrough in the case where "] ROM " doesn't exist
|
|
}
|
|
case MEMCARD_COMMENT_TYPE_DEFAULT: {
|
|
strncpy((char*)dst, "Animal Crossing", 32);
|
|
strncpy((char*)dst + 32, "NES Cassette Save Data ", 32);
|
|
dst += CARD_COMMENT_SIZE;
|
|
break;
|
|
}
|
|
case MEMCARD_COMMENT_TYPE_COPY_EMBEDDED: {
|
|
memcpy(dst, embedded_save_comment_img, CARD_COMMENT_SIZE);
|
|
dst += CARD_COMMENT_SIZE;
|
|
embedded_save_comment_img += CARD_COMMENT_SIZE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rom_file_comment_img += CARD_COMMENT_SIZE;
|
|
switch (famicomCommon.memcard_game_header.flags0.banner_type) {
|
|
case MEMCARD_BANNER_TYPE_NONE:
|
|
break;
|
|
case MEMCARD_BANNER_TYPE_DEFAULT: {
|
|
void* banner = JKRFileLoader::getGlbResource("/FAMICOM/newdisk_system.bti.szs");
|
|
ResTIMG* banner_data = (ResTIMG*)banner;
|
|
if (banner_data != nullptr) {
|
|
u8 banner_fmt;
|
|
SetupResBanner(banner_data, dst, 0x1800, &size, &banner_fmt);
|
|
JKRFileLoader::removeResource(banner_data, nullptr);
|
|
dst += size;
|
|
famicomCommon.memcard_game_header.flags1.banner_fmt = banner_fmt;
|
|
}
|
|
break;
|
|
}
|
|
case MEMCARD_BANNER_TYPE_COPY_ROM: {
|
|
size = getBannerSizeFromFormat(famicomCommon.memcard_game_header.flags1.banner_fmt);
|
|
memcpy(dst, rom_file_comment_img, size);
|
|
dst += size;
|
|
break;
|
|
}
|
|
case MEMCARD_BANNER_TYPE_COPY_EMBEDDED: {
|
|
size = getBannerSizeFromFormat(famicomCommon.memcard_game_header.flags1.banner_fmt);
|
|
memcpy(dst, embedded_save_comment_img, size);
|
|
dst += size;
|
|
embedded_save_comment_img += size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
size_t banner_size = getBannerSizeFromFormat(famicomCommon.memcard_game_header.flags1.banner_fmt);
|
|
rom_file_comment_img += banner_size;
|
|
switch (famicomCommon.memcard_game_header.flags0.icon_type) {
|
|
case MEMCARD_ICON_TYPE_NONE:
|
|
break;
|
|
case MEMCARD_ICON_TYPE_DEFAULT: {
|
|
void* icon = JKRFileLoader::getGlbResource("/FAMICOM/newdisk.bti.szs");
|
|
ResTIMG* icon_data = (ResTIMG*)icon;
|
|
if (icon_data != nullptr) {
|
|
u16 icon_fmts;
|
|
u16 icon_flags;
|
|
SetupResIcon(icon_data, dst, 0x800, nullptr, &icon_fmts, &icon_flags);
|
|
JKRFileLoader::removeResource(icon_data, nullptr);
|
|
famicomCommon.memcard_game_header.icon_format = icon_fmts;
|
|
famicomCommon.memcard_game_header.icon_flags = icon_flags;
|
|
size = getIconSizeFromFormat(icon_fmts);
|
|
}
|
|
break;
|
|
}
|
|
case MEMCARD_ICON_TYPE_COPY_ROM: {
|
|
size = getIconSizeFromFormat(famicomCommon.memcard_game_header.icon_format);
|
|
memcpy(dst, rom_file_comment_img, size);
|
|
break;
|
|
}
|
|
case MEMCARD_ICON_TYPE_COPY_EMBEDDED: {
|
|
size = getIconSizeFromFormat(famicomCommon.memcard_game_header.icon_format);
|
|
memcpy(dst, embedded_save_comment_img, size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
size_t icon_size = getIconSizeFromFormat(famicomCommon.memcard_game_header.icon_format); // unnecessary
|
|
}
|
|
|
|
static void SetupInternalCommentImage(u8* data) {
|
|
u8* data_p = data;
|
|
|
|
// Setup comment
|
|
famicomCommon.memcard_game_header.flags0.comment_type = MEMCARD_COMMENT_TYPE_DEFAULT;
|
|
strncpy((char*)data, "Animal Crossing", 32);
|
|
strncpy((char*)data + 32, "NES Save Data ", 32);
|
|
data += CARD_COMMENT_SIZE;
|
|
|
|
// Setup banner
|
|
famicomCommon.memcard_game_header.flags0.banner_type = MEMCARD_BANNER_TYPE_NONE;
|
|
famicomCommon.memcard_game_header.flags1.banner_fmt = CARD_STAT_BANNER_NONE;
|
|
|
|
// Setup icon
|
|
void* icon = JKRFileLoader::getGlbResource("/FAMICOM/famikon.bti.szs");
|
|
ResTIMG* icon_res = (ResTIMG*)icon;
|
|
if (icon_res != nullptr) {
|
|
u16 icon_fmts;
|
|
u16 icon_flags;
|
|
|
|
SetupResIcon(icon_res, data, 0x800, nullptr, &icon_fmts, &icon_flags);
|
|
JKRFileLoader::removeResource(icon_res, nullptr);
|
|
famicomCommon.memcard_game_header.flags0.icon_type = MEMCARD_ICON_TYPE_DEFAULT;
|
|
famicomCommon.memcard_game_header.icon_format = icon_fmts;
|
|
famicomCommon.memcard_game_header.icon_flags = icon_flags;
|
|
data += getIconSizeFromFormat(icon_fmts);
|
|
}
|
|
else {
|
|
famicomCommon.memcard_game_header.flags0.icon_type = MEMCARD_ICON_TYPE_NONE;
|
|
famicomCommon.memcard_game_header.icon_format = 0;
|
|
famicomCommon.memcard_game_header.icon_flags = 0;
|
|
}
|
|
|
|
famicomCommon.memcard_game_header.comment_img_size = (u32)data - (u32)data_p;
|
|
}
|
|
|
|
static s32 memcard_data_save(
|
|
int chan,
|
|
const char* file_name,
|
|
const char* check_file_name,
|
|
const FamicomSaveDataHeader* header,
|
|
size_t save_data_size,
|
|
const u8* comment_img,
|
|
u8 banner_fmt,
|
|
u32 icon_addr,
|
|
u16 icon_fmts,
|
|
u16 icon_flags,
|
|
u32 comment_addr,
|
|
int do_not_save
|
|
) {
|
|
char temp_name[33];
|
|
s32 result;
|
|
size_t save_data_size_block_aligned;
|
|
int mounted = FALSE;
|
|
int opened = FALSE;
|
|
void* workArea;
|
|
u8* save_data = nullptr;
|
|
void* buf = nullptr;
|
|
CARDFileInfo fileInfo;
|
|
u8 cardAttr;
|
|
size_t save_size = 0;
|
|
size_t comment_img_size;
|
|
|
|
// format temp string using file_name as "file_name~"
|
|
sprintf(temp_name, "%-.31s~", file_name);
|
|
workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
|
|
if (workArea == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
save_size = icon_addr != 0xFFFFFFFF ? icon_addr : 0;
|
|
save_size += getBannerSizeFromFormat(banner_fmt);
|
|
save_size += getIconSizeFromFormat(icon_fmts);
|
|
comment_img_size = save_size;
|
|
save_size += save_data_size;
|
|
save_data_size_block_aligned = ALIGN_NEXT(save_size, 0x2000);
|
|
save_data = (u8*)my_malloc(save_data_size_block_aligned, 32);
|
|
|
|
if (save_data == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
bzero(save_data, save_data_size_block_aligned);
|
|
memcpy(save_data, comment_img, comment_img_size); // copy memcard file info data
|
|
memcpy(save_data + comment_img_size, header, save_data_size); // copy data header & save data
|
|
|
|
result = memcard_mount(chan, workArea);
|
|
mounted = result == CARD_RESULT_READY;
|
|
|
|
if (mounted) {
|
|
if (check_file_name != nullptr) {
|
|
result = CARDOpen(chan, check_file_name, &fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
// The check file does not exist.
|
|
OSReport("チェックファイル存在しないよ\n");
|
|
goto exit;
|
|
}
|
|
|
|
CARDClose(&fileInfo);
|
|
}
|
|
|
|
buf = my_malloc(save_data_size_block_aligned, 32);
|
|
if (buf == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
// Not enough memory!
|
|
OSReport("メモリが足りん!\n");
|
|
goto exit;
|
|
}
|
|
|
|
// Add the contents of the file (check for need to save).
|
|
OSReport("ファイルの内容を取り込む(セーブの必要性チェック)\n");
|
|
result = CARDOpen(chan, file_name, &fileInfo);
|
|
if (result == CARD_RESULT_READY) {
|
|
if (do_not_save) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
result = CARDRead(&fileInfo, buf, save_data_size_block_aligned, 0);
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
bool save_updated = bcmp(save_data, buf, save_data_size_block_aligned) == 0;
|
|
if (save_updated) {
|
|
// No need to save, so the process has completed successfully.
|
|
OSReport("セーブする必要が無いので正常終了\n");
|
|
goto exit;
|
|
}
|
|
|
|
// Need to save (update mode).
|
|
OSReport("セーブする必要がある(更新モード)\n");
|
|
}
|
|
else {
|
|
if (result != CARD_RESULT_NOFILE) {
|
|
goto exit;
|
|
}
|
|
|
|
// Need to save (new creation mode).
|
|
OSReport("セーブする必要がある(新規作成モード)\n");
|
|
}
|
|
|
|
result = CARDRename(chan, file_name, temp_name);
|
|
if (result == CARD_RESULT_EXIST) {
|
|
result = CARDDelete(chan, temp_name); // delete temp file
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit; // failed to delete
|
|
}
|
|
result = CARDRename(chan, file_name, temp_name); // try renaming again
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit; // failed to rename after deleting
|
|
}
|
|
}
|
|
else if (result != CARD_RESULT_NOFILE && result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
// Creating file chan=%d [%s] length=%d.
|
|
OSReport("ファイル作成 chan=%d [%s] length=%d\n", chan, file_name, save_data_size_block_aligned);
|
|
result = CARDCreate(chan, file_name, save_data_size_block_aligned, &fileInfo);
|
|
switch (result) {
|
|
case CARD_RESULT_READY:
|
|
break; // success
|
|
case CARD_RESULT_NOENT:
|
|
case CARD_RESULT_INSSPACE: {
|
|
s32 del_res = CARDDelete(chan, temp_name);
|
|
if (del_res == CARD_RESULT_NOFILE) {
|
|
goto exit; // temp file didn't exist?
|
|
}
|
|
|
|
// check for other errors
|
|
if (del_res != CARD_RESULT_READY) {
|
|
result = del_res;
|
|
goto exit;
|
|
}
|
|
|
|
result = CARDCreate(chan, file_name, save_data_size_block_aligned, &fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit; // still failed to create
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// Failed to create file.
|
|
OSReport("ファイル作成失敗\n");
|
|
goto exit;
|
|
}
|
|
|
|
CARDGetAttributes(chan, fileInfo.fileNo, &cardAttr);
|
|
u8 saved_attr = cardAttr;
|
|
|
|
if ((famicomCommon.memcard_game_header.flags0.no_copy_flag)) {
|
|
saved_attr |= CARD_ATTR_NO_COPY;
|
|
}
|
|
|
|
if ((famicomCommon.memcard_game_header.flags1.no_move_flag)) {
|
|
saved_attr |= CARD_ATTR_NO_MOVE;
|
|
}
|
|
|
|
saved_attr |= cardAttr;
|
|
if ((saved_attr) != 0) {
|
|
CARDSetAttributes(chan, fileInfo.fileNo, saved_attr);
|
|
}
|
|
|
|
// Writing the entire file.
|
|
OSReport("ファイル全体の書き込み\n");
|
|
result = CARDWrite(&fileInfo, save_data, save_data_size_block_aligned, 0);
|
|
if (result == CARD_RESULT_READY) {
|
|
// Verifying the entire file.
|
|
OSReport("ファイル全体のベリファイ\n");
|
|
result = CARDRead(&fileInfo, buf, save_data_size_block_aligned, 0);
|
|
if (result == CARD_RESULT_READY) {
|
|
if (bcmp(save_data, buf, save_data_size_block_aligned) == 0) {
|
|
// Verification successful.
|
|
OSReport("ベリファイ成功\n");
|
|
}
|
|
else {
|
|
// Verification failed.
|
|
OSReport("ベリファイ失敗\n");
|
|
result = CARD_RESULT_IOERROR;
|
|
goto exit;
|
|
}
|
|
|
|
// Close.
|
|
OSReport("クローズ\n");
|
|
result = CARDClose(&fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Failed to close?
|
|
OSReport("クローズに失敗って?\n");
|
|
goto exit;
|
|
}
|
|
else {
|
|
// Get status.
|
|
opened = FALSE;
|
|
OSReport("ステータス取得\n");
|
|
}
|
|
|
|
CARDStat status;
|
|
result = CARDGetStatus(chan, fileInfo.fileNo, &status);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Failed to get status.
|
|
OSReport("ステータス取得失敗\n");
|
|
goto exit;
|
|
}
|
|
|
|
status.bannerFormat = banner_fmt;
|
|
status.iconSpeed = icon_flags;
|
|
status.iconAddr = icon_addr;
|
|
status.iconFormat = icon_fmts;
|
|
status.commentAddr = comment_addr;
|
|
// Change status.
|
|
OSReport("ステータス変更\n");
|
|
result = CARDSetStatus(chan, fileInfo.fileNo, &status);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Failed to change status.
|
|
OSReport("ステータス変更失敗\n");
|
|
}
|
|
else {
|
|
result = CARDDelete(chan, temp_name);
|
|
if (result == CARD_RESULT_NOFILE) {
|
|
result = CARD_RESULT_READY; // successfully deleted temp file
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_NOCARD && result != CARD_RESULT_FATAL_ERROR) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY) {
|
|
// Error code = %d.
|
|
OSReport("エラーコード=%d\n", result);
|
|
}
|
|
|
|
if (buf != nullptr) {
|
|
my_free(buf);
|
|
}
|
|
|
|
if (save_data != nullptr) {
|
|
my_free(save_data);
|
|
}
|
|
|
|
if (opened) {
|
|
CARDClose(&fileInfo);
|
|
}
|
|
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static s32 memcard_data_load(
|
|
int chan,
|
|
const char* file_name,
|
|
const char* check_file_name,
|
|
FamicomSaveDataHeader* save_header,
|
|
size_t save_data_size,
|
|
const u8* comment_img
|
|
) {
|
|
char temp_name[33];
|
|
s32 result;
|
|
int mounted = FALSE;
|
|
void* workArea;
|
|
u8* save_data = nullptr;
|
|
int i;
|
|
void* buf = nullptr;
|
|
CARDFileInfo fileInfo;
|
|
CARDStat status;
|
|
|
|
// format temp string using file_name as "file_name~"
|
|
sprintf(temp_name, "%-.31s~", file_name);
|
|
workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
|
|
if (workArea == nullptr) {
|
|
// Not enough memory!
|
|
OSReport("メモリが足りん!!\n");
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
result = memcard_mount(chan, workArea);
|
|
mounted = result == CARD_RESULT_READY;
|
|
|
|
if (!mounted) {
|
|
// Can't mount!
|
|
OSReport("マウントできない!\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (check_file_name != nullptr) {
|
|
result = CARDOpen(chan, check_file_name, &fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
// The check file does not exist.
|
|
OSReport("チェックファイル存在しないよ\n");
|
|
goto exit;
|
|
}
|
|
|
|
CARDClose(&fileInfo);
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
// Opening file [%s]
|
|
OSReport("ファイルのオープン [%s]\n", i == 0 ? file_name : temp_name);
|
|
result = CARDOpen(chan, i == 0 ? file_name : temp_name, &fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Can't open the file.
|
|
OSReport("ファイルがオープンできない\n");
|
|
continue;
|
|
}
|
|
|
|
if (buf != nullptr) {
|
|
my_free(buf);
|
|
buf = nullptr;
|
|
}
|
|
|
|
result = CARDGetStatus(chan, fileInfo.fileNo, &status);
|
|
if (status.offsetData == 0) {
|
|
// File has no icon
|
|
OSReport("アイコンのないファイル\n");
|
|
|
|
if (status.commentAddr == 0xFFFFFFFF) {
|
|
// File has no comment
|
|
OSReport("コメントもないファイル\n");
|
|
// The file is currently being saved
|
|
OSReport("どう考えてもセーブ中のファイル\n");
|
|
if (CARDDelete(chan, file_name) == CARD_RESULT_READY) {
|
|
// Erased the file that was being saved.
|
|
OSReport("セーブ中のファイルを消しました\n");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
size_t len = status.length;
|
|
buf = my_malloc(len, 32);
|
|
|
|
if (buf == nullptr) {
|
|
// Not enough memory!
|
|
OSReport("メモリが足りん!!\n");
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
// Read the file.
|
|
OSReport("ファイル読む\n");
|
|
result = CARDRead(&fileInfo, buf, len, 0);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Can't read the file.
|
|
OSReport("ファイル読めない\n");
|
|
continue;
|
|
}
|
|
|
|
// Close the file.
|
|
OSReport("ファイル閉じる\n");
|
|
result = CARDClose(&fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
// Can't close the file.
|
|
OSReport("ファイル閉じれない\n");
|
|
continue;
|
|
}
|
|
|
|
// Reading successful!
|
|
OSReport("読み込み成功!!\n");
|
|
FamicomSaveDataHeader* read_save_header = (FamicomSaveDataHeader*)((u32)buf + status.offsetData);
|
|
|
|
if (famicom_save_data_check(read_save_header, -1, comment_img) == 0) {
|
|
// The data is normal!
|
|
OSReport("データは正常!!\n");
|
|
|
|
if (i == 0) {
|
|
if (CARDDelete(chan, temp_name) == CARD_RESULT_READY) {
|
|
// Deleted the old data backup.
|
|
OSReport("旧データバックアップを消しました\n");
|
|
}
|
|
}
|
|
else {
|
|
if (CARDDelete(chan, file_name) == CARD_RESULT_READY) {
|
|
// Deleted invalid 'real' file.
|
|
OSReport("不正なホンチャンファイルを消しました\n");
|
|
}
|
|
|
|
if (CARDRename(chan, temp_name, file_name) == CARD_RESULT_READY) {
|
|
// Renamed backup file to 'real' file.
|
|
OSReport("バックアップをホンチャンにリネームしました\n");
|
|
}
|
|
}
|
|
|
|
if (save_header != nullptr) {
|
|
memcpy(save_header, read_save_header, save_data_size);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
result = CARD_RESULT_NOFILE;
|
|
}
|
|
|
|
exit:
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_FATAL_ERROR) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY) {
|
|
// Error code = %d.
|
|
OSReport("エラーコード=%d\n", result);
|
|
}
|
|
|
|
if (buf != nullptr) {
|
|
my_free(buf);
|
|
}
|
|
|
|
if (save_data != nullptr) {
|
|
my_free(save_data);
|
|
}
|
|
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static s32 memcard_game_load(
|
|
u8* nesromp,
|
|
int rom_idx,
|
|
u8** tags_pp,
|
|
char* mura_save_name,
|
|
char* famicom_save_name,
|
|
MemcardGameHeader_t* game_header,
|
|
u8* memcard_save_comment,
|
|
size_t unused_save_data_start_ofs,
|
|
u8* chr_to_i8_bufp,
|
|
size_t chr_to_i8_buf_size
|
|
) {
|
|
s32 result;
|
|
s32 chan;
|
|
CARDFileInfo fileInfo;
|
|
int mounted = FALSE;
|
|
CARDStat cardStatus;
|
|
int now_rom_idx = 0;
|
|
void* workArea;
|
|
s32 fileNo;
|
|
|
|
workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
if (workArea != nullptr) {
|
|
for (chan = 0; chan < CARD_NUM_CHANS; chan++) {
|
|
result = memcard_mount(chan, workArea);
|
|
mounted = result == CARD_RESULT_READY;
|
|
|
|
if (mounted) {
|
|
for (fileNo = 0; fileNo < 127; fileNo++) {
|
|
result = CARDGetStatus(chan, fileNo, &cardStatus);
|
|
if (result == CARD_RESULT_READY) {
|
|
if (
|
|
memcmp(cardStatus.gameName, "GAFE", 4) == 0 &&
|
|
memcmp(cardStatus.company, "01", 2) == 0 &&
|
|
memcmp(cardStatus.fileName, "DobutsunomoriP_F_", 17) == 0 &&
|
|
strcmp(&cardStatus.fileName[17], "SAVE") != 0 /* filename can't be Doubutsunomori_P_F_SAVE* */
|
|
) {
|
|
if (rom_idx == now_rom_idx) {
|
|
/* Incorporate the contents of the file. */
|
|
OSReport("ファイルの内容を取り込む\n");
|
|
result = CARDFastOpen(chan, fileNo, &fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
if (cardStatus.offsetData == 0) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
/* Reading the entire file. */
|
|
OSReport("ファイル全体の読み出し\n");
|
|
u8* data_bufp = cardStatus.length <= chr_to_i8_buf_size ? chr_to_i8_bufp : nullptr;
|
|
|
|
if (data_bufp == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
result = CARDRead(&fileInfo, data_bufp, cardStatus.length, 0);
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
/* File close. */
|
|
OSReport("ファイルクローズ\n");
|
|
result = CARDClose(&fileInfo);
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
/* Unmount. */
|
|
OSReport("アンマウント\n");
|
|
result = CARDUnmount(chan);
|
|
mounted = FALSE;
|
|
if (result != CARD_RESULT_READY) {
|
|
goto exit;
|
|
}
|
|
|
|
/* Reading successful! */
|
|
OSReport("読み込み成功!!\n");
|
|
if (calcSum(data_bufp, cardStatus.length) != 0 || cardStatus.offsetData == 0) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if (mura_save_name != nullptr) {
|
|
memcpy(mura_save_name, cardStatus.fileName, 32);
|
|
}
|
|
|
|
if (famicom_save_name != nullptr) {
|
|
memcpy(famicom_save_name, cardStatus.fileName, 32);
|
|
famicom_save_name[15] = 'S';
|
|
}
|
|
|
|
u8* datap = &data_bufp[cardStatus.offsetData];
|
|
memcpy(game_header, datap, sizeof(MemcardGameHeader_t));
|
|
datap += sizeof(MemcardGameHeader_t);
|
|
|
|
/* Copy nesinfo tags */
|
|
size_t tags_size = game_header->nestags_size;
|
|
if (tags_size != 0) {
|
|
if (tags_pp != nullptr) {
|
|
*tags_pp = (u8*)my_malloc(tags_size, 1);
|
|
}
|
|
|
|
/* @BUG - tags_pp might be a nullptr but we're dereferencing it anyway */
|
|
memcpy(*tags_pp, datap, tags_size);
|
|
}
|
|
else {
|
|
/* @BUG - tags_pp might be a nullptr but we're dereferencing it anyway */
|
|
*tags_pp = nullptr;
|
|
}
|
|
|
|
datap += ALIGN_NEXT(tags_size, 16);
|
|
|
|
size_t comment_img_size;
|
|
if (game_header->flags0.has_comment_img && (comment_img_size = game_header->comment_img_size, comment_img_size != 0)) {
|
|
if (JC_JKRDecomp_checkCompressed(datap) == JKRCOMPRESSION_NONE) {
|
|
SetupExternCommentImage(datap, memcard_save_comment, data_bufp);
|
|
}
|
|
else {
|
|
JC_JKRDecomp_decode(datap, nesromp, CHR_TO_I8_BUF_SIZE, 0);
|
|
SetupExternCommentImage(nesromp, memcard_save_comment, data_bufp);
|
|
}
|
|
|
|
datap += ALIGN_NEXT(comment_img_size, 16);
|
|
}
|
|
else {
|
|
SetupExternCommentImage(nullptr, memcard_save_comment, data_bufp);
|
|
}
|
|
|
|
if (game_header->nesrom_size != 0) {
|
|
u32 real_size = game_header->nesrom_size << 4;
|
|
|
|
if (JC_JKRDecomp_checkCompressed(datap) == JKRCOMPRESSION_NONE) {
|
|
memcpy(nesromp, datap, real_size);
|
|
nesinfo_data_size = real_size;
|
|
}
|
|
else {
|
|
JC_JKRDecomp_decode(datap, nesromp, CHR_TO_I8_BUF_SIZE, 0);
|
|
nesinfo_data_size = (u32)((datap[4] << 24) | (datap[5] << 16) | (datap[6]<< 8) | datap[7]);
|
|
}
|
|
}
|
|
|
|
result = CARD_RESULT_READY;
|
|
goto exit; // success
|
|
}
|
|
else {
|
|
now_rom_idx++;
|
|
}
|
|
}
|
|
}
|
|
else if (result != CARD_RESULT_NOFILE) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (fileNo >= 127) {
|
|
/* There was no target game image in slot %d. */
|
|
OSReport("スロット%dには目的のゲームイメージがありませんでした\n", chan);
|
|
CARDUnmount(chan);
|
|
mounted = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The rom file was not found */
|
|
result = CARD_RESULT_NOFILE;
|
|
}
|
|
else {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
}
|
|
|
|
exit:
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_FATAL_ERROR) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY) {
|
|
// Error code = %d.
|
|
OSReport("エラーコード=%d\n", result);
|
|
}
|
|
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static s32 memcard_game_list(
|
|
int* n_games,
|
|
char* namebufp,
|
|
int namebuf_size
|
|
) {
|
|
CARDFileInfo fileInfo;
|
|
CARDStat cardStatus;
|
|
s32 result;
|
|
s32 chan;
|
|
int mounted = FALSE;
|
|
int game_count = 0;
|
|
void* workArea;
|
|
s32 fileNo;
|
|
void* buf = nullptr;
|
|
u32 data_ofs;
|
|
|
|
workArea = my_malloc(CARD_WORKAREA_SIZE, 32);
|
|
if (workArea == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
buf = my_malloc(0x200, 32);
|
|
if (buf == nullptr) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
for (chan = 0; chan < CARD_NUM_CHANS; chan++) {
|
|
mounted = memcard_mount(chan, workArea) == CARD_RESULT_READY;
|
|
|
|
if (mounted) {
|
|
for (fileNo = 0; fileNo < 127; fileNo++) {
|
|
result = CARDGetStatus(chan, fileNo, &cardStatus);
|
|
if (result == CARD_RESULT_READY) {
|
|
if (
|
|
memcmp(cardStatus.gameName, "GAFE", 4) == 0 &&
|
|
memcmp(cardStatus.company, "01", 2) == 0 &&
|
|
memcmp(cardStatus.fileName, "DobutsunomoriP_F_", 17) == 0 &&
|
|
strcmp(&cardStatus.fileName[17], "SAVE") != 0 /* filename can't be Doubutsunomori_P_F_SAVE* */
|
|
) {
|
|
if (namebuf_size >= 16) {
|
|
int invalid_game = FALSE;
|
|
|
|
/* Incorporate the contents of the file. */
|
|
OSReport("ファイルの内容を取り込む\n");
|
|
static char brokenTitle[] = "##BROKEN GAME##";
|
|
|
|
if (CARDFastOpen(chan, fileNo, &fileInfo) != CARD_RESULT_READY) {
|
|
invalid_game = TRUE;
|
|
}
|
|
else if (cardStatus.offsetData == 0) {
|
|
invalid_game = TRUE;
|
|
}
|
|
else {
|
|
/* Reading the beginning of the data. */
|
|
OSReport("データ先頭の読み出し\n");
|
|
data_ofs = cardStatus.offsetData - ALIGN_PREV(cardStatus.offsetData, 0x200);
|
|
if (CARDRead(&fileInfo, buf, 0x200, ALIGN_PREV(cardStatus.offsetData, 0x200)) != CARD_RESULT_READY) {
|
|
invalid_game = TRUE;
|
|
}
|
|
else {
|
|
/* File close. */
|
|
OSReport("ファイルクローズ\n");
|
|
if (CARDClose(&fileInfo) != CARD_RESULT_READY) {
|
|
invalid_game = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (invalid_game == FALSE) {
|
|
/* Reading successful! */
|
|
OSReport("読み込み成功!!\n");
|
|
memcpy(namebufp, (u8*)buf + data_ofs + 2, MURA_GAME_NAME_SIZE); // read mori name
|
|
}
|
|
else {
|
|
/* Reading failed! */
|
|
OSReport("読み込み失敗!!\n");
|
|
memcpy(namebufp, brokenTitle, MURA_GAME_NAME_SIZE);
|
|
}
|
|
|
|
namebufp += MURA_GAME_NAME_SIZE;
|
|
namebuf_size -= MURA_GAME_NAME_SIZE;
|
|
}
|
|
|
|
(*n_games)++;
|
|
game_count++;
|
|
}
|
|
}
|
|
else if (result != CARD_RESULT_NOFILE) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (fileNo >= 127) {
|
|
/* Finished searching slot %d. */
|
|
OSReport("スロット%d検索終了\n", chan);
|
|
CARDUnmount(chan);
|
|
mounted = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
result = CARD_RESULT_READY;
|
|
*n_games = game_count;
|
|
exit:
|
|
if (result != CARD_RESULT_READY) {
|
|
// Error code = %d.
|
|
OSReport("エラーコード=%d\n", result);
|
|
}
|
|
|
|
if (buf != nullptr) {
|
|
my_free(buf);
|
|
}
|
|
|
|
if (mounted) {
|
|
CARDUnmount(chan);
|
|
}
|
|
|
|
if (workArea != nullptr) {
|
|
my_free(workArea);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
inline JKRFileFinder* getFirstFile(const char* dir) {
|
|
return JKRFileLoader::findFirstFile(dir);
|
|
}
|
|
|
|
static size_t entrylist(const char* dir, char* filename_strbuf) {
|
|
char pathbuf[256];
|
|
u8* filename_buf = (u8*)filename_strbuf;
|
|
JKRFileFinder* fileFinder;
|
|
s32 dir_path_len;
|
|
size_t len;
|
|
|
|
fileFinder = getFirstFile(dir);
|
|
dir_path_len = strlen(dir);
|
|
|
|
if (filename_strbuf != nullptr) {
|
|
filename_strbuf[0] = ':';
|
|
memcpy(filename_strbuf + 1, dir, dir_path_len + 1);
|
|
nesrom_filename_ptrs[nesrom_count] = filename_buf;
|
|
}
|
|
|
|
nesrom_count++;
|
|
len = dir_path_len + 2;
|
|
if (fileFinder != nullptr) {
|
|
while (fileFinder->isAvailable()) {
|
|
if (fileFinder->mBase.mFileName[0] != '.') {
|
|
snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, fileFinder->mBase.mFileName);
|
|
dir_path_len = strlen(pathbuf);
|
|
if (fileFinder->mIsDir) {
|
|
len += entrylist(pathbuf, filename_strbuf != nullptr ? filename_strbuf + len : nullptr);
|
|
}
|
|
else {
|
|
if (filename_strbuf != nullptr) {
|
|
u8* romname_p = filename_buf + len;
|
|
|
|
memcpy(romname_p, pathbuf, dir_path_len + 1);
|
|
nesrom_filename_ptrs[nesrom_count] = romname_p;
|
|
}
|
|
|
|
nesrom_count++;
|
|
len = 1 + dir_path_len + len;
|
|
}
|
|
}
|
|
|
|
fileFinder->findNextFile();
|
|
}
|
|
|
|
delete fileFinder;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void select_game() {
|
|
int rom_no = famicomCommon.rom_no;
|
|
int rom = rom_no;
|
|
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
nesrom_idx = 0;
|
|
|
|
while (TRUE) {
|
|
if (*nesrom_filename_ptrs[nesrom_idx] != ':' &&--rom <= 0) {
|
|
break;
|
|
}
|
|
|
|
nesrom_idx++;
|
|
|
|
if (nesrom_idx >= nesrom_count) {
|
|
if (nesrom_idx_loaded < 0) {
|
|
OSPanic(__FILE__, 4239, "no nesfile specified");
|
|
continue;
|
|
}
|
|
|
|
nesrom_idx = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
nesrom_idx = rom_no;
|
|
}
|
|
}
|
|
|
|
extern int famicom_mount_archive_end_check() {
|
|
return famicom_arc != nullptr;
|
|
}
|
|
|
|
extern void famicom_mount_archive() {
|
|
if (famicom_arc == nullptr && famicom_arc_mounting == false) {
|
|
famicom_arc_mounting = true;
|
|
famicom_arc = (JKRArchive*)JC__JKRMountArchive("famicom.arc", JKRArchive::MOUNT_COMP, JC__JKRGetSystemHeap(), JKRArchive::MOUNT_DIRECTION_HEAD);
|
|
|
|
if (famicom_arc == nullptr) {
|
|
OSDVDFatalError();
|
|
}
|
|
|
|
famicom_arc_mounting = false;
|
|
}
|
|
|
|
JKRAram::getAramHeap()->dump();
|
|
}
|
|
|
|
static void famicom_mount_archive_wait() {
|
|
if (famicom_arc == nullptr) {
|
|
// Waiting because famicom.arc has not been mounted yet.
|
|
OSReport("famicom.arc がまだマウントされていないので待っています\n");
|
|
|
|
do {
|
|
VIWaitForRetrace();
|
|
} while (famicom_arc == nullptr);
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
#define MALLOC_MALLOC(type, size, dst, line) \
|
|
if (dst != nullptr) { \
|
|
OSPanic(__FILE__, line, "MALLOC_MALLOC: " #dst " isn't NULL"); \
|
|
} \
|
|
dst = (type *)my_malloc(size, 32); \
|
|
if (dst == nullptr) { \
|
|
/* MALLOC_MALLOC: %s %s %dByte allocation failed. */ \
|
|
OSReport("MALLOC_MALLOC: %s %s %dByte 確保失敗\n", #type, #size, size); \
|
|
goto exit; \
|
|
} \
|
|
/* MALLOC_MALLOC: %s %s %dByte successfully allocated. */ \
|
|
OSReport("MALLOC_MALLOC: %s %s %dByte 確保成功\n", #type, #size, size);
|
|
// clang-format on
|
|
|
|
#define CONV_BYTE_KB(bytes) ((f32)(bytes) / 1024.0f)
|
|
|
|
extern int famicom_init(int rom_idx, Famicom_MallocInfo* malloc_info, int player_no) {
|
|
famicom_mount_archive_wait();
|
|
bzero(&famicomCommon, sizeof(FamicomCommon));
|
|
my_malloc_current = malloc_info;
|
|
|
|
if (player_no >= 0 && player_no < PLAYER_NUM) {
|
|
famicomCommon.save_pl_no = player_no;
|
|
}
|
|
else {
|
|
famicomCommon.save_pl_no = 0;
|
|
}
|
|
|
|
if (rom_idx == 0) {
|
|
filer_mode_enable = TRUE;
|
|
}
|
|
else {
|
|
filer_mode_enable = FALSE;
|
|
}
|
|
|
|
filer_mode = 0;
|
|
filer_demo_mode = FILER_DEMO_MODE_NORMAL;
|
|
filer_demo_count = 0;
|
|
nesrom_idx_loaded = -1;
|
|
|
|
if (rom_idx == 0) {
|
|
famicomCommon.nesrom_memcard = FALSE;
|
|
famicomCommon.rom_no = 0;
|
|
nesrom_idx = 1;
|
|
}
|
|
else if (rom_idx > 0) {
|
|
famicomCommon.nesrom_memcard = FALSE;
|
|
famicomCommon.rom_no = rom_idx;
|
|
|
|
if (famicomCommon.save_pl_no >= 0 && save_game_image == 0) {
|
|
famicomCommon.save_data_name = (u8*)"GAFEFSC\x1A";
|
|
famicomCommon.save_data_single_size = 0x660;
|
|
famicomCommon.save_data_total_size = sizeof(FamicomSaveDataHeader) + 0x660 * PLAYER_NUM;
|
|
famicomCommon.save_data_header = (FamicomSaveDataHeader*)famicomCommonSave;
|
|
famicomCommon.internal_save_datap = famicomCommonSave + sizeof(FamicomSaveDataHeader) + 0x660 * famicomCommon.save_pl_no;
|
|
}
|
|
|
|
nesrom_idx = famicomCommon.rom_no + 1;
|
|
strncpy((char*)famicomCommon.mura_save_name, "DobutsunomoriP_MURA", 32);
|
|
strncpy((char*)famicomCommon.famicom_save_name, "DobutsunomoriP_F_SAVE", 32);
|
|
famicomCommon.unused_save_data_start_ofs = 0x640;
|
|
}
|
|
else if (rom_idx < 0) {
|
|
famicomCommon.nesrom_memcard = TRUE;
|
|
famicomCommon.rom_no = -rom_idx;
|
|
famicomCommon.unused_save_data_start_ofs = 0x3040;
|
|
}
|
|
|
|
nesrom_count = 0;
|
|
rapid_count = 0;
|
|
rapid_mode_a = 0;
|
|
rapid_mode_b = 0;
|
|
nines_over_double_click = 0;
|
|
nines_over_mode = 0;
|
|
hispeed_mode = 0;
|
|
slow_mode = 0;
|
|
slow_mode_sub = 0;
|
|
speed_show = 0;
|
|
JKRFileLoader::changeDirectory("/FAMICOM/");
|
|
size_t unused_totalfreesize = my_gettotalfreesize();
|
|
|
|
MALLOC_MALLOC(u8, CHR_TO_I8_BUF_SIZE, famicomCommon.chr_to_i8_bufp, 4497);
|
|
MALLOC_MALLOC(u8, KS_NES_NESFILE_HEADER_SIZE + KS_NES_PRGROM_SIZE + KS_NES_CHRROM_SIZE, famicomCommon.nesromp, 4499);
|
|
MALLOC_MALLOC(ksNesCommonWorkObj, sizeof(ksNesCommonWorkObj), famicomCommon.wp, 4501);
|
|
MALLOC_MALLOC(ksNesStateObj, sizeof(ksNesStateObj), famicomCommon.sp, 4502);
|
|
MALLOC_MALLOC(u8, KS_NES_CHRRAM_SIZE, famicomCommon.chrramp, 4505);
|
|
MALLOC_MALLOC(u8, KS_NES_BBRAM_SIZE, famicomCommon.bbramp, 4510);
|
|
MALLOC_MALLOC(u8, KS_NES_NOISE_DATA_SIZE, famicomCommon.noise_bufp, 4515);
|
|
MALLOC_MALLOC(u8, KS_NES_DRAW_RESULT_BUF_SIZE, famicomCommon.result_bufp, 4517);
|
|
|
|
famicomCommon.memcard_save_comment = commentImageBuffer;
|
|
|
|
OSReport("buffer used:\n");
|
|
OSReport("wp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.wp)));
|
|
OSReport("sp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.sp)));
|
|
OSReport("nesromp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.nesromp)));
|
|
OSReport("chrramp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.chrramp)));
|
|
OSReport("bbramp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.bbramp)));
|
|
OSReport("noise_bufp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.noise_bufp)));
|
|
OSReport("chr_to_i8_bufp=%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.chr_to_i8_bufp)));
|
|
OSReport("result_bufp =%8.3fKB\n", CONV_BYTE_KB(my_getmemblocksize(famicomCommon.result_bufp)));
|
|
OSReport("total %8.3fKB.\nif chrrom-type, and no bbram, minimum total size: %8.3fKB + nesrom size max + (nesrom chr size max * 4) bytes.\n",
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.wp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.sp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.nesromp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.chrramp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.bbramp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.noise_bufp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.chr_to_i8_bufp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.result_bufp)),
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.wp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.sp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.noise_bufp)) +
|
|
CONV_BYTE_KB(my_getmemblocksize(famicomCommon.result_bufp))
|
|
);
|
|
|
|
if (famicomCommon.noise_bufp == nullptr || JKRFileLoader::readGlbResource(famicomCommon.noise_bufp, KS_NES_NOISE_DATA_SIZE, "noise.bin.szs", EXPAND_SWITCH_DECOMPRESS)) {
|
|
if (famicomCommon.noise_bufp != nullptr) {
|
|
EmuSound_Start(famicomCommon.noise_bufp + 0x2000);
|
|
}
|
|
|
|
nesrom_idx_loaded = -1;
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
for (u32 i = 0; i < 2; i++) {
|
|
nesrom_count = 0;
|
|
size_t length = entrylist("GAME", nesrom_filename_strbuf);
|
|
if (i == 0) {
|
|
if (nesrom_count == 0) {
|
|
OSReport("no nesfile specified\n");
|
|
goto exit;
|
|
}
|
|
|
|
MALLOC_MALLOC(u8 *, nesrom_count * sizeof(u8*), nesrom_filename_ptrs, 4676);
|
|
MALLOC_MALLOC(char, length, nesrom_filename_strbuf, 4677);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
nesrom_count = 0;
|
|
}
|
|
|
|
select_game();
|
|
JUTProcBar::getManager()->setVisible(false);
|
|
famicomCommon.low_res_mode = true;
|
|
JW_SetFamicomMode(TRUE);
|
|
JW_SetLowResoMode(famicomCommon.low_res_mode);
|
|
return 0; // success
|
|
}
|
|
|
|
// error
|
|
exit:
|
|
if (famicomCommon.wp != nullptr) {
|
|
my_free(famicomCommon.wp);
|
|
}
|
|
famicomCommon.wp = nullptr;
|
|
|
|
if (famicomCommon.sp != nullptr) {
|
|
my_free(famicomCommon.sp);
|
|
}
|
|
famicomCommon.sp = nullptr;
|
|
|
|
if (famicomCommon.nesromp != nullptr) {
|
|
my_free(famicomCommon.nesromp);
|
|
}
|
|
famicomCommon.nesromp = nullptr;
|
|
|
|
if (famicomCommon.chrramp != nullptr) {
|
|
my_free(famicomCommon.chrramp);
|
|
}
|
|
famicomCommon.chrramp = nullptr;
|
|
|
|
if (famicomCommon.bbramp != nullptr) {
|
|
my_free(famicomCommon.bbramp);
|
|
}
|
|
famicomCommon.bbramp = nullptr;
|
|
|
|
if (famicomCommon.noise_bufp != nullptr) {
|
|
my_free(famicomCommon.noise_bufp);
|
|
}
|
|
famicomCommon.noise_bufp = nullptr;
|
|
|
|
if (famicomCommon.chr_to_i8_bufp != nullptr) {
|
|
my_free(famicomCommon.chr_to_i8_bufp);
|
|
}
|
|
famicomCommon.chr_to_i8_bufp = nullptr;
|
|
|
|
if (famicomCommon.result_bufp != nullptr) {
|
|
my_free(famicomCommon.result_bufp);
|
|
}
|
|
famicomCommon.result_bufp = nullptr;
|
|
|
|
if (famicomCommon.highscore_flagsp != nullptr) {
|
|
my_free(famicomCommon.highscore_flagsp);
|
|
}
|
|
famicomCommon.highscore_flagsp = nullptr;
|
|
|
|
if (famicomCommon.nesinfo_tagsp != nullptr) {
|
|
my_free(famicomCommon.nesinfo_tagsp);
|
|
}
|
|
famicomCommon.nesinfo_tagsp = nullptr;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int SetupResBanner(const ResTIMG* img, u8* dst, size_t max_size, size_t* size, u8* type) {
|
|
size_t banner_size;
|
|
u8* data_p = dst;
|
|
int banner_type;
|
|
|
|
if (img != nullptr) {
|
|
banner_size = img->getWidth() * img->getHeight();
|
|
if (img->mTextureFormat != ResTIMG_FORMAT_C8) {
|
|
banner_size *= sizeof(u16);
|
|
banner_type = CARD_STAT_BANNER_RGB5A3;
|
|
}
|
|
else {
|
|
banner_type = CARD_STAT_BANNER_C8;
|
|
}
|
|
|
|
bcopy(((u8*)img) + img->mImageDataOffset, data_p, banner_size);
|
|
data_p += banner_size;
|
|
|
|
if (img->mPaletteFormat != ResTIMG_NO_PALETTE) {
|
|
size_t pal_size = img->mPaletteEntryCount * sizeof(u16);
|
|
|
|
bcopy(((u8*)img) + img->mPaletteOffset, data_p, pal_size);
|
|
bzero(data_p + pal_size, (256 * sizeof(u16)) - pal_size); // zero out unused entries
|
|
data_p += 256 * sizeof(u16);
|
|
}
|
|
}
|
|
else {
|
|
banner_type = CARD_STAT_BANNER_NONE;
|
|
}
|
|
|
|
if (size != nullptr) {
|
|
*size = (u32)data_p - (u32)dst;
|
|
}
|
|
|
|
if (type != nullptr) {
|
|
*type = banner_type;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int SetupResIcon(const ResTIMG* img, u8* dst, size_t max_size, size_t* size_p, u16* icon_fmt_p, u16* icon_flags_p) {
|
|
u8* data_p = dst;
|
|
size_t icon_size;
|
|
u16 icon_fmt;
|
|
u16 icon_flags;
|
|
int banner_type;
|
|
u32 icon_type;
|
|
int i;
|
|
u32 icon_count;
|
|
int j;
|
|
|
|
if (img != nullptr) {
|
|
icon_size = img->getWidth() * img->getHeight();
|
|
icon_count = icon_size / 0x400;
|
|
|
|
if (img->mTextureFormat != ResTIMG_FORMAT_C8) {
|
|
icon_size *= sizeof(u16);
|
|
icon_type = CARD_STAT_ICON_RGB5A3;
|
|
}
|
|
else {
|
|
icon_type = CARD_STAT_ICON_C8;
|
|
}
|
|
|
|
/* Make icon format flags & speed flags for each frame */
|
|
icon_fmt = 0;
|
|
icon_flags = 0;
|
|
i = 0;
|
|
|
|
for (j = 0; j < icon_count; j++) {
|
|
icon_fmt |= (icon_type << (i * 2));
|
|
icon_flags |= (CARD_STAT_SPEED_MIDDLE << (i * 2));
|
|
i++;
|
|
}
|
|
|
|
bcopy(((u8*)img) + img->mImageDataOffset, data_p, icon_size);
|
|
data_p += icon_size;
|
|
|
|
if (img->mPaletteFormat != ResTIMG_NO_PALETTE) {
|
|
size_t pal_size = img->mPaletteEntryCount * sizeof(u16);
|
|
|
|
bcopy(((u8*)img) + img->mPaletteOffset, data_p, pal_size);
|
|
bzero(data_p + pal_size, (256 * sizeof(u16)) - pal_size); // zero out unused entries
|
|
data_p += 256 * sizeof(u16);
|
|
}
|
|
}
|
|
else {
|
|
icon_fmt = CARD_STAT_ICON_NONE;
|
|
}
|
|
|
|
if (size_p != nullptr) {
|
|
*size_p = (u32)data_p - (u32)dst;
|
|
}
|
|
|
|
if (icon_fmt_p != nullptr) {
|
|
*icon_fmt_p = icon_fmt;
|
|
}
|
|
|
|
if (icon_flags_p != nullptr) {
|
|
*icon_flags_p = icon_flags;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern int famicom_cleanup() {
|
|
int res = FALSE;
|
|
|
|
if (nesrom_idx_loaded != -1 && save_game_image == 0) {
|
|
OSReport("nesrom_memcard = %d nesrom_idx = %d\n", famicomCommon.nesrom_memcard, nesrom_idx);
|
|
|
|
if (famicomCommon.internal_save_datap != nullptr) {
|
|
nesinfo_tag_process3(famicomCommon.internal_save_datap);
|
|
}
|
|
|
|
if (famicomCommon.save_pl_no >= 0 && famicomCommon.save_data_header != nullptr) {
|
|
if (famicom_save_data_check((FamicomSaveDataHeader*)famicomCommon.save_data_header, famicomCommon.save_pl_no, famicomCommon.save_data_name) == 0) {
|
|
famicom_save_data_setup((FamicomSaveDataHeader*)famicomCommon.save_data_header, famicomCommon.save_data_single_size, famicomCommon.save_data_name);
|
|
}
|
|
else {
|
|
// Will not save because the common save area is damaged.
|
|
OSReport(VT_COL(RED, WHITE) "共通セーブ領域が壊れているのでセーブしません\n" VT_RST);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (famicomCommon.noise_bufp != nullptr) {
|
|
EmuSound_Exit();
|
|
}
|
|
|
|
if (famicomCommon.wp != nullptr) {
|
|
my_free(famicomCommon.wp);
|
|
}
|
|
famicomCommon.wp = nullptr;
|
|
|
|
if (famicomCommon.sp != nullptr) {
|
|
my_free(famicomCommon.sp);
|
|
}
|
|
famicomCommon.sp = nullptr;
|
|
|
|
if (famicomCommon.nesromp != nullptr) {
|
|
my_free(famicomCommon.nesromp);
|
|
}
|
|
famicomCommon.nesromp = nullptr;
|
|
|
|
if (famicomCommon.chrramp != nullptr) {
|
|
my_free(famicomCommon.chrramp);
|
|
}
|
|
famicomCommon.chrramp = nullptr;
|
|
|
|
if (famicomCommon.bbramp != nullptr) {
|
|
my_free(famicomCommon.bbramp);
|
|
}
|
|
famicomCommon.bbramp = nullptr;
|
|
|
|
if (famicomCommon.noise_bufp != nullptr) {
|
|
my_free(famicomCommon.noise_bufp);
|
|
}
|
|
famicomCommon.noise_bufp = nullptr;
|
|
|
|
if (famicomCommon.chr_to_i8_bufp != nullptr) {
|
|
my_free(famicomCommon.chr_to_i8_bufp);
|
|
}
|
|
famicomCommon.chr_to_i8_bufp = nullptr;
|
|
|
|
if (famicomCommon.result_bufp != nullptr) {
|
|
my_free(famicomCommon.result_bufp);
|
|
}
|
|
famicomCommon.result_bufp = nullptr;
|
|
|
|
if (famicomCommon.highscore_flagsp != nullptr) {
|
|
my_free(famicomCommon.highscore_flagsp);
|
|
}
|
|
famicomCommon.highscore_flagsp = nullptr;
|
|
|
|
if (nesrom_filename_ptrs != nullptr) {
|
|
my_free(nesrom_filename_ptrs);
|
|
}
|
|
nesrom_filename_ptrs = nullptr;
|
|
|
|
if (nesrom_filename_strbuf != nullptr) {
|
|
my_free(nesrom_filename_strbuf);
|
|
}
|
|
nesrom_filename_strbuf = nullptr;
|
|
|
|
my_malloc_current = nullptr;
|
|
JW_SetLowResoMode(FALSE);
|
|
JW_SetFamicomMode(FALSE);
|
|
return res;
|
|
}
|
|
|
|
static s32 famicom_common_save_initial();
|
|
|
|
static int famicom_rom_load() {
|
|
int nesrom_buffer_size = my_getmemblocksize(famicomCommon.nesromp);
|
|
nesinfo_init();
|
|
OSReport("nesrom_buffer_size=%d\n", nesrom_buffer_size);
|
|
|
|
if (nesrom_buffer_size >= KS_NES_CHRROM_SIZE) {
|
|
bzero(famicomCommon.nesromp, KS_NES_CHRROM_SIZE);
|
|
}
|
|
|
|
if (famicomCommon.nesrom_memcard != FALSE) {
|
|
/* Loading the game image from the memory card. */
|
|
OSReport("ゲームイメージをメモリーカードから読み込みます\n");
|
|
s32 load_res = memcard_game_load(
|
|
famicomCommon.nesromp,
|
|
famicomCommon.rom_no - 1,
|
|
&famicomCommon.nesinfo_tagsp,
|
|
(char*)famicomCommon.mura_save_name,
|
|
(char*)famicomCommon.famicom_save_name,
|
|
&famicomCommon.memcard_game_header,
|
|
famicomCommon.memcard_save_comment, // comment & images
|
|
famicomCommon.unused_save_data_start_ofs,
|
|
famicomCommon.chr_to_i8_bufp,
|
|
CHR_TO_I8_BUF_SIZE
|
|
);
|
|
|
|
if (load_res == CARD_RESULT_READY) {
|
|
/* Loaded the game image from the memory card. */
|
|
OSReport("ゲームイメージをメモリーカードから読み込みました\n");
|
|
nesinfo_tags_start = famicomCommon.nesinfo_tagsp;
|
|
nesinfo_tags_end = nesinfo_tags_start + famicomCommon.memcard_game_header.nestags_size;
|
|
nesinfo_tags_size = nesinfo_tags_end - nesinfo_tags_start;
|
|
nesinfo_data_start = famicomCommon.nesromp;
|
|
nesinfo_data_end = nesinfo_data_start + nesinfo_data_size;
|
|
}
|
|
else {
|
|
/* Failed to load game [%d]. */
|
|
OSReport("ゲーム読み込みに失敗しました [%d]\n", load_res);
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
const char* filename = (char*)nesrom_filename_ptrs[nesrom_idx];
|
|
OSReport("%d:<%s>\n", nesrom_idx, filename);
|
|
bzero(famicomCommon.nesromp, nesrom_buffer_size);
|
|
size_t loaded_size = JKRFileLoader::readGlbResource(famicomCommon.nesromp, nesrom_buffer_size, filename, EXPAND_SWITCH_DECOMPRESS);
|
|
if (loaded_size == 0) {
|
|
return -1;
|
|
}
|
|
|
|
nesinfo_tags_set(famicomCommon.rom_no - 1);
|
|
|
|
nesinfo_data_size = loaded_size;
|
|
nesinfo_data_start = famicomCommon.nesromp;
|
|
nesinfo_data_end = nesinfo_data_start + loaded_size;
|
|
|
|
MemcardGameHeader_t* memcard_game_header = &famicomCommon.memcard_game_header;
|
|
bzero(memcard_game_header, sizeof(MemcardGameHeader_t));
|
|
memcard_game_header->_00 = 'G';
|
|
memcard_game_header->_01 = 0;
|
|
strncpy((char*)memcard_game_header->mori_name, (char*)nesinfo_get_moriName(), MURA_GAME_NAME_SIZE);
|
|
memcard_game_header->nesrom_size = 0;
|
|
memcard_game_header->nestags_size = 0;
|
|
memcard_game_header->flags0.has_comment_img = TRUE;
|
|
memcard_game_header->flags0.no_copy_flag = FALSE;
|
|
memcard_game_header->flags1.no_move_flag = FALSE;
|
|
SetupInternalCommentImage(famicomCommon.memcard_save_comment);
|
|
}
|
|
|
|
u32 max_ofs = 0;
|
|
tcs_bad = false;
|
|
ics_bad = false;
|
|
|
|
nesinfo_tag_process1(nullptr, 0, &max_ofs);
|
|
|
|
if (ics_bad || tcs_bad) {
|
|
if (tcs_bad) {
|
|
// Tag checksum is incorrect.
|
|
OSReport(VT_COL(YELLOW, BLACK) "タグチェックサムが違います\n" VT_RST);
|
|
}
|
|
|
|
if (ics_bad) {
|
|
// Image checksum is incorrect.
|
|
OSReport(VT_COL(YELLOW, BLACK) "イメージチェックサムが違います\n" VT_RST);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (famicomCommon.nesrom_memcard && famicomCommon.save_pl_no >= 0) {
|
|
famicomCommon.save_data_name = (u8*)"GAFEFSE\x1A";
|
|
famicomCommon.save_data_single_size = max_ofs + 0xB;
|
|
famicomCommon.save_data_total_size = famicomCommon.save_data_single_size * PLAYER_NUM + sizeof(FamicomSaveDataHeader);
|
|
famicomCommon.save_data_header = (FamicomSaveDataHeader*)nintendo_hi_0; // overwritten for use with save data
|
|
famicomCommon.internal_save_datap = nintendo_hi_0 + sizeof(FamicomSaveDataHeader) + famicomCommon.save_data_single_size * famicomCommon.save_pl_no;
|
|
bzero(famicomCommon.save_data_header, famicomCommon.save_data_total_size);
|
|
}
|
|
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
famicomCommon.internal_save_datap = famicomCommonSave + sizeof(FamicomSaveDataHeader) + 0x660 * famicomCommon.save_pl_no;
|
|
}
|
|
else if (famicomCommon.save_data_header != nullptr) {
|
|
famicom_common_save_initial();
|
|
}
|
|
|
|
if (famicomCommon.internal_save_datap != nullptr) {
|
|
nesinfo_tag_process1(famicomCommon.internal_save_datap, 1, nullptr);
|
|
}
|
|
|
|
famicomCommon.highscore_flagsp = (u8*)my_malloc(nesinfo_get_highscore_num(), 32);
|
|
highscore_setup_flags(famicomCommon.highscore_flagsp);
|
|
nesinfo_tag_process2();
|
|
|
|
ksNesCommonWorkObj* wp = famicomCommon.wp;
|
|
wp->nesromp = famicomCommon.nesromp;
|
|
u32 flags = 0;
|
|
wp->prg_size = KS_NES_PRGROM_SIZE;
|
|
wp->noise_bufp = famicomCommon.noise_bufp;
|
|
wp->_001D = 4;
|
|
wp->chr_to_u8_bufp = famicomCommon.chr_to_i8_bufp;
|
|
wp->chr_to_i8_buf_size = CHR_TO_I8_BUF_SIZE;
|
|
wp->result_bufp = famicomCommon.result_bufp;
|
|
wp->_0018 = 0;
|
|
|
|
if (famicomCommon.noise_bufp == nullptr) {
|
|
flags |= 0x40; // no sound?
|
|
}
|
|
|
|
int reset_res = ksNesReset(wp, famicomCommon.sp, flags | 0x10, famicomCommon.chrramp, famicomCommon.bbramp);
|
|
|
|
if (reset_res != 0) {
|
|
OSReport("err code=%d (0x%x), %x,%x,%x,%x,%x,%x\n",
|
|
reset_res, reset_res, famicomCommon.wp, famicomCommon.sp,
|
|
famicomCommon.wp->draw_ctx.sprite_scanline_limit, famicomCommon.wp->draw_ctx.ppu_scanline_regs,
|
|
famicomCommon.wp->draw_ctx.post_process_lut, famicomCommon.sp->ppu_chr_banks
|
|
);
|
|
OSReport("NES emu reset failed!!");
|
|
}
|
|
else {
|
|
OSReport("ksNesReset() OK PC=%x, prg_size = 0x%x, chr_size = 0x%x\n",
|
|
famicomCommon.sp->PC, famicomCommon.sp->prg_size, famicomCommon.sp->chr_size);
|
|
nesrom_idx_loaded = nesrom_idx;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void famicom_key_convert() {
|
|
u32 input;
|
|
|
|
for (int i = 0; i < PAD_MAX_CONTROLLERS; i++) {
|
|
|
|
if (InputValid[i]) {
|
|
input = InputData[i];
|
|
}
|
|
else {
|
|
input = 0;
|
|
}
|
|
|
|
if (rapid_mode_a && (input & JUTGamePad::A) && (rapid_count & 0x80000000)) {
|
|
input ^= JUTGamePad::A;
|
|
}
|
|
|
|
if (rapid_mode_b && (input & JUTGamePad::B) && (rapid_count & 0x40000000)) {
|
|
input ^= JUTGamePad::B;
|
|
}
|
|
|
|
if (filer_mode != 0 || filer_demo_mode != FILER_DEMO_MODE_NORMAL) {
|
|
input &= ~(JUTGamePad::A | JUTGamePad::B);
|
|
}
|
|
|
|
input =
|
|
(((input >> 2) & 3) << 26) // DPad up & down
|
|
| (((input >> 0) & 1) << 25) // DPad left (0x02000000)
|
|
| (((input >> 1) & 1) << 24) // DPad right (0x01000000)
|
|
| (((input >> 8) & 1) << 31) // A -> A (0x80000000)
|
|
| (((input >> 9) & 1) << 30) // B -> B (0x40000000)
|
|
| (((input >> 10) & 1) << 29) // X -> Select (0x20000000)
|
|
| (((input >> 4) & 1) << 29) // Z -> Select (0x20000000)
|
|
| (((input >> 11) & 1) << 28) // Y -> Start (0x10000000)
|
|
| (((input >> 12) & 1) << 28) // Start -> Start (0x10000000)
|
|
| (((input >> 24) & 1) ? (1 << 25) : (0 << 25)) // Left analog stick -> Pad Left
|
|
| (((input >> 25) & 1) ? (1 << 24) : (0 << 24)) // Right analog stick -> Pad Right
|
|
| (((input >> 26) & 1) ? (1 << 26) : (0 << 26)) // Down analog stick -> Pad Down
|
|
| (((input >> 27) & 1) ? (1 << 27) : (0 << 27)) // Up analog stick -> Pad Up
|
|
;
|
|
|
|
switch (i) {
|
|
case 0:
|
|
famicomCommon.wp->pads[0] = input;
|
|
break;
|
|
case 1:
|
|
famicomCommon.wp->pads[2] = input;
|
|
break;
|
|
case 2:
|
|
famicomCommon.wp->pads[1] = input;
|
|
break;
|
|
case 3:
|
|
famicomCommon.wp->pads[3] = input;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rapid_count += 0xABCD0000;
|
|
}
|
|
|
|
static void famicom_draw() {
|
|
GXTexObj famicomTexObj;
|
|
|
|
GXSetScissor(0, 0, 640, 480);
|
|
GXSetViewport(0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
|
|
ksNesDrawInit(famicomCommon.wp);
|
|
GXSetNumChans(0);
|
|
GXSetNumTexGens(1);
|
|
GXSetNumTevStages(1);
|
|
GXSetNumIndStages(0);
|
|
GXClearVtxDesc();
|
|
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
|
|
GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
|
|
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0);
|
|
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 9);
|
|
GXInitTexObj(&famicomTexObj, famicomCommon.wp->result_bufp, (u16)KS_NES_WIDTH, (u16)KS_NES_HEIGHT, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
GXInitTexObjLOD(&famicomTexObj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
|
|
GXLoadTexObj(&famicomTexObj, GX_TEXMAP0);
|
|
GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY);
|
|
GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_FALSE, 0, 0);
|
|
GXSetTevDirect(GX_TEVSTAGE0);
|
|
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
|
|
GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
|
|
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
|
|
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
|
|
|
|
int n2; // r0
|
|
int n0; // r4
|
|
int n3; // r3
|
|
int n1; // r6
|
|
|
|
if (filer_mode != 0 || filer_demo_mode != FILER_DEMO_MODE_NORMAL) {
|
|
n2 = 256;
|
|
n0 = KS_NES_HEIGHT;
|
|
n3 = -288;
|
|
n1 = KS_NES_HEIGHT;
|
|
|
|
}
|
|
else {
|
|
n2 = 256;
|
|
n0 = KS_NES_HEIGHT;
|
|
n3 = 30;
|
|
n1 = 6;
|
|
}
|
|
|
|
|
|
// vertex 0
|
|
GXPosition2s16(320 - n3, -8 - n1);
|
|
GXTexCoord2s16(512, 0);
|
|
|
|
// vertex 1
|
|
GXPosition2s16(320 - n3, -8 - n1 - n0);
|
|
GXTexCoord2s16(512, 512);
|
|
|
|
// vertex 2
|
|
GXPosition2s16(320 - n3 - n2, -8 - n1 - n0);
|
|
GXTexCoord2s16(0, 512);
|
|
|
|
// vertex 3
|
|
GXPosition2s16(320 - n3 - n2, -8 - n1);
|
|
GXTexCoord2s16(0, 0);
|
|
|
|
ksNesDrawEnd();
|
|
}
|
|
|
|
static void* ksnes_proc(void* param) {
|
|
u32* flags_p = (u32*)param;
|
|
|
|
ksNesEmuFrame(famicomCommon.wp, famicomCommon.sp, *flags_p);
|
|
return nullptr;
|
|
}
|
|
|
|
static void ksnes_thread_kill(OSAlarm* alarm, OSContext* context) {
|
|
/* Famicom emulator crashed. */
|
|
OSReport(VT_COL(YELLOW, BLACK) "ファミコンエミュ死亡\n" VT_RST);
|
|
OSCancelThread(&ksnes_thread);
|
|
}
|
|
|
|
static int ksnes_thread_exec(u32 flags) {
|
|
int res;
|
|
u8* ksnes_emu_stack = (u8*)my_malloc(KS_NES_EMU_STACK_SIZE, 32);
|
|
|
|
if (ksnes_emu_stack != nullptr) {
|
|
OSTime timeout = OSMillisecondsToTicks(2000); // 2 second timeout
|
|
|
|
if (famicomCommon.sp->motor_timer != 0) {
|
|
// Disk motor is on.
|
|
OSReport("ディスクモーターオン\n");
|
|
}
|
|
|
|
OSTime frame_start_time = OSGetTime();
|
|
s32 prio = OSGetThreadPriority(OSGetCurrentThread());
|
|
OSAlarm alarm;
|
|
|
|
OSCreateThread(&ksnes_thread, &ksnes_proc, &flags, ksnes_emu_stack + KS_NES_EMU_STACK_SIZE, KS_NES_EMU_STACK_SIZE, prio + 1, 0);
|
|
OSCreateAlarm(&alarm);
|
|
OSResumeThread(&ksnes_thread);
|
|
OSSetAlarm(&alarm, timeout, &ksnes_thread_kill);
|
|
OSJoinThread(&ksnes_thread, (void**)&res);
|
|
OSCancelAlarm(&alarm);
|
|
|
|
OSTime frame_end_time = OSGetTime();
|
|
|
|
if ((frame_end_time - frame_start_time) > OSMillisecondsToTicks(16)) {
|
|
OSTime ms = OSTicksToMicroseconds(frame_end_time - frame_start_time); // isn't this supposed to be OSTicksToMilliseconds?
|
|
|
|
// Emulator slow %d ms.
|
|
OSReport(VT_COL(YELLOW, BLACK) "エミュ遅い %d ms\n" VT_RST, ms);
|
|
}
|
|
|
|
my_free(ksnes_emu_stack);
|
|
return res;
|
|
}
|
|
else {
|
|
return -2; // failed to allocate stack
|
|
}
|
|
}
|
|
|
|
#pragma dont_inline on // @HACK - necessary to not inline JUTGamePad::getPortStatus
|
|
static void nogbaInput() {
|
|
int port;
|
|
u32 disconnected_ports = 0;
|
|
|
|
for (port = 0; port < PAD_MAX_CONTROLLERS; port++) {
|
|
InputValid[port] = false;
|
|
|
|
// switch (GetPortStatus(port)) {
|
|
switch ((int)JUTGamePad::getPortStatus((JUTGamePad::EPadPort)port)) {
|
|
case PAD_ERR_NONE:
|
|
{
|
|
InputValid[port] = true;
|
|
|
|
InputData[port] = ((JUTGamePad*)gamePad)[port].mButtons.mButton;
|
|
|
|
if (((JUTGamePad*)gamePad)[port].mButtons.mAnalogL != 0) {
|
|
InputData[port] |= BUTTON_X;
|
|
}
|
|
|
|
if (((JUTGamePad*)gamePad)[port].mButtons.mAnalogR != 0) {
|
|
InputData[port] |= BUTTON_L;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PAD_ERR_NO_CONTROLLER:
|
|
{
|
|
disconnected_ports |= (0x80000000 >> port);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (disconnected_ports != 0) {
|
|
PADReset(disconnected_ports);
|
|
}
|
|
|
|
for (int port = 0; port < PAD_MAX_CONTROLLERS; port++) {
|
|
if (InputValid[port]) {
|
|
InputTrigger[port] = InputData[port] & (InputButton[port] ^ InputData[port]); // Update trigger to only newly pressed buttons
|
|
InputButton[port] = InputData[port]; // update pressed buttons
|
|
|
|
if (InputButton[port] == 0) {
|
|
InputRepeat[port] = 0;
|
|
}
|
|
else if (InputTrigger[port] != 0) {
|
|
InputRepeat[port] = InputButton[port];
|
|
InputRepeatCount[port] = 15;
|
|
}
|
|
else {
|
|
if (InputRepeatCount[port] != 0) {
|
|
InputRepeatCount[port]--;
|
|
InputRepeat[port] = 0;
|
|
}
|
|
else {
|
|
InputRepeatCount[port] = 0;
|
|
InputRepeat[port] = InputButton[port];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
InputButton[port] = 0;
|
|
InputTrigger[port] = 0;
|
|
InputRepeat[port] = 0;
|
|
}
|
|
}
|
|
}
|
|
#pragma dont_inline reset
|
|
|
|
// @HACK - we need to force J2DOrthoGraph's dtor to emit
|
|
static void fake() {
|
|
J2DOrthoGraph graph;
|
|
|
|
graph.~J2DOrthoGraph();
|
|
}
|
|
|
|
extern void famicom_1frame() {
|
|
nogbaInput();
|
|
u32 flags = 0;
|
|
|
|
if (famicomCommon.noise_bufp == nullptr) {
|
|
flags |= 0x1000;
|
|
}
|
|
|
|
famicom_key_convert();
|
|
famicomCommon.wp->_0030 = 0;
|
|
famicomCommon.wp->_0034 = 0;
|
|
famicomCommon.wp->_0038 = 0;
|
|
|
|
/* Special input modes activated when L & R are held */
|
|
/*
|
|
Always - L + R + START = reset console
|
|
Zurumode 1 - L + R + D-Left = Toggle obj 9 'over' mode
|
|
Zurumode 1 - L + R + A = Toggle rapid A mode
|
|
Zurumode 1 - L + R + B = Toggle rapid B mode
|
|
*/
|
|
if ((InputButton[0] & (JUTGamePad::L | JUTGamePad::R)) == (JUTGamePad::L | JUTGamePad::R) &&
|
|
filer_mode == 0 && filer_demo_mode == FILER_DEMO_MODE_NORMAL) {
|
|
if ((InputTrigger[0] & JUTGamePad::START)) {
|
|
if (famicomCommon.internal_save_datap != nullptr) {
|
|
nesinfo_update_highscore(famicomCommon.internal_save_datap, 1);
|
|
}
|
|
|
|
ksNesPushResetButton(famicomCommon.sp);
|
|
}
|
|
|
|
if (APPNMI_ZURUMODE_GET()) {
|
|
if ((InputTrigger[0] & JUTGamePad::DPAD_LEFT)) {
|
|
nines_over_mode ^= TRUE;
|
|
|
|
JUTReport(30, 46, 30, "OBJ 9'S OVER %s", nines_over_mode ? "DISABLED" : "ENABLED");
|
|
}
|
|
|
|
if ((InputTrigger[0] & JUTGamePad::DPAD_RIGHT)) {
|
|
famicomCommon.low_res_mode ^= TRUE;
|
|
}
|
|
|
|
if ((InputTrigger[0] & JUTGamePad::A)) {
|
|
rapid_mode_a ^= TRUE;
|
|
}
|
|
|
|
if ((InputTrigger[0] & JUTGamePad::B)) {
|
|
rapid_mode_b ^= TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nesrom_idx_loaded != -1 &&
|
|
((InputButton[0] & (JUTGamePad::L | JUTGamePad::R)) != (JUTGamePad::L | JUTGamePad::R) || ((InputButton[0] & JUTGamePad::START) == 0 && APPNMI_ZURUMODE_GET() == 0))) {
|
|
u32 frames = 1;
|
|
|
|
if (APPNMI_ZURUMODE_GET() && (InputButton[0] & JUTGamePad::L)) {
|
|
/* When zurumode 1 is enabled, L + C-Stick (Left/Right) adjusts emulation speed */
|
|
JUTGamePad* pad = (JUTGamePad*)&gamePad[0];
|
|
if (pad->mSubStick.mX > 0.1) {
|
|
hispeed_mode = 2.0f + (pad->mSubStick.mX - 0.1f) * 8.9f;
|
|
slow_mode = 0;
|
|
speed_show = 60;
|
|
}
|
|
else if (pad->mSubStick.mX < -0.1) {
|
|
slow_mode = 2.0f + (-pad->mSubStick.mX - 0.1f) * 3.4f;
|
|
hispeed_mode = 0;
|
|
speed_show = 60;
|
|
}
|
|
else {
|
|
slow_mode = 0;
|
|
hispeed_mode = 0;
|
|
speed_show = 0;
|
|
}
|
|
}
|
|
|
|
if (nines_over_mode) {
|
|
flags |= KS_NES_FLAG_NINES_OVER_MODE;
|
|
}
|
|
|
|
|
|
if (slow_mode != 0) {
|
|
frames = 1;
|
|
if (speed_show != 0) {
|
|
JUTReport(32, 48, 1, "1/%d SPEED", slow_mode);
|
|
}
|
|
}
|
|
|
|
|
|
if (hispeed_mode != 0) {
|
|
frames = hispeed_mode;
|
|
if (speed_show != 0) {
|
|
JUTReport(32, 48, 1, "%2dX SPEED", hispeed_mode);
|
|
}
|
|
}
|
|
|
|
if (speed_show != 0) {
|
|
speed_show--;
|
|
}
|
|
|
|
famicomCommon.wp->_0014 = 0;
|
|
famicomCommon.wp->frames = frames;
|
|
|
|
do {
|
|
if (slow_mode != 0) {
|
|
slow_mode_sub++;
|
|
if (slow_mode_sub < slow_mode) {
|
|
for (int j = 0; j < 262; j++) {
|
|
Sound_Write(0, 0, j * 114);
|
|
}
|
|
break;
|
|
}
|
|
|
|
slow_mode_sub = 0;
|
|
}
|
|
|
|
ksnes_thread_exec(flags);
|
|
} while (--frames > 0);
|
|
}
|
|
else {
|
|
for (int j = 0; j < 262; j++) {
|
|
Sound_Write(0, 0, j * 114);
|
|
}
|
|
}
|
|
|
|
GXSetScissor(0, 0, 640, 480);
|
|
GXSetViewport(0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
|
|
ksNesDraw(famicomCommon.wp, famicomCommon.sp);
|
|
|
|
if (filer_mode != 0 || filer_demo_mode != FILER_DEMO_MODE_NORMAL) {
|
|
JW_SetLowResoMode(FALSE);
|
|
process_filer_mode();
|
|
}
|
|
else {
|
|
JW_SetLowResoMode(famicomCommon.low_res_mode);
|
|
}
|
|
|
|
JW_SetFamicomMode(TRUE);
|
|
|
|
famicom_draw();
|
|
|
|
/* If filer mode is enabled, controller 4 L press toggles process profiling bar */
|
|
if (filer_mode_enable && (((JUTGamePad*)gamePad)[3].mButtons.mTrigger & JUTGamePad::L)) {
|
|
JUTProcBar::getManager()->setVisible(JUTProcBar::getManager()->isVisible() ? FALSE : TRUE);
|
|
}
|
|
|
|
if (filer_demo_mode != FILER_DEMO_MODE_NORMAL) {
|
|
filer_demo_count++;
|
|
|
|
if (filer_demo_count > 1200) {
|
|
filer_demo_count = 0;
|
|
nesrom_idx = nesrom_idx < (nesrom_count - 1) ? (nesrom_idx + 1) : 0;
|
|
}
|
|
|
|
if ((InputTrigger[0] & (
|
|
JUTGamePad::MAINSTICK_LEFT |
|
|
JUTGamePad::MAINSTICK_RIGHT |
|
|
JUTGamePad::MAINSTICK_DOWN |
|
|
JUTGamePad::MAINSTICK_UP |
|
|
JUTGamePad::DPAD_LEFT |
|
|
JUTGamePad::DPAD_RIGHT |
|
|
JUTGamePad::DPAD_DOWN |
|
|
JUTGamePad::DPAD_UP |
|
|
JUTGamePad::L |
|
|
JUTGamePad::R |
|
|
JUTGamePad::A |
|
|
JUTGamePad::X |
|
|
JUTGamePad::Y
|
|
))) {
|
|
filer_demo_mode = FILER_DEMO_MODE_NORMAL;
|
|
filer_mode = 1;
|
|
}
|
|
}
|
|
|
|
if (filer_mode_enable != FALSE && (InputTrigger[0] & JUTGamePad::R)) {
|
|
filer_mode ^= 1;
|
|
filer_demo_mode = FILER_DEMO_MODE_NORMAL;
|
|
}
|
|
|
|
if ((flags & 0x400) != 0 && famicomCommon.sp->motor_timer != 0 && (famicomCommon.sp->vblank_frame_counter & 0x10)) {
|
|
// Please wait
|
|
JUTReport(400, 350, 1, "omati kudasai...");
|
|
}
|
|
|
|
if (famicomCommon.internal_save_datap != nullptr) {
|
|
nesinfo_update_highscore(famicomCommon.internal_save_datap, 0);
|
|
}
|
|
}
|
|
|
|
extern int famicom_rom_load_check() {
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
while (*nesrom_filename_ptrs[nesrom_idx] == ':') {
|
|
nesrom_idx++;
|
|
if (nesrom_idx >= nesrom_count) {
|
|
if (nesrom_idx_loaded < 0) {
|
|
OSPanic(__FILE__, 6072, "no nesfile specified");
|
|
continue;
|
|
}
|
|
|
|
nesrom_idx = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nesrom_idx_loaded != nesrom_idx) {
|
|
if (famicom_rom_load() == 0) {
|
|
nesrom_idx_loaded = nesrom_idx;
|
|
}
|
|
else {
|
|
nesrom_idx_loaded = -1;
|
|
}
|
|
}
|
|
|
|
return nesrom_idx_loaded;
|
|
}
|
|
|
|
static int famicom_cardResultToMiyake(s32 cardResult) {
|
|
switch (cardResult) {
|
|
case CARD_RESULT_READY:
|
|
return FAMICOM_RESULT_OK;
|
|
case CARD_RESULT_INSSPACE:
|
|
return FAMICOM_RESULT_NOSPACE;
|
|
case CARD_RESULT_NOENT:
|
|
return FAMICOM_RESULT_NOENTRY;
|
|
case CARD_RESULT_BROKEN:
|
|
return FAMICOM_RESULT_BROKEN;
|
|
case CARD_RESULT_WRONGDEVICE:
|
|
return FAMICOM_RESULT_WRONGDEVICE;
|
|
case CARD_RESULT_ENCODING:
|
|
return FAMICOM_RESULT_WRONGENCODING;
|
|
case CARD_RESULT_NOCARD:
|
|
return FAMICOM_RESULT_NOCARD;
|
|
case CARD_RESULT_NOFILE:
|
|
return FAMICOM_RESULT_NOFILE;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
extern int famicom_internal_data_load() {
|
|
famicom_mount_archive_wait();
|
|
bzero(&famicomCommon, sizeof(FamicomCommon));
|
|
famicomCommon.nesrom_memcard = FALSE;
|
|
strncpy((char*)famicomCommon.mura_save_name, "DobutsunomoriP_MURA", 32);
|
|
strncpy((char*)famicomCommon.famicom_save_name, "DobutsunomoriP_F_SAVE", 32);
|
|
famicomCommon.save_data_name = (u8*)"GAFEFSC\x1A";
|
|
famicomCommon.save_data_single_size = 0x660;
|
|
famicomCommon.save_data_total_size = sizeof(FamicomSaveDataHeader) + 0x660 * PLAYER_NUM;
|
|
famicomCommon.save_data_header = (FamicomSaveDataHeader*)famicomCommonSave;
|
|
famicomCommon.internal_save_datap = nullptr;
|
|
|
|
s32 cardResult = famicom_common_save_initial();
|
|
s32 chan;
|
|
if (cardResult == CARD_RESULT_NOFILE) {
|
|
chan = famicom_getSaveChan(famicomCommon.nesrom_memcard, &cardResult);
|
|
if (chan >= 0) {
|
|
s32 freeBytes;
|
|
s32 freeEntries;
|
|
|
|
cardResult = memcard_getFreeBlocks(chan, &freeBytes, &freeEntries);
|
|
if (cardResult == CARD_RESULT_READY) {
|
|
if (freeEntries < 1) {
|
|
cardResult = CARD_RESULT_NOENT;
|
|
}
|
|
else if (freeBytes < 0x2000) {
|
|
cardResult = CARD_RESULT_INSSPACE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* @BUG - chan can be an invalid value due to being allocted on the stack & cardResult not being CARD_RESULT_NOFILE */
|
|
if (cardResult != CARD_RESULT_READY && cardResult != CARD_RESULT_FATAL_ERROR && chan >= 0) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
return famicom_cardResultToMiyake(cardResult);
|
|
}
|
|
|
|
extern int famicom_internal_data_save() {
|
|
s32 result;
|
|
s32 chan;
|
|
|
|
famicom_mount_archive_wait();
|
|
bzero(&famicomCommon, sizeof(FamicomCommon));
|
|
famicomCommon.nesrom_memcard = FALSE;
|
|
strncpy((char*)famicomCommon.mura_save_name, "DobutsunomoriP_MURA", 32);
|
|
strncpy((char*)famicomCommon.famicom_save_name, "DobutsunomoriP_F_SAVE", 32);
|
|
famicomCommon.save_data_name = (u8*)"GAFEFSC\x1A";
|
|
famicomCommon.save_data_single_size = 0x660;
|
|
famicomCommon.save_data_total_size = sizeof(FamicomSaveDataHeader) + 0x660 * PLAYER_NUM;
|
|
famicomCommon.save_data_header = (FamicomSaveDataHeader*)famicomCommonSave;
|
|
famicomCommon.internal_save_datap = nullptr;
|
|
|
|
chan = famicom_getSaveChan(FALSE, &result);
|
|
if (chan >= 0) {
|
|
famicomCommon.unused_save_data_start_ofs = 0x640;
|
|
famicomCommon.memcard_save_comment = commentImageBuffer;
|
|
famicomCommon.memcard_game_header.flags0.no_copy_flag = FALSE;
|
|
famicomCommon.memcard_game_header.flags1.no_move_flag = FALSE;
|
|
SetupInternalCommentImage(commentImageBuffer);
|
|
|
|
if (famicom_save_data_check(famicomCommon.save_data_header, famicomCommon.save_pl_no, famicomCommon.save_data_name) == 0) {
|
|
int no_save = famicomCommon.save_data_header->no_save;
|
|
famicomCommon.save_data_header->no_save = FALSE;
|
|
|
|
famicom_save_data_setup(famicomCommon.save_data_header, famicomCommon.save_data_single_size, famicomCommon.save_data_name);
|
|
memcard_data_load(chan, (char*)famicomCommon.famicom_save_name, (char*)famicomCommon.mura_save_name, nullptr, famicomCommon.save_data_total_size, famicomCommon.save_data_name);
|
|
|
|
result = memcard_data_save(
|
|
chan,
|
|
(char*)famicomCommon.famicom_save_name,
|
|
(char*)famicomCommon.mura_save_name,
|
|
famicomCommon.save_data_header,
|
|
famicomCommon.save_data_total_size,
|
|
famicomCommon.memcard_save_comment,
|
|
famicomCommon.memcard_game_header.flags1.banner_fmt,
|
|
famicomCommon.memcard_game_header.flags0.comment_type == MEMCARD_COMMENT_TYPE_NONE ? 0xFFFFFFFF : CARD_COMMENT_SIZE,
|
|
famicomCommon.memcard_game_header.icon_format,
|
|
famicomCommon.memcard_game_header.icon_flags,
|
|
famicomCommon.memcard_game_header.flags0.comment_type == MEMCARD_COMMENT_TYPE_NONE ? 0xFFFFFFFF : 0,
|
|
no_save
|
|
);
|
|
}
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_FATAL_ERROR && chan >= 0) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
return famicom_cardResultToMiyake(result);
|
|
}
|
|
|
|
extern int famicom_external_data_save() {
|
|
s32 result;
|
|
s32 chan;
|
|
|
|
famicom_mount_archive_wait();
|
|
if (famicomCommon.nesrom_memcard == FALSE) {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
}
|
|
else if (famicomCommon.save_data_header == nullptr) {
|
|
result = CARD_RESULT_NOCARD;
|
|
chan = 2;
|
|
}
|
|
else {
|
|
chan = famicom_getSaveChan(TRUE, &result);
|
|
if (chan >= 0) {
|
|
if (famicom_save_data_check((FamicomSaveDataHeader*)famicomCommon.save_data_header, -1, famicomCommon.save_data_name) == 0) {
|
|
if (famicomCommon.nesrom_memcard && famicomCommon.memcard_game_header.flags0.has_comment_img) {
|
|
int no_save = ((FamicomSaveDataHeader*)famicomCommon.save_data_header)->no_save;
|
|
((FamicomSaveDataHeader*)famicomCommon.save_data_header)->no_save = FALSE;
|
|
|
|
famicom_save_data_setup((FamicomSaveDataHeader*)famicomCommon.save_data_header, famicomCommon.save_data_single_size, famicomCommon.save_data_name);
|
|
memcard_data_load(chan, (char*)famicomCommon.famicom_save_name, (char*)famicomCommon.mura_save_name, nullptr, famicomCommon.save_data_total_size, famicomCommon.save_data_name);
|
|
|
|
result = memcard_data_save(
|
|
chan,
|
|
(char*)famicomCommon.famicom_save_name,
|
|
(char*)famicomCommon.mura_save_name,
|
|
(FamicomSaveDataHeader*)famicomCommon.save_data_header,
|
|
famicomCommon.save_data_total_size,
|
|
famicomCommon.memcard_save_comment,
|
|
famicomCommon.memcard_game_header.flags1.banner_fmt,
|
|
famicomCommon.memcard_game_header.flags0.comment_type == MEMCARD_COMMENT_TYPE_NONE ? 0xFFFFFFFF : CARD_COMMENT_SIZE,
|
|
famicomCommon.memcard_game_header.icon_format,
|
|
famicomCommon.memcard_game_header.icon_flags,
|
|
famicomCommon.memcard_game_header.flags0.comment_type == MEMCARD_COMMENT_TYPE_NONE ? 0xFFFFFFFF : 0,
|
|
no_save
|
|
);
|
|
}
|
|
else {
|
|
result = CARD_RESULT_READY;
|
|
}
|
|
}
|
|
else {
|
|
result = CARD_RESULT_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != CARD_RESULT_READY && result != CARD_RESULT_FATAL_ERROR && chan >= 0) {
|
|
errorChan = chan;
|
|
}
|
|
|
|
return famicom_cardResultToMiyake(result);
|
|
}
|
|
|
|
extern int famicom_external_data_save_check() {
|
|
return FALSE;
|
|
}
|
|
|
|
static s32 famicom_common_save_initial() {
|
|
s32 result;
|
|
s32 chan = famicom_getSaveChan(famicomCommon.nesrom_memcard, &result);
|
|
|
|
if (chan >= 0) {
|
|
// Loading Famicom save data.
|
|
OSReport("ファミコンセーブデータを読み込みます\n");
|
|
result = memcard_data_load(chan, (char*)famicomCommon.famicom_save_name, (char*)famicomCommon.mura_save_name, (FamicomSaveDataHeader*)famicomCommon.save_data_header, famicomCommon.save_data_total_size, famicomCommon.save_data_name);
|
|
|
|
if (famicom_save_data_check((FamicomSaveDataHeader*)famicomCommon.save_data_header, -1, famicomCommon.save_data_name) != 0) {
|
|
famicom_save_data_init((FamicomSaveDataHeader*)famicomCommon.save_data_header, famicomCommon.save_data_single_size, famicomCommon.save_data_name);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
extern int famicom_get_disksystem_titles(int* n_games, char* title_name_bufp, int namebuf_size) {
|
|
famicom_mount_archive_wait();
|
|
return memcard_game_list(n_games, title_name_bufp, namebuf_size) == CARD_RESULT_READY;
|
|
}
|