From d08076381b25d02cbb3901c9b5678a2d84c2e3ff Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Mon, 29 Jan 2024 20:31:47 -0500 Subject: [PATCH] Implement & link famicom.cpp --- common.py | 4 +- config/dol_slices.yml | 5 + config/symbols.yml | 2 + configure.py | 5 +- include/Famicom/famicom.h | 129 +- include/Famicom/famicomInternal.h | 15 + include/Famicom/ks_nes_common.h | 63 +- include/Famicom/ks_nes_core.h | 12 +- include/Famicom/ks_nes_draw.h | 12 +- include/JSystem/J2D/J2DGrafContext.h | 134 + include/JSystem/JGeometry.h | 368 ++- include/JSystem/JKernel/JKRDecomp.h | 1 + include/JSystem/JKernel/JKRFileFinder.h | 4 +- include/JSystem/JUtility/JUTAssertion.h | 2 +- include/JSystem/JUtility/JUTDbPrint.h | 2 +- include/JSystem/JUtility/JUTGamePad.h | 6 +- include/JSystem/JUtility/JUTProcBar.h | 156 + include/JSystem/ResTIMG.h | 56 + include/MSL_C/printf.h | 8 + include/dolphin/card.h | 178 ++ include/dolphin/gx/GXEnum.h | 1 + include/dolphin/gx/GXVert.h | 6 + include/dolphin/mtx.h | 73 +- include/dolphin/pad.h | 5 + include/dolphin/string.h | 1 + include/famicom_emu.h | 3 +- include/jaudio_NES/emusound.h | 18 + include/libc64/sprintf.h | 10 +- include/libjsys/jsyswrapper.h | 7 + include/m_card.h | 2 +- include/m_controller.h | 18 - include/types.h | 18 + src/famicom_emu.c | 12 +- src/m_play.c | 4 +- src/static/Famicom/famicom.cpp | 2801 ++++++++++++++++++ src/static/Famicom/famicom_nesinfo.cpp | 6 +- src/static/JSystem/JKernel/JKRFileFinder.cpp | 6 +- src/static/jsyswrap.cpp | 10 +- src/static/libc64/sprintf.c | 4 +- 39 files changed, 4048 insertions(+), 119 deletions(-) create mode 100644 include/Famicom/famicomInternal.h create mode 100644 include/JSystem/J2D/J2DGrafContext.h create mode 100644 include/JSystem/JUtility/JUTProcBar.h create mode 100644 include/JSystem/ResTIMG.h create mode 100644 include/dolphin/card.h create mode 100644 include/jaudio_NES/emusound.h create mode 100644 src/static/Famicom/famicom.cpp diff --git a/common.py b/common.py index 7562bb0e..1596983c 100644 --- a/common.py +++ b/common.py @@ -434,7 +434,9 @@ JSYSTEM_JGADGET_BASE = [ FAMICOM_BASE = CFLAGS + [ "-lang=c++", "-sdata 0", - "-sdata2 0" + "-sdata2 0", + "-enum int", + "-sym on" ] + DOL_DEFINES JSYSTEM_CFLAGS = ' '.join(JSYSTEM_BASE + LOCAL_CFLAGS) diff --git a/config/dol_slices.yml b/config/dol_slices.yml index 332cce53..69e7025c 100644 --- a/config/dol_slices.yml +++ b/config/dol_slices.yml @@ -48,6 +48,11 @@ jaudio_NES/dummyprobe.c: #jaudio_NES/verysimple.c: # .text: [0x80008400, 0x80008480] # .sdata: [0x80217b80, 0x80217b88] +Famicom/famicom.cpp: + .text: [0x80041614, 0x80046888] # TODO: get ~J2DOrthoGraph's dtor in here somehow? 0x800468fc, also add in JUTGamePad::getPortStatus when JUTGamePad is linked? + .rodata: [0x800aa9a8, 0x800aaa30] + .data: [0x800d8778, 0x800d99a0] + .bss: [0x801ef540, 0x801f6bd8] Famicom/famicom_nesinfo.cpp: .text: [0x800468fc, 0x80047e40] .rodata: [0x800aaa30, 0x800aab60] diff --git a/config/symbols.yml b/config/symbols.yml index 7b8ead3b..a9cf3013 100644 --- a/config/symbols.yml +++ b/config/symbols.yml @@ -2634,6 +2634,7 @@ global: 0x8009ae84: _restfpr_28 0x8009ae88: _restfpr_29 0x8009ae98: __save_gpr + 0x8009ae98: _savegpr_14 0x8009ae9c: _savegpr_15 0x8009aea0: _savegpr_16 0x8009aea4: _savegpr_17 @@ -2650,6 +2651,7 @@ global: 0x8009aed0: _savegpr_28 0x8009aed4: _savegpr_29 0x8009aee4: __restore_gpr + 0x8009aee4: _restgpr_14 0x8009aee8: _restgpr_15 0x8009aeec: _restgpr_16 0x8009aef0: _restgpr_17 diff --git a/configure.py b/configure.py index f491cd1e..3aaf98fb 100644 --- a/configure.py +++ b/configure.py @@ -637,7 +637,10 @@ class CSource(Source): self.cc = c.CC elif path.startswith("src/static/Famicom/"): self.cflags = c.FAMICOM_CLFAGS - self.cc = c.CC + if "ksNes" in path: # ksNes doesn't have rodata pooling off + self.cc = c.CC + else: + self.cc = c.CC_R elif path.startswith("src/static/GBA2/"): self.cflags = c.DOL_CFLAGS_SDATA0_CFLAGS self.cc = c.CC diff --git a/include/Famicom/famicom.h b/include/Famicom/famicom.h index 692b1e6c..34340e29 100644 --- a/include/Famicom/famicom.h +++ b/include/Famicom/famicom.h @@ -9,6 +9,7 @@ extern "C" { #endif +#define FAMICOM_SAVE_HEADER_SIZE 0x40 #define FAMICOM_INTERNAL_ROM_NUM 19 #define NESTAG_CMD_SIZE 3 @@ -44,8 +45,8 @@ extern "C" { typedef void* (*MALLOC_ALIGN_FUNC)(size_t size, u32 align); typedef void (*MALLOC_FREE_FUNC)(void* ptr); -typedef size_t (*MALLOC_GETMEMBLOCKSIZE_FUNC)(void* ptr); -typedef size_t (*MALLOC_GETTOTALFREESIZE_FUNC)(); +typedef int (*MALLOC_GETMEMBLOCKSIZE_FUNC)(void* ptr); +typedef int (*MALLOC_GETTOTALFREESIZE_FUNC)(); typedef struct malloc_s { MALLOC_ALIGN_FUNC malloc_align; @@ -54,11 +55,59 @@ typedef struct malloc_s { MALLOC_GETTOTALFREESIZE_FUNC gettotalfreesize; } Famicom_MallocInfo; -typedef struct save_data_header_s { - u8 _temp[0x19C0]; +enum filer_demo_mode { + FILER_DEMO_MODE_NORMAL, + FILER_DEMO_MODE_AUTO, + + FILER_DEMO_MODE_NUM +}; + +#define FAMICOM_SAVE_DATA_NAME_LEN 8 + +typedef struct FamicomSaveDataHeader { + u8 name[FAMICOM_SAVE_DATA_NAME_LEN]; + u8 _08; + u8 _09; + u8 headerSize; + u8 checksum; + u16 size; + u8 no_save; + u8 _temp[FAMICOM_SAVE_HEADER_SIZE - 0x000F]; } FamicomSaveDataHeader; -typedef struct memcard_game_header_s { +enum { + MEMCARD_COMMENT_TYPE_NONE, + MEMCARD_COMMENT_TYPE_DEFAULT, + MEMCARD_COMMENT_TYPE_COPY_ROM, // converts rom's comment but converts "] ROM" to "] SAVE" + MEMCARD_COMMENT_TYPE_COPY_EMBEDDED // uses the embedded and unique 'save' comment +}; + +enum { + MEMCARD_BANNER_TYPE_NONE, + MEMCARD_BANNER_TYPE_DEFAULT, + MEMCARD_BANNER_TYPE_COPY_ROM, // copies the NES rom save's banner + MEMCARD_BANNER_TYPE_COPY_EMBEDDED // uses the embedded and unique 'save' banner +}; + +enum { + MEMCARD_ICON_TYPE_NONE, + MEMCARD_ICON_TYPE_DEFAULT, + MEMCARD_ICON_TYPE_COPY_ROM, // copies the NES rom save's icon + MEMCARD_ICON_TYPE_COPY_EMBEDDED // uses the embedded and unique 'save' icon +}; + +enum { + FAMICOM_RESULT_OK, + FAMICOM_RESULT_NOSPACE, + FAMICOM_RESULT_NOENTRY, + FAMICOM_RESULT_BROKEN, + FAMICOM_RESULT_WRONGDEVICE, + FAMICOM_RESULT_WRONGENCODING, + FAMICOM_RESULT_NOCARD, + FAMICOM_RESULT_NOFILE +}; + +typedef struct MemcardGameHeader_t { u8 _00; u8 _01; u8 mori_name[16]; @@ -66,14 +115,24 @@ typedef struct memcard_game_header_s { u16 nestags_size; u16 icon_format; u16 icon_flags; - u16 banner_size; - u8 flags0; - u8 flags1; + u16 comment_img_size; /* Size of comment + banner + icon */ + struct { + u8 has_comment_img:1; + u8 comment_type:2; + u8 banner_type:2; + u8 icon_type:2; + u8 no_copy_flag:1; + } flags0; + struct { + u8 no_move_flag:1; + u8 banner_fmt:2; + u8 reserved:5; + } flags1; u16 pad; } MemcardGameHeader_t; /* sizeof (FamicomCommon) == 0xB8 */ -typedef struct famicom_common_s { +typedef struct FamicomCommon { /* 0x00 */ ksNesCommonWorkObj* wp; /* 0x04 */ ksNesStateObj* sp; /* 0x08 */ u8* nesromp; @@ -85,27 +144,28 @@ typedef struct famicom_common_s { /* 0x20 */ u8* highscore_flagsp; /* 0x24 */ u8* nesinfo_tagsp; /* 0x28 */ int _28; - /* 0x2C */ u8 player_no; - /* 0x2D */ u8 _2d; - /* 0x2E */ u8 _2e; + /* 0x2C */ u8 nesrom_memcard; // TRUE: rom is loaded from memcard, FALSE: rom is loaded internally + /* 0x2D */ u8 rom_no; + /* 0x2E */ s8 save_pl_no; /* 0x2F */ u8 mura_save_name[33]; /* 0x50 */ u8 famicom_save_name[33]; /* 0x74 */ FamicomSaveDataHeader* save_data_header; - /* 0x78 */ int _78; - /* 0x7C */ size_t save_data_header_size; - /* 0x80 */ size_t _80; + /* 0x78 */ u8* internal_save_datap; + /* 0x7C */ size_t save_data_total_size; + /* 0x80 */ size_t save_data_single_size; /* 0x84 */ int _84; - /* 0x88 */ u8* _88; - /* 0x8C */ u16 _8C; + /* 0x88 */ u8* save_data_name; + /* 0x8C */ u8 _8C; + /* 0x8D */ u8 low_res_mode; /* 0x8E */ MemcardGameHeader_t memcard_game_header; /* 0xB0 */ u8* memcard_save_comment; - /* 0xB4 */ int _b4; + /* 0xB4 */ size_t unused_save_data_start_ofs; } FamicomCommon; -extern void* my_malloc_current; +extern Famicom_MallocInfo* my_malloc_current; extern u8 save_game_image; -extern char** nesrom_filename_ptrs; +extern u8** nesrom_filename_ptrs; extern u32 nesinfo_tags_size; extern u8* nesinfo_tags_start; @@ -115,16 +175,33 @@ extern u32 nesinfo_data_size; extern u8* nesinfo_data_start; extern u8* nesinfo_data_end; +extern u8 InputValid[]; +extern u32 InputData[]; +extern u32 InputButton[]; +extern u32 InputTrigger[]; +extern u32 InputRepeat[]; +extern u32 InputRepeatCount[]; + +extern u8 tcs_bad; +extern u8 ics_bad; + extern FamicomCommon famicomCommon; -typedef u8 (*FAMICOM_GETSAVECHAN_PROC)(int* player_no, int* slot_card_result); -extern void famicom_setCallback_getSaveChan(FAMICOM_GETSAVECHAN_PROC getSaveChan_proc); -extern int famicom_mount_archive(); +typedef s32 (*FAMICOM_GETSAVECHAN_PROC)(int* player_no, s32* slot_card_result); + +extern int famicom_getErrorChan(); +extern void famicom_setCallback_getSaveChan(FAMICOM_GETSAVECHAN_PROC proc); extern int famicom_mount_archive_end_check(); -extern int famicom_rom_load_check(); -extern int famicom_1frame(); -extern int famicom_init(int, void*, u8); +extern void famicom_mount_archive(); +extern int famicom_init(int rom_idx, Famicom_MallocInfo* malloc_info, int player_no); extern int famicom_cleanup(); +extern void famicom_1frame(); +extern int famicom_rom_load_check(); +extern int famicom_internal_data_load(); +extern int famicom_internal_data_save(); +extern int famicom_external_data_save(); +extern int famicom_external_data_save_check(); +extern int famicom_get_disksystem_titles(int* n_games, char* title_name_bufp, int namebuf_size); extern void nesinfo_tags_set(int rom_no); extern void nesinfo_tag_process1(u8* save_data, int mode, u32* max_ofs_p); diff --git a/include/Famicom/famicomInternal.h b/include/Famicom/famicomInternal.h new file mode 100644 index 00000000..97ab6ba7 --- /dev/null +++ b/include/Famicom/famicomInternal.h @@ -0,0 +1,15 @@ +#ifndef FAMICOMINTERNAL_H +#define FAMICOMINTERNAL_H + +#include "types.h" +#include "Famicom/famicom.h" +#include "Famicom/ks_nes.h" + +#define MURA_GAME_NAME_SIZE 16 + +static void* my_malloc(size_t size, u32 align) { return (*my_malloc_current->malloc_align)(size, align); } +static void my_free(void* ptr) { (*my_malloc_current->free)(ptr); } +static int my_getmemblocksize(void* ptr) { return (*my_malloc_current->getmemblocksize)(ptr); } +static int my_gettotalfreesize() { return (*my_malloc_current->gettotalfreesize)(); } + +#endif diff --git a/include/Famicom/ks_nes_common.h b/include/Famicom/ks_nes_common.h index 6642239f..6f292c20 100644 --- a/include/Famicom/ks_nes_common.h +++ b/include/Famicom/ks_nes_common.h @@ -7,28 +7,71 @@ extern "C" { #endif +#define KS_NES_WIDTH 256 +#define KS_NES_HEIGHT 228 + #define CHR_TO_I8_BUF_SIZE 0x100000 #define KS_NES_NESFILE_HEADER_SIZE 0x10 -#define KS_NES_PRGROM_SIZE 0x8000 // not certain, generally 32kb (0x8000) -#define KS_NES_CHRROM_SIZE 0x4000 // not certain, usually only 8kb (0x2000) +#define KS_NES_PRGROM_SIZE 0x80000 // 512kb for MMC3 +#define KS_NES_CHRROM_SIZE 0x40000 // 256kb for MMC3 #define KS_NES_CHRRAM_SIZE 0x2000 // 8kb #define KS_NES_BBRAM_SIZE 0x8000 // 32kb, battery backed-up ram #define KS_NES_NOISE_DATA_SIZE 0x7F000 -#define KS_NES_DRAW_RESULT_SIZE 0x1C800 +#define KS_NES_DRAW_RESULT_BUF_SIZE 0x1C800 -#define KS_NES_SAVE_DATA_HEADER_SIZE +#define KS_NES_WRAM_SIZE 0x800 // 2kb + +#define KS_NES_EMU_STACK_SIZE 0x1000 // 4kb for thread stack #define KS_NES_BYTES_PER_KB (1024) -#define KS_NES_TO_KB(b) ((f32)b / (1.0f / (f32)KS_NES_BYTES_PER_KB)) +#define KS_NES_TO_KB(b) ((f32)b * (1.0f / (f32)KS_NES_BYTES_PER_KB)) -typedef struct ks_nes_common_work_obj_s { - u8 _temp[0x8F78]; +typedef struct ksNesCommonWorkPriv { + /* 0x0000 */ u8 wram[KS_NES_WRAM_SIZE]; + /* 0x0800 */ u8 _0800[0x0B40 - 0x0800]; + /* 0x0B40 */ u8 _0B40[1]; // TODO: figure out what this is and its size + /* 0x0B41 */ u8 _0B41[0x2A40 - 0x0B41]; + /* 0x2A40 */ u8 _2A40[0x800]; + /* 0x3240 */ u8 _3240[0x8F18 - 0x3240]; +} ksNesCommonWorkPriv; + +typedef struct ksNesCommonWorkObj { + /* 0x0000 */ u8* nesromp; + /* 0x0004 */ u8* noise_bufp; + /* 0x0008 */ size_t chr_to_i8_buf_size; + /* 0x000C */ u8* chr_to_u8_bufp; + /* 0x0010 */ u8* result_bufp; + /* 0x0014 */ int _0014; + /* 0x0018 */ int _0018; + /* 0x001C */ u8 frames; + /* 0x001D */ u8 _001D; + /* 0x001E */ u8 _001E; + /* 0x001F */ u8 _001F; + /* 0x0020 */ u32 pads[4]; + /* 0x0030 */ u32 _0030; + /* 0x0034 */ u32 _0034; + /* 0x0038 */ u32 _0038; + /* 0x003C */ u8 _003C[0x0048 - 0x003C]; + /* 0x0048 */ size_t prg_size; + /* 0x004C */ u8 _004C[0x0060 - 0x004C]; + /* 0x0060 */ ksNesCommonWorkPriv work_priv; } ksNesCommonWorkObj; -typedef struct ks_nes_state_obj_s { - /* 0x0000 */ u8 wram[2048]; - /* 0x0800 */ u8 _temp[0x1A78 - 0x800]; +typedef struct ksNesStateObj { + /* 0x0000 */ u8 wram[KS_NES_WRAM_SIZE]; + /* 0x0800 */ u8 _0800[0x16B4 - 0x0800]; + /* 0x16B4 */ u32 _16B4; + /* 0x16BC */ u8 _16B8[0x176D - 0x16B8]; + /* 0x176D */ u8 disk_motor; + /* 0x176E */ u8 _176E[0x17FC - 0x176E]; + /* 0x17FC */ u8 _17FC[8]; + /* 0x1804 */ u8 _1804[0x1844 - 0x1804]; + /* 0x1844 */ u16 PC; + /* 0x1846 */ u8 _1846[0x1860 - 0x1846]; + /* 0x1860 */ size_t prg_size; + /* 0x1864 */ size_t chr_size; + /* 0x1868 */ u8 _1868[0x1A78 - 0x1868]; } ksNesStateObj; #ifdef __cplusplus diff --git a/include/Famicom/ks_nes_core.h b/include/Famicom/ks_nes_core.h index 3f6c0899..9d820136 100644 --- a/include/Famicom/ks_nes_core.h +++ b/include/Famicom/ks_nes_core.h @@ -4,14 +4,8 @@ #include "types.h" #include "Famicom/ks_nes_common.h" -#ifdef __cplusplus -extern "C" { -#endif - - - -#ifdef __cplusplus -} -#endif +extern int ksNesReset(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 flags, u8* chrramp, u8* bbramp); +extern void ksNesEmuFrame(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 flags); +extern void ksNesPushResetButton(ksNesStateObj* sp); #endif diff --git a/include/Famicom/ks_nes_draw.h b/include/Famicom/ks_nes_draw.h index 79c27e85..6938dd1b 100644 --- a/include/Famicom/ks_nes_draw.h +++ b/include/Famicom/ks_nes_draw.h @@ -4,14 +4,8 @@ #include "types.h" #include "Famicom/ks_nes_common.h" -#ifdef __cplusplus -extern "C" { -#endif - - - -#ifdef __cplusplus -} -#endif +extern void ksNesDrawInit(ksNesCommonWorkObj* wp); +extern void ksNesDraw(ksNesCommonWorkObj* wp, ksNesStateObj* sp); +extern void ksNesDrawEnd(); #endif diff --git a/include/JSystem/J2D/J2DGrafContext.h b/include/JSystem/J2D/J2DGrafContext.h new file mode 100644 index 00000000..490cf3d5 --- /dev/null +++ b/include/JSystem/J2D/J2DGrafContext.h @@ -0,0 +1,134 @@ +#ifndef _JSYSTEM_J2D_J2DGRAFCONTEXT_H +#define _JSYSTEM_J2D_J2DGRAFCONTEXT_H + +#include "types.h" +#include "JSystem/JGeometry.h" +#include "JSystem/J2D/J2DTypes.h" +#include "JSystem/JUtility/TColor.h" +#include "dolphin/mtx.h" + +/** + * @fabricated + */ +enum J2DGrafType { + J2DGraf_Base = 0, + J2DGraf_Ortho = 1, + J2DGraf_Persp = 2, +}; + +struct J2DGrafBlend { + u8 mType; // _00 + u8 mSrcFactor; // _01 + u8 mDestFactor; // _02 +}; + +struct J2DGrafContext { + J2DGrafContext(f32, f32, f32, f32); + + virtual ~J2DGrafContext() { } // _08 (weak) + virtual void place(const JGeometry::TBox2f&); // _0C + virtual void place(f32 x, f32 y, f32 width, f32 height) + { + JGeometry::TBox2f box(x, y, x + width, y + height); + place(box); + } // _10 (weak) + virtual void setPort(); // _14 + virtual void setup2D(); // _18 + virtual void setScissor(); // _1C + virtual J2DGrafType getGrafType() const { return J2DGraf_Base; } // _20 (weak) + virtual void setLookat() { } // _24 (weak) + + void drawFrame(const JGeometry::TBox2f&); + void fillBox(const JGeometry::TBox2f&); + void lineTo(JGeometry::TVec2f); + + void lineTo(f32 x, f32 y) { lineTo(JGeometry::TVec2f(x, y)); } + void moveTo(f32 x, f32 y) { moveTo(JGeometry::TVec2f(x, y)); } + + void moveTo(JGeometry::TVec2f pos) { mPrevPos = pos; } + + void scissor(const JGeometry::TBox2f&); + void setColor(JUtility::TColor c) { setColor(c, c, c, c); } + void setColor(JUtility::TColor, JUtility::TColor, JUtility::TColor, JUtility::TColor); + void setLineWidth(u8); + + // inlined + void line(JGeometry::TVec2f, JGeometry::TVec2f); + + // _00 VTBL + JGeometry::TBox2f mBounds; // _04 + JGeometry::TBox2f mScissorBounds; // _14 + JUtility::TColor mColorTL; // _24, top left + JUtility::TColor mColorTR; // _28, top right + JUtility::TColor mColorBR; // _2C, bottom right + JUtility::TColor mColorBL; // _30, bottom left + u8 mLineWidth; // _34 + JGeometry::TVec2f mPrevPos; // _38 + Mtx44 mMtx44; // _40 + GC_Mtx mPosMtx; // _80 + J2DGrafBlend _B0; // _B0 + J2DGrafBlend mLinePart; // _B3 + J2DGrafBlend mBoxPart; // _B6 +}; + +struct J2DPerspGraph : public J2DGrafContext { + J2DPerspGraph(); + + virtual ~J2DPerspGraph() { } // _08 (weak) + virtual void setPort(); // _14 + virtual J2DGrafType getGrafType() const { return J2DGraf_Persp; } // _20 (weak) + virtual void setLookat(); // _24 + + void makeLookat(); + void set(f32, f32, f32); + void setFovy(f32); + + inline f32 getFovY() const { return mFovY; } + + // _00 = VTBL + // _00-_BC = J2DGrafContext + f32 mFovY; // _BC + f32 _C0; // _C0 + f32 _C4; // _C4 + f32 _C8; // _C8 +}; + +struct J2DOrthoGraph : public J2DGrafContext { + J2DOrthoGraph(); + J2DOrthoGraph(f32, f32, f32, f32, f32, f32); + + virtual ~J2DOrthoGraph() {}; // _08 (weak) + virtual void setPort(); // _14 + virtual J2DGrafType getGrafType() const { return J2DGraf_Ortho; }; // _20 (weak) + virtual void setLookat(); // _24 + + void setOrtho(JGeometry::TBox2f const&, f32, f32); + void scissorBounds(JGeometry::TBox2f*, JGeometry::TBox2f const*); + + f32 getWidthPower() const { return mBounds.getWidth() / mOrtho.getWidth(); } + f32 getHeightPower() const { return mBounds.getHeight() / mOrtho.getHeight(); } + + void setOrtho(f32 param_0, f32 param_1, f32 param_2, f32 param_3, f32 param_4, f32 param_5) + { + JGeometry::TBox2 ortho(param_0, param_1, param_0 + param_2, param_1 + param_3); + setOrtho(ortho, param_4, param_5); + } + + // _00 = VTBL + // _00-_BC = J2DGrafContext + JGeometry::TBox2f mOrtho; // _BC + f32 mNear; // _CC + f32 mFar; // _D0 +}; + +void J2DFillBox(f32 param_0, f32 param_1, f32 param_2, f32 param_3, JUtility::TColor color); +void J2DFillBox(JGeometry::TBox2f const& param_0, JUtility::TColor param_1); +void J2DFillBox(f32, f32, f32, f32, JUtility::TColor, JUtility::TColor, JUtility::TColor, JUtility::TColor); +void J2DFillBox(const JGeometry::TBox2f&, JUtility::TColor, JUtility::TColor, JUtility::TColor, JUtility::TColor); + +void J2DDrawFrame(f32 param_0, f32 param_1, f32 param_2, f32 param_3, JUtility::TColor param_4, u8 param_5); +void J2DDrawFrame(JGeometry::TBox2f const& param_0, JUtility::TColor param_1, u8 param_2); + +void J2DDrawLine(f32, f32, f32, f32, JUtility::TColor, int); + +#endif \ No newline at end of file diff --git a/include/JSystem/JGeometry.h b/include/JSystem/JGeometry.h index 6afd86fd..3965f69e 100644 --- a/include/JSystem/JGeometry.h +++ b/include/JSystem/JGeometry.h @@ -1,9 +1,363 @@ -#ifndef JGEOMETRY_H -#define JGEOMETRY_H +#ifndef _JSYSTEM_JGEOMETRY_H +#define _JSYSTEM_JGEOMETRY_H -#include "JSystem/JGeometry/Vec.h" -#include "JSystem/JGeometry/Box.h" -#include "JSystem/JGeometry/Util.h" -#endif +#include "types.h" -#endif +inline f32 fsqrt_step(f32 mag) +{ + f32 root = __frsqrte(mag); + return 0.5f * root * (3.0f - mag * (root * root)); +} + +namespace JGeometry { +template +struct TVec2 { + TVec2() { } + TVec2(T v) { set(v); } + TVec2(T x, T y) { set(x, y); } + + void set(T v) { y = x = v; } + + void set(T x, T y) + { + this->x = x; + this->y = y; + } + + void set(const TVec2& other) + { + x = other.x; + y = other.y; + } + + void setMin(const TVec2& min) + { + if (x >= min.x) + x = min.x; + if (y >= min.y) + y = min.y; + } + + void setMax(const TVec2& max) + { + if (x <= max.x) + x = max.x; + if (y <= max.y) + y = max.y; + } + + void add(const TVec2& other) + { + x += other.x; + y += other.y; + } + + /** @fabricated */ + // TVec2 adding(const TVec2& other) { return TVec2(x + other.x, y + other.y); } + + /** @fabricated */ + TVec2 adding(T xDelta, T yDelta) { return TVec2(x + xDelta, y + yDelta); } + + /** @fabricated */ + void add(T xDelta, T yDelta) + { + x += xDelta; + y += yDelta; + } + + TVec2& operator+=(const TVec2& other) + { + x += other.x; + y += other.y; + return *this; + } + + TVec2& operator*=(const TVec2& other) + { + x *= other.x; + y *= other.y; + return *this; + } + + bool isAbove(const TVec2& other) const { return (x >= other.x) && (y >= other.y) ? true : false; } + + T x; + T y; +}; + +template +struct TVec3 { + // inline TVec3() { } + // inline TVec3(T value) + // : x(value) + // , y(value) + // , z(value) + // { + // } + // inline TVec3(T inX, T inY, T inZ) + // : x(inX) + // , y(inY) + // , z(inZ) {}; + + // // TODO: Determine if this could've actually existed, or if I'm just making it up. + // inline TVec3(const TVec3& other) + // { + // x = other.x; + // y = other.y; + // z = other.z; + // } + + // TODO: Determine if this could've actually existed, or if I'm just making + // it up. + inline TVec3& operator=(const TVec3& other) + { + x = other.x; + y = other.y; + z = other.z; + return *this; + } + + void set(T x, T y, T z) + { + this->x = x; + this->y = y; + this->z = z; + } + + void set(const TVec3& other) + { + x = other.x; + y = other.y; + z = other.z; + } + + void setMin(const TVec3& min) + { + if (x >= min.x) + x = min.x; + if (y >= min.y) + y = min.y; + if (z >= min.z) + z = min.z; + } + + void setMax(const TVec3& max) + { + if (x <= max.x) + x = max.x; + if (y <= max.y) + y = max.y; + if (z >= max.z) + z = max.z; + } + + // inline operator Vec() const { return *this; } + inline operator Vec() const + { + Vec other; + other.x = x; + other.y = y; + other.z = z; + return other; + } + + // inline TVec3(Vec& vec) + // { + // x = vec.x; + // y = vec.y; + // z = vec.z; + // } + + void zero() { x = y = z = 0.0f; } + + f32 squared() const { return x * x + y * y + z * z; } + + void normalize() + { + f32 sq = squared(); + if (sq <= FLT_EPSILON * 32.0f) { + return; + } + f32 norm; + if (sq <= 0.0f) { + norm = sq; + } else { + norm = fsqrt_step(sq); + } + x *= norm; + y *= norm; + z *= norm; + } + + void normalize(const TVec3& other) + { + f32 sq = other.squared(); + if (sq <= FLT_EPSILON * 32.0f) { + zero(); + return; + } + f32 norm; + if (sq <= 0.0f) { + norm = sq; + } else { + norm = fsqrt_step(sq); + } + x = other.x * norm; + y = other.y * norm; + z = other.z * norm; + } + + bool isAbove(const TVec3& other) const { return (x >= other.x) && (y >= other.y) && (z >= other.z); } + + T x; + T y; + T z; +}; + +// Size: 0x10 +template +struct TBox { + TBox() + : i() + , f() + { + } + TBox(const TBox& other) + : i(other.f) + , f(other.y) + { + } + + T i, f; +}; + +// clang-format off +template<> struct TBox > { + inline f32 getWidth() const { return f.x - i.x; } + inline f32 getHeight() const { return f.y - i.y; } + + bool isValid() const { return f.isAbove(i); } + + void addPos(f32 x, f32 y) { + addPos(TVec2(x, y)); + } + + void addPos(const TVec2& pos) { + i.add(pos); + f.add(pos); + } + + bool intersect(const TBox >& other) { + i.setMax(other.i); + f.setMin(other.f); + return isValid(); + } + + TVec2 i, f; +}; + +template +struct TBox2 : TBox > { + TBox2() {} + // TBox2(const TBox2& other) { set(other); } + TBox2(const TVec2& i, const TVec2 f) { set(i, f); } + // TBox2(const TVec2& i, T x1, T y1) { set(i, x1, y1); } + // TBox2(T x0, T y0, const TVec2& f) { set(x0, y0, f); } + TBox2(f32 x0, f32 y0, f32 x1, f32 y1) { set(x0, y0, x1, y1); } + TBox2(f32 x0, f32 y0, TVec2& f) { set(x0, y0, x0 + f.x, y0 + f.y); } + TBox2(f32 val) + { + f.y = val; + f.x = val; + i.y = val; + i.x = val; + } + + // inline TBox2& operator=(const TBox2& other) + // { + // set(other); + // return *this; + // } + + void absolute() { + if (!this->isValid()) { + TBox2 box(*this); + this->i.setMin(box.i); + this->i.setMin(box.f); + this->f.setMax(box.i); + this->f.setMax(box.f); + } + } + + inline void setX(const T& x) { + this->f.x = this->i.x = x; + } + + inline void setY(const T& y) { + this->f.y = this->i.y = y; + } + + // /** @fabricated */ + // TBox2& addingPos(TBox2& result, const TVec2& pos) { + // return TBox2(i.adding(pos), f.adding(pos)); + // } + + void set(const TBox2& other) { set(other.i, other.f); } + void set(const TVec2& i, const TVec2& f) { this->i.set(i), this->f.set(f); } + // void set(const TVec2& i, T x1, T y1) { this->i.set(i), this->f.set(x1, y1); } + // void set(T x0, T y0, const TVec2& f) { this->i.set(x0, y0), this->f.set(f); } + void set(T x0, T y0, T x1, T y1) { this->i.set(x0, y0); this->f.set(x1, y1); } + + // void setOrigin(const TVec2& other) { this->i.set(other); } + // void setSize(T width, T height) { this->f.x = this->i.x + width; this->f.y = this->i.y + height; } +}; +// clang-format on + +template +struct TBox3 { + // TBox3() {} + // TBox2(const TBox2& other) { set(other); } + // TBox3(const TVec3& i, const TVec3 f) { set(i, f); } + // // TBox2(const TVec2& i, T x1, T y1) { set(i, x1, y1); } + // // TBox2(T x0, T y0, const TVec2& f) { set(x0, y0, f); } + // TBox3(T x0, T y0, T z0, T x1, T y1, T z1) { set(x0, y0, z0, x1, y1, z1); } + // TBox3(T x0, T y0, TVec3& f) { set(x0, y0, z0, x0 + f.x, y0 + f.y, z0 + f.z); } + // TBox3(T val) + // { + // f.x = f.y = f.z = val; + // i.x = i.y = i.z = val; + // } + + inline bool isValid() { return mMax.isAbove(mMin); } + + void absolute() + { + if (!this->isValid()) { + TBox3 box(*this); + this->mMin.setMin(box.mMin); + this->mMin.setMin(box.mMax); + this->mMax.setMax(box.mMin); + this->mMax.setMax(box.mMax); + } + } + + void set(const TBox3& other) { set(other.mMin, other.mMax); } + void set(const TVec3& i, const TVec3& f) { this->mMin.set(i), this->mMax.set(f); } + void set(T x0, T y0, T z0, T x1, T y1, T z1) + { + this->mMin.set(x0, y0); + this->mMax.set(x1, y1); + } + + TVec3 mMin; // _00 + TVec3 mMax; // _0C +}; + +typedef TVec2 TVec2f; +typedef TVec3 TVec3f; +typedef TBox2 TBox2f; +typedef TBox3 TBox3f; + +} // namespace JGeometry + +#endif \ No newline at end of file diff --git a/include/JSystem/JKernel/JKRDecomp.h b/include/JSystem/JKernel/JKRDecomp.h index fa792bde..e35c908b 100644 --- a/include/JSystem/JKernel/JKRDecomp.h +++ b/include/JSystem/JKernel/JKRDecomp.h @@ -6,6 +6,7 @@ #include "JSystem/JKernel/JKRDvdFile.h" #include "JSystem/JKernel/JKRThread.h" #include "JSystem/JKernel/JKRAram.h" +#include "JSystem/JKernel/JKREnum.h" #define JKRDECOMP_MSG_BUF_COUNT 4 #define JKRDECOMP_STACK_SIZE 0x4000 diff --git a/include/JSystem/JKernel/JKRFileFinder.h b/include/JSystem/JKernel/JKRFileFinder.h index f0bcfdca..736fa25b 100644 --- a/include/JSystem/JKernel/JKRFileFinder.h +++ b/include/JSystem/JKernel/JKRFileFinder.h @@ -19,7 +19,7 @@ class JKRFileFinder { public: JKRFileFinder() - : mIsAvailable(false), mIsFileOrDir(false) + : mIsAvailable(false), mIsDir(false) { } @@ -36,7 +36,7 @@ public: // _00 = VTBL bool mIsAvailable; // _10 - bool mIsFileOrDir; // _11 + bool mIsDir; // _11 }; class JKRArcFinder : public JKRFileFinder diff --git a/include/JSystem/JUtility/JUTAssertion.h b/include/JSystem/JUtility/JUTAssertion.h index a96af3f3..041769c4 100644 --- a/include/JSystem/JUtility/JUTAssertion.h +++ b/include/JSystem/JUtility/JUTAssertion.h @@ -46,7 +46,7 @@ namespace JUTAssertion #define JUT_MAX_ASSERT(...) #define JUT_LOG_F(...) -#ifndef DEBUG +#if defined(DEBUG) || 1 #define JUT_ASSERT(...) #define JUT_ASSERT_F(...) #else diff --git a/include/JSystem/JUtility/JUTDbPrint.h b/include/JSystem/JUtility/JUTDbPrint.h index a6f0e610..d1a14116 100644 --- a/include/JSystem/JUtility/JUTDbPrint.h +++ b/include/JSystem/JUtility/JUTDbPrint.h @@ -4,6 +4,6 @@ void* JC_JUTDbPrint_getManager(void); void JC_JUTDbPrint_setVisible(void*, int); // I know these are C++ but these were used to match a c function so I'll fix these when I need them or fix zurumode update. - +void JUTReport(int x, int y, int show_count, const char* fmt, ...); #endif \ No newline at end of file diff --git a/include/JSystem/JUtility/JUTGamePad.h b/include/JSystem/JUtility/JUTGamePad.h index 8299425a..5c37dc2b 100644 --- a/include/JSystem/JUtility/JUTGamePad.h +++ b/include/JSystem/JUtility/JUTGamePad.h @@ -126,10 +126,12 @@ public: return JUTGamePad::sClampMode; } - static s8 getPortStatus(EPadPort port) { + /* @HACK - This gets inlined when defined -- JSystem might have precompiled headers */ + static s8 getPortStatus(EPadPort port); + /*{ JUT_ASSERT(0 <= port && port < 4); return mPadStatus[port].err; - } + }*/ bool isPushing3ButtonReset() const { bool pushing = false; diff --git a/include/JSystem/JUtility/JUTProcBar.h b/include/JSystem/JUtility/JUTProcBar.h new file mode 100644 index 00000000..291d2dd8 --- /dev/null +++ b/include/JSystem/JUtility/JUTProcBar.h @@ -0,0 +1,156 @@ +#ifndef _JUTPROCBAR_H +#define _JUTPROCBAR_H + +#include "dolphin/os.h" +#include "dolphin/os/OSTime.h" +#include "JSystem/JUtility/TColor.h" +#include "JSystem/JKernel/JKRHeap.h" + +#include "types.h" + +class JUTProcBar +{ +public: + struct CTime + { + CTime() { clear(); } + + void clear() + { + mCost = 0; + _08 = 0; + _0C = 0; + } + + void start(u8 red, u8 green, u8 blue) + { + mR = red; + mG = green; + mB = blue; + mStartTick = OSGetTick(); + } + + void end() + { + mCost = OSTicksToMicroseconds(OSDiffTick(OSGetTick(), mStartTick)); + if (mCost == 0) + mCost = 1; + } + + void accumePeek() + { + //u32 prev = ++_0C; + if (++_0C >= 0x10 || mCost >= _08) + { + _08 = mCost; + _0C = 0; + } + } + + int calcBarSize(int p1, int p2) { return mCost * p1 / p2; } + + u32 mStartTick; // _00 + u32 mCost; // _04 + u32 _08; // _08 + u32 _0C; // _0C + u8 mR; // _10 + u8 mG; // _11 + u8 mB; // _12 + }; + + struct CParamSet { + void setBarWidth(int w) { mBarWidth = w; }; + void setWidth(int w) { mWidth = w; } + void setUserPosition(int pos) { mUserPosition = pos; } + void setPosition(int x, int y) + { + mPosX = x; + mPosY = y; + } + + /* 0x00 */ int mBarWidth; + /* 0x04 */ int mPosX; + /* 0x08 */ int mPosY; + /* 0x0C */ int mWidth; + /* 0x10 */ int mUserPosition; + }; + + JUTProcBar(); // unused / inlined + ~JUTProcBar(); // unused / inlined + + static JUTProcBar* create(); + static void destroy(); + static void clear(); + + void draw(); + void drawProcessBar(); + void drawHeapBar(); + + // Unused Functions / Inlines + void bar_subroutine(int, int, int, int, int, int, int, JUtility::TColor, JUtility::TColor); + void adjustMeterLength(u32, f32 *, f32, f32, int *); + void getUnuseUserBar(); + + u32 getGpCost() const { + return mGp.mCost; + } + + u32 getCpuCost() const { + return mCpu.mCost; + } + + u32 getUserCost(int idx) { + return sManager->mUsers[idx].mCost; + } + + static JUTProcBar* getManager() { + return sManager; + } + + void idleStart() { mIdle.start(255, 129, 30); } + void idleEnd() { mIdle.end(); } + void gpStart() { mGp.start(255, 129, 30); } + void gpEnd() { mGp.end(); } + void cpuStart() { mCpu.start(255, 129, 30); } + void cpuEnd() { mCpu.end(); } + void gpWaitStart() { mGpWait.start(255, 129, 30); } + void gpWaitEnd() { mGpWait.end(); } + void wholeLoopStart() { mWholeLoop.start(255, 129, 30); } + void wholeLoopEnd() { mWholeLoop.end(); } + + void setCostFrame(int frame) { mCostFrame = frame; } + void setVisible(bool visible) { mVisible = visible; } + bool isVisible() { return mVisible; } + void setHeapBarVisible(bool visible) { mHeapBarVisible = visible; } + void userStart(int idx, u8 p2, u8 p3, u8 p4) { + sManager->mUsers[idx].start(p2, p3, p4); + sManager->_108 |= 1 << idx; + } + + inline u32 calcGPUTime() { // fabricated + return mGp.mCost - mGpWait.mCost; + } + + int calcBarHeight() { // fabricated + return mParams.mBarWidth * 2; + } + + static JUTProcBar* sManager; // might be private too +private: + CTime mIdle; // _00 + CTime mGp; // _14 + CTime mCpu; // _28 + CTime mGpWait; // _3C + CTime mWholeLoop; // _50 + CTime mUsers[8]; // _64 + int mCostFrame; // _104 + u32 _108; // _108, active users? + bool mVisible; // _10C + int _110; // _110 + CParamSet mParams; // _114 + int _128; // _128 + JKRHeap* mWatchHeap; // _12C + bool mHeapBarVisible; // _130 +}; // 0x134 size + +#endif diff --git a/include/JSystem/ResTIMG.h b/include/JSystem/ResTIMG.h new file mode 100644 index 00000000..a070bec5 --- /dev/null +++ b/include/JSystem/ResTIMG.h @@ -0,0 +1,56 @@ +#ifndef _JSYSTEM_RESTIMG_H +#define _JSYSTEM_RESTIMG_H + +#include "dolphin/gx.h" +#include "types.h" + +#ifndef _JUTTransparency +typedef u8 _JUTTransparency; +#endif + +enum { + ResTIMG_FORMAT_C8 = 9 + // TODO: others +}; + +enum { + ResTIMG_NO_PALETTE, + ResTIMG_PALETTE +}; + +struct ResTIMG { + inline BOOL isMIPmapEnabled() const { return (mIsMIPmapEnabled > 0); } + + inline u16 getWidth() const { return mSizeX; } + inline u16 getHeight() const { return mSizeY; } + + u8 mTextureFormat; // _00 + _JUTTransparency mTransparency; // _01 + u16 mSizeX; // _02 + u16 mSizeY; // _04 + u8 mWrapS; // _06 + u8 mWrapT; // _07 + u8 mPaletteFormat; // _08 + u8 mColorFormat; // _09 + u16 mPaletteEntryCount; // _0A + u32 mPaletteOffset; // _0C + GXBool mIsMIPmapEnabled; // _10 + GXBool mDoEdgeLOD; // _11 + GXBool mIsBiasClamp; // _12 + GXBool mIsMaxAnisotropy; // _13 + u8 mMinFilterType; // _14 + u8 mMagFilterType; // _15 + s8 mMinLOD; // _16 + s8 mMaxLOD; // _17 + u8 mTotalImageCount; // _18 + u8 _19; // _19, unknown + s16 mLODBias; // _1A + u32 mImageDataOffset; // _1C +}; + +struct ResTIMGPair { + ResTIMG _00; + ResTIMG _20; +}; + +#endif \ No newline at end of file diff --git a/include/MSL_C/printf.h b/include/MSL_C/printf.h index 13d5a41b..185d82af 100644 --- a/include/MSL_C/printf.h +++ b/include/MSL_C/printf.h @@ -4,8 +4,16 @@ #include "types.h" #include "va_args.h" +#ifdef __cplusplus +extern "C" { +#endif extern void vprintf(const char*, va_list); extern void printf(const char*, ...); +int snprintf(char* s, size_t n, const char* format, ...); + +#ifdef __cplusplus +} +#endif #endif diff --git a/include/dolphin/card.h b/include/dolphin/card.h new file mode 100644 index 00000000..0108223a --- /dev/null +++ b/include/dolphin/card.h @@ -0,0 +1,178 @@ +#ifndef _DOLPHIN_CARD +#define _DOLPHIN_CARD + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif +#define CARD_ENCODE_ANSI 0u +#define CARD_ENCODE_SJIS 1u + +/* Sizes */ +#define CARD_WORKAREA_SIZE (5 * 8 * 1024) +#define CARD_READ_SIZE 512 +#define CARD_MAX_FILE 127 +#define CARD_COMMENT_SIZE 64 +#define CARD_FILENAME_MAX 32 +#define CARD_ICON_MAX 8 +#define CARD_ICON_WIDTH 32 +#define CARD_ICON_HEIGHT 32 +#define CARD_BANNER_WIDTH 96 +#define CARD_BANNER_HEIGHT 32 + +/* Icon animation */ +#define CARD_MODE_NORMAL 0 +#define CARD_MODE_FAST 1 + +#define CARDGetBannerFormat(stat) (((stat)->bannerFormat) & CARD_STAT_BANNER_MASK) +#define CARDGetIconAnim(stat) (((stat)->bannerFormat) & CARD_STAT_ANIM_MASK) +#define CARDGetIconFormat(stat, n) (((stat)->iconFormat >> (2 * (n))) & CARD_STAT_ICON_MASK) +#define CARDGetIconSpeed(stat, n) (((stat)->iconSpeed >> (2 * (n))) & CARD_STAT_SPEED_MASK) +#define CARDSetBannerFormat(stat, f) \ + ((stat)->bannerFormat = (u8)(((stat)->bannerFormat & ~CARD_STAT_BANNER_MASK) | (f))) +#define CARDSetIconAnim(stat, f) \ + ((stat)->bannerFormat = (u8)(((stat)->bannerFormat & ~CARD_STAT_ANIM_MASK) | (f))) +#define CARDSetIconFormat(stat, n, f) \ + ((stat)->iconFormat = \ + (u16)(((stat)->iconFormat & ~(CARD_STAT_ICON_MASK << (2 * (n)))) | ((f) << (2 * (n))))) +#define CARDSetIconSpeed(stat, n, f) \ + ((stat)->iconSpeed = \ + (u16)(((stat)->iconSpeed & ~(CARD_STAT_SPEED_MASK << (2 * (n)))) | ((f) << (2 * (n))))) +#define CARDSetIconAddress(stat, addr) ((stat)->iconAddr = (u32)(addr)) +#define CARDSetCommentAddress(stat, addr) ((stat)->commentAddr = (u32)(addr)) +#define CARDGetFileNo(fileInfo) ((fileInfo)->fileNo) + +#define CARD_NUM_CHANS 2 + +#define CARD_RESULT_UNLOCKED 1 +#define CARD_RESULT_READY 0 +#define CARD_RESULT_BUSY -1 +#define CARD_RESULT_WRONGDEVICE -2 +#define CARD_RESULT_NOCARD -3 +#define CARD_RESULT_NOFILE -4 +#define CARD_RESULT_IOERROR -5 +#define CARD_RESULT_BROKEN -6 +#define CARD_RESULT_EXIST -7 +#define CARD_RESULT_NOENT -8 +#define CARD_RESULT_INSSPACE -9 +#define CARD_RESULT_NOPERM -10 +#define CARD_RESULT_LIMIT -11 +#define CARD_RESULT_NAMETOOLONG -12 +#define CARD_RESULT_ENCODING -13 +#define CARD_RESULT_CANCELED -14 +#define CARD_RESULT_FATAL_ERROR -128 + +#define CARD_STAT_ICON_NONE 0 +#define CARD_STAT_ICON_C8 1 +#define CARD_STAT_ICON_RGB5A3 2 +#define CARD_STAT_ICON_MASK 3 + +#define CARD_STAT_BANNER_NONE 0 +#define CARD_STAT_BANNER_C8 1 +#define CARD_STAT_BANNER_RGB5A3 2 +#define CARD_STAT_BANNER_MASK 3 + +#define CARD_STAT_ANIM_LOOP 0x00 +#define CARD_STAT_ANIM_BOUNCE 0x04 +#define CARD_STAT_ANIM_MASK 0x04 + +#define CARD_STAT_SPEED_END 0 +#define CARD_STAT_SPEED_FAST 1 +#define CARD_STAT_SPEED_MIDDLE 2 +#define CARD_STAT_SPEED_SLOW 3 +#define CARD_STAT_SPEED_MASK 3 + +#define CARD_ATTR_PUBLIC 0x04u +#define CARD_ATTR_NO_COPY 0x08u +#define CARD_ATTR_NO_MOVE 0x10u +#define CARD_ATTR_GLOBAL 0x20u +#define CARD_ATTR_COMPANY 0x40u + +typedef struct CARDFileInfo { + s32 chan; + s32 fileNo; + + s32 offset; + s32 length; + u16 iBlock; + u16 __padding; +} CARDFileInfo; + +typedef struct CARDStat { + char fileName[CARD_FILENAME_MAX]; + u32 length; + u32 time; // seconds since 01/01/2000 midnight + u8 gameName[4]; + u8 company[2]; + + u8 bannerFormat; + u8 __padding; + u32 iconAddr; // offset to the banner, bannerTlut, icon, iconTlut data set. + u16 iconFormat; + u16 iconSpeed; + u32 commentAddr; // offset to the pair of 32 byte character strings. + + u32 offsetBanner; + u32 offsetBannerTlut; + u32 offsetIcon[CARD_ICON_MAX]; + u32 offsetIconTlut; + u32 offsetData; +} CARDStat; + +typedef void (*CARDCallback)(s32 chan, s32 result); + +void CARDInit(void); +BOOL CARDGetFastMode(void); +BOOL CARDSetFastMode(BOOL enable); + +s32 CARDCheck(s32 chan); +s32 CARDCheckAsync(s32 chan, CARDCallback callback); +s32 CARDCheckEx(s32 chan, s32* xferBytes); +s32 CARDCheckExAsync(s32 chan, s32* xferBytes, CARDCallback callback); +s32 CARDCreate(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo); +s32 CARDCreateAsync(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo, + CARDCallback callback); +s32 CARDDelete(s32 chan, const char* fileName); +s32 CARDDeleteAsync(s32 chan, const char* fileName, CARDCallback callback); +s32 CARDFastDelete(s32 chan, s32 fileNo); +s32 CARDFastDeleteAsync(s32 chan, s32 fileNo, CARDCallback callback); +s32 CARDFastOpen(s32 chan, s32 fileNo, CARDFileInfo* fileInfo); +s32 CARDFormat(s32 chan); +s32 CARDFormatAsync(s32 chan, CARDCallback callback); +s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed); +s32 CARDGetAttributes(s32 chan, s32 fileNo, u8* attr); +s32 CARDGetEncoding(s32 chan, u16* encode); +s32 CARDGetMemSize(s32 chan, u16* size); +s32 CARDGetResultCode(s32 chan); +s32 CARDGetSectorSize(s32 chan, u32* size); +s32 CARDGetSerialNo(s32 chan, u64* serialNo); +s32 CARDGetStatus(s32 chan, s32 fileNo, CARDStat* stat); +s32 CARDGetXferredBytes(s32 chan); +s32 CARDMount(s32 chan, void* workArea, CARDCallback detachCallback); +s32 CARDMountAsync(s32 chan, void* workArea, CARDCallback detachCallback, + CARDCallback attachCallback); +s32 CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo); +BOOL CARDProbe(s32 chan); +s32 CARDProbeEx(s32 chan, s32* memSize, s32* sectorSize); +s32 CARDRename(s32 chan, const char* oldName, const char* newName); +s32 CARDRenameAsync(s32 chan, const char* oldName, const char* newName, CARDCallback callback); +s32 CARDSetAttributesAsync(s32 chan, s32 fileNo, u8 attr, CARDCallback callback); +s32 CARDSetAttributes(s32 chan, s32 fileNo, u8 attr); +s32 CARDSetStatus(s32 chan, s32 fileNo, CARDStat* stat); +s32 CARDSetStatusAsync(s32 chan, s32 fileNo, CARDStat* stat, CARDCallback callback); +s32 CARDUnmount(s32 chan); +s32 CARDGetCurrentMode(s32 chan, u32* mode); +s32 CARDCancel(CARDFileInfo* fileInfo); +s32 CARDClose(CARDFileInfo* fileInfo); +s32 CARDRead(CARDFileInfo* fileInfo, void* addr, s32 length, s32 offset); +s32 CARDReadAsync(CARDFileInfo* fileInfo, void* addr, s32 length, s32 offset, + CARDCallback callback); +s32 CARDWrite(CARDFileInfo* fileInfo, const void* addr, s32 length, s32 offset); +s32 CARDWriteAsync(CARDFileInfo* fileInfo, const void* addr, s32 length, s32 offset, + CARDCallback callback); + +#ifdef __cplusplus +} +#endif +#endif // _DOLPHIN_CARD diff --git a/include/dolphin/gx/GXEnum.h b/include/dolphin/gx/GXEnum.h index 0ac650d0..4348223d 100644 --- a/include/dolphin/gx/GXEnum.h +++ b/include/dolphin/gx/GXEnum.h @@ -179,6 +179,7 @@ typedef enum _GXCITexFmt { GX_TF_C14X2 = 0xa, } GXCITexFmt; +// TODO: fix typedef enum _GXTexWrapMode { GX_CLAMP, GX_REPEAT, diff --git a/include/dolphin/gx/GXVert.h b/include/dolphin/gx/GXVert.h index 6d616c46..07b8f567 100644 --- a/include/dolphin/gx/GXVert.h +++ b/include/dolphin/gx/GXVert.h @@ -120,6 +120,12 @@ static inline void GXTexCoord2s16(s16 u, s16 v) { GXWGFifo.s16 = v; } +static inline void GXPosition2s16(s16 x, s16 y) +{ + GXWGFifo.s16 = x; + GXWGFifo.s16 = y; +} + static inline void GXPosition2u16(u16 x, u16 y) { GXWGFifo.u16 = x; diff --git a/include/dolphin/mtx.h b/include/dolphin/mtx.h index 228bb306..30f8c18a 100644 --- a/include/dolphin/mtx.h +++ b/include/dolphin/mtx.h @@ -7,8 +7,8 @@ extern "C" { #endif -typedef float Mtx[3][4]; -typedef float Mtx44[4][4]; +/////////////// TYPE DEFINES /////////////// +#define MTXDegToRad(a) ((a)*0.01745329252f) typedef struct { f32 x; @@ -16,11 +16,70 @@ typedef struct { f32 z; } Vec; -void PSMTXConcat(const Mtx, const Mtx, Mtx); -void PSMTXCopy(const Mtx, Mtx); -void PSMTXIdentity(Mtx); -void PSMTXTranspose(const Mtx, Mtx); -u32 PSMTXInverse(const Mtx, Mtx); +typedef f32 Mtx34[3][4]; +typedef f32 Mtx23[2][3]; +typedef f32 Mtx33[3][3]; +typedef f32 Mtx44[4][4]; +typedef f32 (*MtxP)[4]; +typedef f32 PSQuaternion[4]; +typedef Mtx34 GC_Mtx; // TODO: fix this + +typedef struct Quaternion { + f32 x, y, z, w; +} Quaternion; + +//////////////////////////////////////////// + +////// PAIRED SINGLE MATRIX FUNCTIONS ////// +void PSMTXIdentity(GC_Mtx mtx); +void PSMTXCopy(const GC_Mtx src, GC_Mtx dest); +void PSMTXConcat(const GC_Mtx A, const GC_Mtx B, GC_Mtx concat); + +void PSMTXTranspose(const GC_Mtx src, GC_Mtx xPose); +u32 PSMTXInverse(const GC_Mtx src, GC_Mtx inv); + +void __PSMTXRotAxisRadInternal(GC_Mtx mtx, const Vec* axis, f32 sinA, f32 cosA); +void PSMTXRotRad(GC_Mtx mtx, char axis, f32 angle); +void PSMTXRotTrig(GC_Mtx mtx, char axis, f32 sinA, f32 cosA); +void PSMTXRotAxisRad(GC_Mtx mtx, const Vec* axis, f32 angle); + +void PSMTXTrans(GC_Mtx mtx, f32 xT, f32 yT, f32 zT); +void PSMTXTransApply(const GC_Mtx src, GC_Mtx dest, f32 xT, f32 yT, f32 zT); + +void PSMTXScale(GC_Mtx mtx, f32 xS, f32 yS, f32 zS); +void PSMTXScaleApply(const GC_Mtx src, GC_Mtx dest, f32 xS, f32 yS, f32 zS); +void PSMTXQuat(GC_Mtx mtx, const PSQuaternion* quat); + +//////////////////////////////////////////// + +//// PAIRED SINGLE MATRIX VEC FUNCTIONS //// +void PSMTXMultVec(const GC_Mtx, const Vec*, Vec*); +void PSMTXMultVecSR(const GC_Mtx, const Vec*, Vec*); +void PSMTXMultVecArraySR(const GC_Mtx, f32*, f32*, f32*); + +//////////////////////////////////////////// + +/////////// MATRIX44 FUNCTIONS //////////// +void PSMTX44Copy(Mtx44 src, Mtx44 dest); +void C_MTXPerspective(Mtx44 mtx, f32 fovY, f32 aspect, f32 n, f32 f); +void C_MTXOrtho(Mtx44 mtx, f32 t, f32 b, f32 l, f32 r, f32 n, f32 f); +//////////////////////////////////////////// + +///////// CODED C MATRIX FUNCTIONS ///////// +void C_MTXLookAt(GC_Mtx, const Vec*, const Vec*, const Vec*); +void C_MTXLightPerspective(GC_Mtx mtx, f32 fovY, f32 aspect, f32 scaleS, f32 scaleT, f32 transS, f32 transT); +void C_MTXLightOrtho(GC_Mtx mtx, f32 t, f32 b, f32 l, f32 r, f32 scaleS, f32 scaleT, f32 transS, f32 transT); +//////////////////////////////////////////// + +////////////// MATRIX INLINES ////////////// +static inline void MTXSetPosition(GC_Mtx mtx, const Vec* pos) +{ + mtx[0][3] = pos->x; + mtx[1][3] = pos->y; + mtx[2][3] = pos->z; +} + +//////////////////////////////////////////// #ifdef __cplusplus } diff --git a/include/dolphin/pad.h b/include/dolphin/pad.h index 69c79193..9c951343 100644 --- a/include/dolphin/pad.h +++ b/include/dolphin/pad.h @@ -9,6 +9,11 @@ extern "C" { #define PAD_CONTROLLER_NUM 4 +#define PAD_ERR_NONE 0 +#define PAD_ERR_NO_CONTROLLER -1 +#define PAD_ERR_NOT_READY -2 +#define PAD_ERR_TRANSFER -3 + #define PAD_MOTOR_STOP 0 #define PAD_MOTOR_RUMBLE 1 #define PAD_MOTOR_STOP_HARD 2 diff --git a/include/dolphin/string.h b/include/dolphin/string.h index 2e5970d1..0da20042 100644 --- a/include/dolphin/string.h +++ b/include/dolphin/string.h @@ -16,6 +16,7 @@ int stricmp(char*, char*); char* strcpy(char*, const char*); char* strcat(char* dst, const char* src); char* strncpy(char* dst, const char* src, size_t n); +char* strstr(const char* str, const char* pat); #ifdef __cplusplus }; diff --git a/include/famicom_emu.h b/include/famicom_emu.h index ef76d633..b260dd60 100644 --- a/include/famicom_emu.h +++ b/include/famicom_emu.h @@ -3,6 +3,7 @@ #include "types.h" #include "game.h" +#include "Famicom/famicom.h" #ifdef __cplusplus extern "C" { @@ -13,7 +14,7 @@ typedef struct game_famicom_emu_s { /* 0x00 */ GAME game; } GAME_FAMICOM_EMU; -extern void* my_malloc_func[]; +extern Famicom_MallocInfo my_malloc_func; extern void famicom_emu_main(GAME* game); extern void famicom_emu_init(GAME* game); diff --git a/include/jaudio_NES/emusound.h b/include/jaudio_NES/emusound.h new file mode 100644 index 00000000..81a38bcd --- /dev/null +++ b/include/jaudio_NES/emusound.h @@ -0,0 +1,18 @@ +#ifndef EMUSOUND_H +#define EMUSOUND_H + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void EmuSound_Start(u8* noise_data); +extern void EmuSound_Exit(); +extern void Sound_Write(u16 event, u8 value, u16 maybe_frames); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libc64/sprintf.h b/include/libc64/sprintf.h index 855d2d20..92aba94c 100644 --- a/include/libc64/sprintf.h +++ b/include/libc64/sprintf.h @@ -1,8 +1,14 @@ #ifndef SPRINTF_H #define SPRINTF_H -#include "types.h" +#ifdef __cplusplus +extern "C" { +#endif -s32 sprintf(char* dst, const char* fmt, ...); +int sprintf(char* dst, const char* fmt, ...); + +#ifdef __cplusplus +} +#endif #endif \ No newline at end of file diff --git a/include/libjsys/jsyswrapper.h b/include/libjsys/jsyswrapper.h index f7682e71..c1938f10 100644 --- a/include/libjsys/jsyswrapper.h +++ b/include/libjsys/jsyswrapper.h @@ -139,6 +139,13 @@ extern s32 JW_Resize(void* ptr, size_t new_size); extern size_t JW_GetMemBlockSize(void* ptr); extern void JW_JUTReport(int x, int y, int show_count, const char* fmt, ...); +extern int JC_JKRDecomp_checkCompressed(u8* bufp); +extern void JC_JKRDecomp_decode(u8* comp_bufp, u8* decomp_bufp, u32 decomp_buf_size, u32 skipCount); + +extern void* JC__JKRMountArchive(const char* path, int mount_mode, void* heap, int mount_direction); + +extern void* JC__JKRGetSystemHeap(); + #ifdef JSYSWRAPPER_DEBUG #define JSYSWRAPPER_PRINTF(console, fmt, ...) JC_JUTConsole_print_f(console, fmt, ...) #else diff --git a/include/m_card.h b/include/m_card.h index 55b8403f..b90ef73b 100644 --- a/include/m_card.h +++ b/include/m_card.h @@ -16,7 +16,7 @@ typedef struct { PersonalID_c pid[PLAYER_NUM]; } mCD_persistent_data_c; -extern u8 mCD_GetThisLandSlotNo_code(int* player_no, int* slot_card_results); +extern s32 mCD_GetThisLandSlotNo_code(int* player_no, s32* slot_card_results); extern void mCD_save_data_aram_malloc(); extern void mCD_set_aram_save_data(); extern void mCD_init_card(); diff --git a/include/m_controller.h b/include/m_controller.h index 34e2859d..350de4fb 100644 --- a/include/m_controller.h +++ b/include/m_controller.h @@ -8,24 +8,6 @@ extern "C" { #endif -#define BUTTON_NONE 0x0000 -#define BUTTON_CRIGHT 0x0001 -#define BUTTON_CLEFT 0x0002 -#define BUTTON_CDOWN 0x0004 -#define BUTTON_CUP 0x0008 -#define BUTTON_R 0x0010 -#define BUTTON_L 0x0020 -#define BUTTON_X 0x0040 -#define BUTTON_Y 0x0080 -#define BUTTON_DRIGHT 0x0100 -#define BUTTON_DLEFT 0x0200 -#define BUTTON_DDOWN 0x0400 -#define BUTTON_DUP 0x0800 -#define BUTTON_START 0x1000 -#define BUTTON_Z 0x2000 -#define BUTTON_B 0x4000 -#define BUTTON_A 0x8000 - #define CHECK_BTN_ALL(state, combo) (~((state) | ~(combo)) == 0) /* sizeof(struct controller_s) == 0x38 */ diff --git a/include/types.h b/include/types.h index 763e946d..9c7b972c 100644 --- a/include/types.h +++ b/include/types.h @@ -64,6 +64,24 @@ typedef u32 unknown; #endif #endif +#define BUTTON_NONE 0x0000 +#define BUTTON_CRIGHT 0x0001 +#define BUTTON_CLEFT 0x0002 +#define BUTTON_CDOWN 0x0004 +#define BUTTON_CUP 0x0008 +#define BUTTON_R 0x0010 +#define BUTTON_L 0x0020 +#define BUTTON_X 0x0040 +#define BUTTON_Y 0x0080 +#define BUTTON_DRIGHT 0x0100 +#define BUTTON_DLEFT 0x0200 +#define BUTTON_DDOWN 0x0400 +#define BUTTON_DUP 0x0800 +#define BUTTON_START 0x1000 +#define BUTTON_Z 0x2000 +#define BUTTON_B 0x4000 +#define BUTTON_A 0x8000 + #define ARRAY_SIZE(arr, type) (sizeof(arr) / sizeof(type)) #define ARRAY_COUNT(arr) (int)(sizeof(arr) / sizeof(arr[0])) diff --git a/src/famicom_emu.c b/src/famicom_emu.c index d0feba2b..63208c87 100644 --- a/src/famicom_emu.c +++ b/src/famicom_emu.c @@ -38,12 +38,12 @@ static void my_alloc_cleanup() { zelda_CleanupArena(); } -static void my_zelda_getmemblocksize(void* ptr) { - zelda_GetMemBlockSize(ptr); +static int my_zelda_getmemblocksize(void* ptr) { + return zelda_GetMemBlockSize(ptr); } -static void my_zelda_gettotalfreesize() { - zelda_GetTotalFreeSize(); +static int my_zelda_gettotalfreesize() { + return zelda_GetTotalFreeSize(); } static void* my_zelda_malloc_align(size_t size, u32 align) { @@ -59,7 +59,7 @@ static void my_zelda_free(void* ptr) { zelda_free(ptr); } -void* my_malloc_func[] = { +Famicom_MallocInfo my_malloc_func = { my_zelda_malloc_align, my_zelda_free, my_zelda_getmemblocksize, @@ -151,7 +151,7 @@ extern void famicom_emu_init(GAME* game) { my_alloc_init(game, freeXfbBase, freeXfbSize); - if (famicom_init(rom_id, my_malloc_func, player) != 0) { + if (famicom_init(rom_id, &my_malloc_func, player) != 0) { Common_Set(famicom_2DBAC, Common_Get(famicom_2DBAC) | 1); return_emu_game(game); } diff --git a/src/m_play.c b/src/m_play.c index 8fc53ba9..d6180256 100644 --- a/src/m_play.c +++ b/src/m_play.c @@ -414,7 +414,7 @@ void play_cleanup(GAME* game){ mCD_toNextLand(); mEA_CleanCardDLProgram(); - if(my_malloc_current == my_malloc_func){ + if(my_malloc_current == &my_malloc_func){ my_malloc_current = NULL; } @@ -507,7 +507,7 @@ void play_init(GAME* game){ zelda_InitArena((void*)aligned, freebytes - size); if(my_malloc_current == NULL){ - my_malloc_current = my_malloc_func; + my_malloc_current = &my_malloc_func; } mFM_FieldInit(play); diff --git a/src/static/Famicom/famicom.cpp b/src/static/Famicom/famicom.cpp new file mode 100644 index 00000000..ddfa5fb2 --- /dev/null +++ b/src/static/Famicom/famicom.cpp @@ -0,0 +1,2801 @@ +#include "Famicom/famicom.h" +#include "Famicom/famicomInternal.h" + +#include "dolphin/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 "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]; +#endif + +static u8 commentImageBuffer[CARD_COMMENT_SIZE + 0x5800]; +u8 save_game_image = false; +FamicomCommon famicomCommon; +static 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->work_priv.wram, famicomCommon.wp->work_priv._0B40, + famicomCommon.wp->work_priv._2A40, famicomCommon.sp->_17FC + ); + 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_CONTROLLER_NUM; 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, (u32)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->disk_motor) { + // 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 + } +} + +inline int GetPortStatus(int port) { + JUTGamePad::EPadPort padPort = (JUTGamePad::EPadPort)port; + return JUTGamePad::getPortStatus(padPort); +} + +static void nogbaInput() { + int port; + u32 disconnected_ports = 0; + + for (port = 0; port < PAD_CONTROLLER_NUM; port++) { + InputValid[port] = false; + + switch (GetPortStatus(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_CONTROLLER_NUM; 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; + } + } +} + +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 |= 0x2000; + } + + + 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->disk_motor != 0 && (famicomCommon.sp->_16B4 & 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; +} diff --git a/src/static/Famicom/famicom_nesinfo.cpp b/src/static/Famicom/famicom_nesinfo.cpp index a858785f..030c2536 100644 --- a/src/static/Famicom/famicom_nesinfo.cpp +++ b/src/static/Famicom/famicom_nesinfo.cpp @@ -30,8 +30,8 @@ static u32 nesinfo_rom_size = 0; static u8* nesinfo_rom_start = nullptr; static u8* nesinfo_rom_end = nullptr; static u32 nesinfo_expand_rom_size = 0; -static bool tcs_bad = false; -static bool ics_bad = false; +u8 tcs_bad = false; +u8 ics_bad = false; // clang-format off @@ -839,7 +839,7 @@ extern void nesinfo_tag_process1(u8* save_data, int mode, u32* max_ofs_p) { OSReport("ロムデータ参照: %d\n", rom_id); // Referencing ROM data: %d\n if (nesrom_filename_ptrs != nullptr && rom_p != nullptr) { - nesinfo_data_size = JKRFileLoader::readGlbResource(rom_p, 0x100000, nesrom_filename_ptrs[rom_id], + nesinfo_data_size = JKRFileLoader::readGlbResource(rom_p, 0x100000, (char*)nesrom_filename_ptrs[rom_id], EXPAND_SWITCH_DECOMPRESS); nesinfo_data_start = rom_p; nesinfo_data_end = rom_p + nesinfo_data_size; diff --git a/src/static/JSystem/JKernel/JKRFileFinder.cpp b/src/static/JSystem/JKernel/JKRFileFinder.cpp index e9a23781..71683dc1 100644 --- a/src/static/JSystem/JKernel/JKRFileFinder.cpp +++ b/src/static/JSystem/JKernel/JKRFileFinder.cpp @@ -27,7 +27,7 @@ bool JKRArcFinder::findNextFile() mBase.mFileIndex = mNextIndex; mBase.mFileID = dirEntry.mID; mBase.mFileTypeFlags = dirEntry.mFlags; - mIsFileOrDir = FLAG_OFF(mBase.mFileTypeFlags, 2); + mIsDir = FLAG_OFF(mBase.mFileTypeFlags, 2); mNextIndex++; } } @@ -51,12 +51,12 @@ bool JKRDvdFinder::findNextFile() mIsAvailable = DVDReadDir(&mDir, &entry); if (mIsAvailable) { - mIsFileOrDir = (bool)entry.isDir; + mIsDir = (bool)entry.isDir; mBase.mFileName = entry.name; mBase.mFileIndex = entry.entryNum; mBase.mFileID = 0; - mBase.mFileTypeFlags = mIsFileOrDir ? 2 : 1; + mBase.mFileTypeFlags = mIsDir ? 2 : 1; } } return mIsAvailable; diff --git a/src/static/jsyswrap.cpp b/src/static/jsyswrap.cpp index c1983056..fa1868eb 100644 --- a/src/static/jsyswrap.cpp +++ b/src/static/jsyswrap.cpp @@ -323,23 +323,23 @@ extern void JW_getPadStatus(PADStatus* padStatus) { } extern int JW_JUTGamepad_getErrorStatus() { - return (s8)gamePad[0].mErrorStatus; + return (s8)((JUTGamePad*)gamePad)[0].mErrorStatus; } extern u32 JW_JUTGamepad_getButton() { - return gamePad[0].mButtons.mButton; + return ((JUTGamePad*)gamePad)[0].mButtons.mButton; } extern u32 JW_JUTGamepad_getTrigger() { - return gamePad[0].mButtons.mTrigger; + return ((JUTGamePad*)gamePad)[0].mButtons.mTrigger; } extern f32 JW_JUTGamepad_getSubStickValue() { - return gamePad[0].mSubStick.mValue; + return ((JUTGamePad*)gamePad)[0].mSubStick.mValue; } extern int JW_JUTGamepad_getSubStickAngle() { - return gamePad[0].mSubStick.mAngle; + return ((JUTGamePad*)gamePad)[0].mSubStick.mAngle; } static bool FrameDrawing = false; diff --git a/src/static/libc64/sprintf.c b/src/static/libc64/sprintf.c index 535ed2ff..a50e58c0 100644 --- a/src/static/libc64/sprintf.c +++ b/src/static/libc64/sprintf.c @@ -8,8 +8,8 @@ static void* proutPrintf(void* dst, const char* fmt, int size) { return (void*)((u8*)memcpy(dst, fmt, size) + size); } -s32 sprintf(char* dst, const char* fmt, ...) { - s32 ret; +int sprintf(char* dst, const char* fmt, ...) { + int ret; va_list args; va_start(args, fmt);