From f5d851f7c2d6a491b866f75e087a88d93e6f53c8 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Mon, 24 Mar 2025 03:33:34 +0800 Subject: [PATCH 01/32] mpsetupfile (not 100% functional yet): load/save setup file, save new setups/groups, increased mp options/scenario data width --- .gitignore | 1 + port/include/mpsetups.h | 10 + port/src/mpsetups.c | 363 +++++++++++++++++++ src/game/filemgr.c | 11 - src/game/menuitem.c | 9 + src/game/mplayer/mplayer.c | 61 +--- src/game/mplayer/scenarios.c | 2 +- src/game/mplayer/scenarios/kingofthehill.inc | 4 +- src/game/mplayer/setup.c | 107 +++--- src/include/constants.h | 5 + src/include/data.h | 4 + src/include/game/mplayer/mplayer.h | 1 - src/include/types.h | 18 + 13 files changed, 484 insertions(+), 112 deletions(-) create mode 100644 port/include/mpsetups.h create mode 100644 port/src/mpsetups.c diff --git a/.gitignore b/.gitignore index 3d0bebd42..bb7c0cff9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ tools/mkrom/mkrom /.vs CppProperties.json .idea +cmake-build-debug diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h new file mode 100644 index 000000000..0341658a2 --- /dev/null +++ b/port/include/mpsetups.h @@ -0,0 +1,10 @@ +#ifndef _IN_MPSETUPS_H +#define _IN_MPSETUPS_H + +#include "types.h" + +s32 mpsetupLoadFile(); +void mpsetupLoadSetup(s32 index); +s32 mpsetupSaveFile(const char *name, u8 overwrite); + +#endif diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c new file mode 100644 index 000000000..8702d362e --- /dev/null +++ b/port/src/mpsetups.c @@ -0,0 +1,363 @@ +#include "types.h" +#include "constants.h" +#include "game/menu.h" +#include "game/savebuffer.h" +#include "game/mplayer/mplayer.h" +#include +#include "fs.h" +#include "system.h" + +/* +MP Setup File Format + # header + [version{1}] + [defaultsetup{1}] + [numsetups{1}] + [numgroups{1}] + # setups + [setup_1{64}] + ... + [setup_n{64}] + # groups + [offset_index_1{1}] + [name_1{1-32}] + ... + [offset_index_n{1}] + [name_n{1-32}] + */ + +extern struct menudialogdef g_MpSaveSetupNameMenuDialog; +struct menudialogdef g_MpGroupNameMenuDialog; + +#define MPSETUP_FILE "mpsetups.bin" +u8 filebuffer[sizeof(struct mpsetupfile)]; + +struct mpsetupfile g_MpSetupFile; +s16 g_MpCurrentSetup = -1; +s16 g_MpCurrentGroup = -1; + +#define BUF_READ(dst, buf, type) { dst = *(type*)((buf)); buf += sizeof(type); } +#define BUF_WRITE(src, buf, type) { *((type*)(buf)) = src; buf += sizeof(type); } +#define BUF_WRITEBLOCK(buf, src, size) { \ + memcpy(buf, src, size); \ + buf += size; } + +// #### Group List +MenuItemHandlerResult mpSelectGroupHandler(s32 operation, struct menuitem *item, union handlerdata *data); + +struct menuitem g_MpSetupGroupsDialogItems[] = { + { + MENUITEMTYPE_LIST, + 0, + MENUITEMFLAG_LABEL_CUSTOMCOLOUR, + 0x00000078, + 0x00000042, + mpSelectGroupHandler, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_MpSetupGroupsDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Groups", + g_MpSetupGroupsDialogItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_CLOSEONSELECT, + NULL, +}; + +MenuItemHandlerResult mpSelectGroupHandler(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETCOLOUR: { + u8 disabled = data->list.value == g_MpSetupFile.numgroups && g_MpSetupFile.numsetups == 0; + u32 colour = data->list.unk04; + data->list.unk04 = disabled ? 0xff333300 | (colour & 0xff) : colour; + } + case MENUOP_GETOPTIONCOUNT: + data->list.value = g_MpSetupFile.numgroups + 1; + break; + case MENUOP_GETOPTIONTEXT: + if (data->list.value == g_MpSetupFile.numgroups) { + return (uintptr_t) "[New Group]\n"; + } + return (uintptr_t) g_MpSetupFile.groups[data->list.value].name; + case MENUOP_SET: + { + u8 newgroupdisabled = data->list.value == g_MpSetupFile.numgroups && g_MpSetupFile.numsetups == 0; + if (newgroupdisabled) { + return 0; + } + + menuPopDialog(); + if (data->list.value == g_MpSetupFile.numgroups) { + menuPushDialog(&g_MpGroupNameMenuDialog); + } + else { + g_MpCurrentGroup = data->list.value; + menuPushDialog(&g_MpSaveSetupNameMenuDialog); + } + } + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0xfffff; + break; + } + + return 0; +} + +static void createGroup(char *name) +{ + g_MpCurrentGroup = g_MpSetupFile.numgroups++; + struct setupgroup *group = &g_MpSetupFile.groups[g_MpCurrentGroup]; + strcpy(group->name, name); + group->offset_index = g_MpSetupFile.numsetups; +} + +// #### Group name dialog +MenuItemHandlerResult menuhandlerMpSetupGroupName(s32 operation, struct menuitem *item, union handlerdata *data) +{ + char *name = data->keyboard.string; + struct setupgroup *group = &g_MpSetupFile.groups[g_MpSetupFile.numgroups]; + + switch (operation) { + case MENUOP_GETTEXT: + strcpy(name, group->name); + break; + case MENUOP_SETTEXT: + strcpy(group->name, name); + break; + case MENUOP_SET: + createGroup(name); + menuPushDialog(&g_MpSaveSetupNameMenuDialog); + break; + } + + return 0; +} + +struct menuitem g_MpGroupNameMenuItems[] = { +#if VERSION != VERSION_JPN_FINAL + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t) "Enter the group name:\n", + 0, + NULL, + }, +#endif + { + MENUITEMTYPE_KEYBOARD, + 0, + 0, + 0, + 0, + menuhandlerMpSetupGroupName, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_MpGroupNameMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Group Name\n", + g_MpGroupNameMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + + +static size_t readStr(char *dst, char *src) +{ + char *start = dst; + while (*src) { + *dst = *src; + dst++; + src++; + } + *dst = '\0'; + dst++; + return dst - start; +} + +static void mpsetupfileRead(struct mpsetupfile *setupfile, u8 *buffer) +{ + BUF_READ(setupfile->version, buffer, u8); + BUF_READ(setupfile->defaultsetup, buffer, u8); + BUF_READ(setupfile->numsetups, buffer, u8); + BUF_READ(setupfile->numgroups, buffer, u8); + + for (int i = 0; i < setupfile->numsetups; ++i) { + memcpy(setupfile->setups[i].bytes, buffer, MPSETUP_BLOCKSIZE); + buffer += MPSETUP_BLOCKSIZE; + } + + for (int i = 0; i < setupfile->numgroups; ++i) { + struct setupgroup *group = &setupfile->groups[i]; + BUF_READ(group->offset_index, buffer, u8); + buffer += readStr(group->name, buffer); + } +} + +void mpsetupfileWrite(u8 *buffer, struct mpsetupfile *setupfile, struct savebuffer *setup, u8 setupidx, u8 overwrite) +{ + BUF_WRITE(setupfile->version, buffer, u8); + BUF_WRITE(setupfile->defaultsetup, buffer, u8); + BUF_WRITE(setupfile->numsetups, buffer, u8); + BUF_WRITE(setupfile->numgroups, buffer, u8); + + for (int i = 0; i < setupfile->numsetups; ++i) { + if (i == setupidx) { + BUF_WRITEBLOCK(buffer, setup->bytes, MPSETUP_BLOCKSIZE); + + if (!overwrite) { + BUF_WRITEBLOCK(buffer, setupfile->setups[i].bytes, MPSETUP_BLOCKSIZE) + } + continue; + } + + BUF_WRITEBLOCK(buffer, setupfile->setups[i].bytes, MPSETUP_BLOCKSIZE) + } + + for (int i = 0; i < setupfile->numgroups; ++i) { + struct setupgroup *group = &setupfile->groups[i]; + BUF_WRITE(group->offset_index, buffer, u8); + buffer += readStr(buffer, group->name); + } +} + +static FILE *openMpSetupFile(u8 mode) { + FILE *f; + + if (fsFileSize(MPSETUP_FILE) < 0) { + // setup file doesn't exist: create one + f = fsFileOpenWrite(MPSETUP_FILE); + fsFileFree(f); + } + + if (mode == 'r') { + f = fsFileOpenRead(MPSETUP_FILE); + } + else if (mode == 'w') { + f = fsFileOpenWrite(MPSETUP_FILE); + } + else { + sysLogPrintf(LOG_ERROR, "openMpSetupFile: invalid file mode"); + return NULL; + } + + if (f == NULL) { + sysLogPrintf(LOG_NOTE, "Unable to open mp setup file"); + return NULL; + } + + return f; +} + +void zeroFileBuffer() +{ + memset(filebuffer, 0, sizeof(struct mpsetupfile)); +} + +s32 mpsetupLoadFile() +{ + FILE *f = openMpSetupFile('r'); + if (f == NULL) { + return -1; + } + + zeroFileBuffer(); + + fseek(f, 0L, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + fread(filebuffer, 1, size, f); + + mpsetupfileRead(&g_MpSetupFile, filebuffer); + + if (g_MpSetupFile.defaultsetup != 0) { + g_MpCurrentSetup = g_MpSetupFile.defaultsetup; + } + + // setup a default group if there is none + if (g_MpSetupFile.numgroups == 0) { + g_MpSetupFile.numgroups++; + strcpy(g_MpSetupFile.groups[0].name, "Default\n"); + g_MpSetupFile.groups[0].offset_index = 0; + g_MpCurrentSetup = -1; + } + + fsFileFree(f); + + return 0; +} + +static s32 getNewSetupIndex() +{ + if (g_MpSetupFile.numsetups == 0) { + return 0; + } + + if (g_MpCurrentGroup == g_MpSetupFile.numgroups - 1) + return g_MpSetupFile.numsetups; + + return g_MpSetupFile.groups[g_MpCurrentGroup + 1].offset_index; +} + +static s32 getGroupIndex(s32 setupIndex) +{ + s32 index = 0; + struct setupgroup *groups = g_MpSetupFile.groups; + for (int i = 0; i < g_MpSetupFile.numgroups; ++i) { + if (setupIndex >= groups[i].offset_index) { + index = groups[i].offset_index; + } + } + + return index; +} + +s32 mpsetupSaveFile(const char *name, u8 overwrite) +{ + struct savebuffer setup; + + if (!overwrite) { + g_MpCurrentSetup = getNewSetupIndex(); + g_MpSetupFile.numsetups++; + + for (int i = g_MpCurrentGroup + 1; i < g_MpSetupFile.numgroups; ++i) { + g_MpSetupFile.groups[i].offset_index++; + } + } + + savebufferClear(&setup); + mpsetupfileSaveWad(&setup); + + zeroFileBuffer(); + mpsetupfileWrite(filebuffer, &g_MpSetupFile, &setup, g_MpCurrentSetup, overwrite); + + FILE *f = openMpSetupFile('w'); + if (f == NULL) { + return -1; + } + + fwrite(filebuffer, sizeof(struct mpsetupfile), 1, f); + fsFileFree(f); + + // reload the file to update in-memory structs + mpsetupLoadFile(); + return 0; +} + +void mpsetupLoadSetup(s32 index) +{ + struct savebuffer buffer; + savebufferClear(&buffer); + struct setupblock *block = &g_MpSetupFile.setups[index]; + memcpy(&buffer.bytes, block->bytes, MPSETUP_BLOCKSIZE); + mpsetupfileLoadWad(&buffer); + g_MpCurrentSetup = index; + g_MpCurrentGroup = getGroupIndex(index); +} diff --git a/src/game/filemgr.c b/src/game/filemgr.c index e82d7af15..9c87e071d 100644 --- a/src/game/filemgr.c +++ b/src/game/filemgr.c @@ -816,12 +816,6 @@ bool filemgrAttemptOperation(s32 device, bool closeonsuccess) g_Menus[g_MpPlayerNum].fm.fileid, g_Menus[g_MpPlayerNum].fm.deviceserial); break; - case FILEOP_SAVE_MPSETUP: - errnum = mpsetupfileSave(device, - g_Menus[g_MpPlayerNum].fm.fileid, - g_Menus[g_MpPlayerNum].fm.deviceserial); - showfilesaved = true; - break; case FILEOP_WRITE_GAME: case FILEOP_WRITE_MPSETUP: case FILEOP_WRITE_MPPLAYER: @@ -845,11 +839,6 @@ bool filemgrAttemptOperation(s32 device, bool closeonsuccess) g_Menus[g_MpPlayerNum].fm.fileid, g_Menus[g_MpPlayerNum].fm.deviceserial); break; - case FILEOP_LOAD_MPSETUP: - errnum = mpsetupfileLoad(device, - g_Menus[g_MpPlayerNum].fm.fileid, - g_Menus[g_MpPlayerNum].fm.deviceserial); - break; case FILEOP_READ_GAME: case FILEOP_READ_MPSETUP: case FILEOP_READ_MPPLAYER: diff --git a/src/game/menuitem.c b/src/game/menuitem.c index 72c24573a..18f80c1a6 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -614,6 +614,15 @@ Gfx *menuitemListRender(Gfx *gdl, struct menurendercontext *context) height = 0; } + // reuse the label color flag here + if (context->item->flags & MENUITEMFLAG_LABEL_CUSTOMCOLOUR) { + u32 savedunk04 = sp15c.list.unk04; + sp15c.list.unk04 = colour; + context->item->handler(MENUOP_GETCOLOUR, context->item, &sp15c); + colour = sp15c.list.unk04; + sp15c.list.unk04 = savedunk04; + } + gdl = textRenderProjected(gdl, &x, &y, text2, chars, font, colour, context->width - left + context->x, height, sp128, 0); // Consider a checkbox diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index b329f51db..3d9381522 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -28,6 +28,9 @@ #include "lib/lib_317f0.h" #include "data.h" #include "types.h" +#include "fs.h" +#include "system.h" +#include "mpsetups.h" // bss struct chrdata *g_MpAllChrPtrs[MAX_MPCHRS]; @@ -3924,7 +3927,7 @@ void mpsetupfileLoadWad(struct savebuffer *buffer) scenarioInit(); scenarioReadSave(buffer); - g_MpSetup.options = savebufferReadBits(buffer, 21); + g_MpSetup.options = savebufferReadBits(buffer, 32); g_MpSetup.chrslots &= 0x000f; for (i = 0; i < MAX_BOTS; i++) { @@ -3984,7 +3987,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) scenarioWriteSave(buffer); - savebufferOr(buffer, g_MpSetup.options, 21); + savebufferOr(buffer, g_MpSetup.options, 32); for (i = 0; i < MAX_BOTS; i++) { savebufferOr(buffer, g_BotConfigsArray[i].type, 5); @@ -4039,60 +4042,6 @@ void mpsetupfileGetOverview(char *arg0, char *filename, u16 *numsims, u16 *stage *scenarionum = savebufferReadBits(&buffer, 3); } -s32 mpsetupfileSave(s32 device, s32 fileid, u16 deviceserial) -{ - s32 ret; - s32 newfileid; - struct savebuffer buffer; - - if (device >= 0) { - savebufferClear(&buffer); - mpsetupfileSaveWad(&buffer); - func0f0d54c4(&buffer); - - ret = pakSaveAtGuid(device, fileid, PAKFILETYPE_MPSETUP, buffer.bytes, &newfileid, 0); - var80075bd0[1] = true; - - if (ret == 0) { - g_MpSetup.fileguid.fileid = newfileid; - g_MpSetup.fileguid.deviceserial = deviceserial; - return 0; - } - - g_FilemgrLastPakError = ret; - return -1; - } - - return -1; -} - -s32 mpsetupfileLoad(s32 device, s32 fileid, u16 deviceserial) -{ - s32 ret; - struct savebuffer buffer; - - if (device >= 0) { - savebufferClear(&buffer); - ret = pakReadBodyAtGuid(device, fileid, buffer.bytes, 0); - - if (ret == 0) { - g_MpSetup.fileguid.fileid = fileid; - g_MpSetup.fileguid.deviceserial = deviceserial; - - mpsetupfileLoadWad(&buffer); - func0f0d54c4(&buffer); - - return 0; - } - - g_FilemgrLastPakError = ret; - - return -1; - } - - return -1; -} - void func0f18e558(void) { s32 i; diff --git a/src/game/mplayer/scenarios.c b/src/game/mplayer/scenarios.c index 60a4f85f5..f7a504382 100644 --- a/src/game/mplayer/scenarios.c +++ b/src/game/mplayer/scenarios.c @@ -438,7 +438,7 @@ void scenarioWriteSave(struct savebuffer *buffer) if (g_MpScenarios[g_MpSetup.scenario].writesavefunc) { g_MpScenarios[g_MpSetup.scenario].writesavefunc(buffer); } else { - savebufferOr(buffer, 0, 8); + savebufferOr(buffer, 0, 32); } } diff --git a/src/game/mplayer/scenarios/kingofthehill.inc b/src/game/mplayer/scenarios/kingofthehill.inc index 0160c7632..721550423 100644 --- a/src/game/mplayer/scenarios/kingofthehill.inc +++ b/src/game/mplayer/scenarios/kingofthehill.inc @@ -183,12 +183,12 @@ struct menudialogdef g_KohOptionsMenuDialog = { void kohReadSave(struct savebuffer *buffer) { - g_Vars.mphilltime = savebufferReadBits(buffer, 8); + g_Vars.mphilltime = savebufferReadBits(buffer, 32); } void kohWriteSave(struct savebuffer *buffer) { - savebufferOr(buffer, g_Vars.mphilltime, 8); + savebufferOr(buffer, g_Vars.mphilltime, 32); } void kohInit(void) diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index cae26d3c7..a94d32dd1 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -23,6 +23,8 @@ #include "data.h" #include "gbiex.h" #include "types.h" +#include "system.h" +#include "mpsetups.h" struct menuitem g_MpCharacterMenuItems[]; struct menudialogdef g_MpAddSimulantMenuDialog; @@ -31,6 +33,10 @@ struct menudialogdef g_MpChangeTeamNameMenuDialog; struct menudialogdef g_MpEditSimulantMenuDialog; struct menudialogdef g_MpSaveSetupNameMenuDialog; +extern struct menudialogdef g_MpSetupGroupsDialog; +extern struct menudialogdef g_FilemgrFileSavedMenuDialog; +extern struct menudialogdef g_FilemgrErrorMenuDialog; + #ifndef PLATFORM_N64 extern s32 g_MpWeaponSetNum; #endif @@ -454,6 +460,7 @@ MenuItemHandlerResult menuhandlerMpConfirmSaveChr(s32 operation, struct menuitem MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *item, union handlerdata *data) { char *name = data->keyboard.string; + s32 err; switch (operation) { case MENUOP_GETTEXT: @@ -463,7 +470,14 @@ MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *ite strcpy(g_MpSetup.name, name); break; case MENUOP_SET: - filemgrPushSelectLocationDialog(7, FILETYPE_MPSETUP); + err = mpsetupSaveFile(name, false); + if (!err) { + menuPushDialog(&g_FilemgrFileSavedMenuDialog); + } + else { +// menuPushDialog(&g_FilemgrErrorMenuDialog); + // TODO + } break; } @@ -474,7 +488,7 @@ MenuItemHandlerResult menuhandlerMpSaveSetupOverwrite(s32 operation, struct menu { if (operation == MENUOP_SET) { menuPopDialog(); - filemgrSaveOrLoad(&g_MpSetup.fileguid, FILEOP_SAVE_MPSETUP, 0); + mpsetupSaveFile(g_MpSetupFile.setups[g_MpCurrentSetup].bytes, true); } return 0; @@ -484,7 +498,7 @@ MenuItemHandlerResult menuhandlerMpSaveSetupCopy(s32 operation, struct menuitem { if (operation == MENUOP_SET) { menuPopDialog(); - menuPushDialog(&g_MpSaveSetupNameMenuDialog); + menuPushDialog(&g_MpSetupGroupsDialog); } return 0; @@ -1112,10 +1126,10 @@ struct menuitem g_MpSaveSetupExistsMenuItems[] = { NULL, }, { - MENUITEMTYPE_LABEL, + MENUITEMTYPE_SEPARATOR, + 0, + 0, 0, - MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_SMALLFONT, - (uintptr_t)&filemgrMenuTextDeviceName, 0, NULL, }, @@ -1123,8 +1137,16 @@ struct menuitem g_MpSaveSetupExistsMenuItems[] = { { MENUITEMTYPE_LABEL, 0, - MENUITEMFLAG_LESSLEFTPADDING, - L_MPMENU_184, // "Do you want to save over your original game file?" + MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Save over your\noriginal setup?\n", + 0, + NULL, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, 0, NULL, }, @@ -2232,17 +2254,15 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * switch (operation) { case MENUOP_GETOPTIONCOUNT: data->list.value = mpGetNumUnlockedPresets(); - - if (g_FileLists[1] != NULL) { - data->list.value += g_FileLists[1]->numfiles; - } + data->list.value += g_MpSetupFile.numsetups; break; case MENUOP_GETOPTIONTEXT: if (data->list.value < mpGetNumUnlockedPresets()) { return (uintptr_t)mpGetPresetNameBySlot(data->list.value); } - if (g_FileLists[1] != NULL) { - func0f0d564c(g_FileLists[1]->files[data->list.value - mpGetNumUnlockedPresets()].name, g_StringPointer, false); + if (g_MpSetupFile.numsetups > 0) { + struct setupblock *block = &g_MpSetupFile.setups[data->list.value - mpGetNumUnlockedPresets()]; + func0f0d564c(block->bytes, g_StringPointer, false); return (uintptr_t)g_StringPointer; } break; @@ -2251,14 +2271,10 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * if (data->list.value < mpGetNumUnlockedPresets()) { mp0f18dec4(data->list.value); - } else if (g_FileLists[1] != NULL) { - struct filelistfile *file = &g_FileLists[1]->files[data->list.value - mpGetNumUnlockedPresets()]; - struct fileguid guid; - - guid.fileid = file->fileid; - guid.deviceserial = file->deviceserial; - - filemgrSaveOrLoad(&guid, FILEOP_LOAD_MPSETUP, 0); + g_MpCurrentSetup = -1; + } + else { + mpsetupLoadSetup(data->list.value - mpGetNumUnlockedPresets()); } if (item->param == 1) { @@ -2273,19 +2289,14 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * data->list.value = 0xfffff; break; case MENUOP_GETOPTGROUPCOUNT: - data->list.value = 1; - - if (g_FileLists[1] != NULL) { - data->list.value += g_FileLists[1]->numdevices; - } + data->list.value = g_MpSetupFile.numgroups + 1; break; case MENUOP_GETOPTGROUPTEXT: if (data->list.value == 0) { return (uintptr_t)langGet(L_MPMENU_141); // "Presets" } - if (g_FileLists[1] != NULL) { - return (uintptr_t)filemgrGetDeviceNameOrStartIndex(1, operation, data->list.value - 1); - } + + return (uintptr_t)&g_MpSetupFile.groups[data->list.value - 1].name; break; case MENUOP_GETGROUPSTARTINDEX: if (data->list.value == 0) { @@ -2293,16 +2304,14 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * } else { data->list.groupstartindex = mpGetNumUnlockedPresets(); - if (g_FileLists[1] != NULL) { - data->list.groupstartindex += filemgrGetDeviceNameOrStartIndex(1, operation, data->list.value - 1); + if (g_MpSetupFile.numgroups > 0) { + data->list.groupstartindex += g_MpSetupFile.groups[data->list.value - 1].offset_index; } } break; case MENUOP_LISTITEMFOCUS: if (data->list.value < mpGetNumUnlockedPresets()) { g_Menus[g_MpPlayerNum].mpsetup.slotindex = 0xffff; - } else { - g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value - mpGetNumUnlockedPresets(); } break; } @@ -2630,6 +2639,22 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { 0, NULL, }, + { + MENUITEMTYPE_SEPARATOR, + 0, + MENUITEMFLAG_00000002, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_00000002 | MENUITEMFLAG_SELECTABLE_CENTRE, + L_MPMENU_414, // "Press the B Button to go back." + 0, + NULL, + }, { MENUITEMTYPE_END }, }; @@ -5013,13 +5038,10 @@ MenuItemHandlerResult menuhandler0017ef30(s32 operation, struct menuitem *item, MenuItemHandlerResult menuhandlerMpSaveSettings(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_SET) { - if (g_MpSetup.fileguid.fileid == 0) { - menuPushDialog(&g_MpSaveSetupNameMenuDialog); - } else { -#if VERSION >= VERSION_NTSC_1_0 - filemgrSetDevice1BySerial(g_MpSetup.fileguid.deviceserial); -#endif - + if (g_MpCurrentSetup < 0) { + menuPushDialog(&g_MpSetupGroupsDialog); + } + else { menuPushDialog(&g_MpSaveSetupExistsMenuDialog); } } @@ -5362,6 +5384,9 @@ MenuDialogHandlerResult menudialogCombatSimulator(s32 operation, struct menudial g_Vars.waitingtojoin[1] = false; g_Vars.waitingtojoin[2] = false; g_Vars.waitingtojoin[3] = false; + + // load the setup file when entering the Combat Simulator + mpsetupLoadFile(); } if (g_Menus[g_MpPlayerNum].curdialog diff --git a/src/include/constants.h b/src/include/constants.h index 110e88f5b..1baa3d998 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -4754,6 +4754,11 @@ enum weaponnum { #define EXTRA_SLEEP_TIME 1000LL // 100us +#define MPSETUP_MAXSETUPS 100 +#define MPSETUP_MAXGROUPS 100 +#define MPSETUP_MAXGROUPNAME 33 +#define MPSETUP_BLOCKSIZE 64 + #endif #endif diff --git a/src/include/data.h b/src/include/data.h index 0bc0f12b3..8b71f7fdd 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -562,6 +562,10 @@ extern s32 g_FileAutoSelect; extern u8 g_MpWeaponSetRandomFilters[NUM_MPWEAPONS]; extern s32 g_MpWeaponRandomFilterNum; +extern struct mpsetupfile g_MpSetupFile; +extern s16 g_MpCurrentSetup; +extern s16 g_MpCurrentGroup; + #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) diff --git a/src/include/game/mplayer/mplayer.h b/src/include/game/mplayer/mplayer.h index 3d2591ca7..b097e5a39 100644 --- a/src/include/game/mplayer/mplayer.h +++ b/src/include/game/mplayer/mplayer.h @@ -122,7 +122,6 @@ void mpsetupfileLoadWad(struct savebuffer *buffer); void mpsetupfileSaveWad(struct savebuffer *buffer); void mpsetupfileGetOverview(char *arg0, char *filename, u16 *numsims, u16 *stagenum, u16 *scenarionum); s32 mpsetupfileSave(s32 device, s32 filenum, u16 deviceserial); -s32 mpsetupfileLoad(s32 device, s32 filenum, u16 deviceserial); void func0f18e558(void); struct modeldef *func0f18e57c(s32 index, s32 *headnum); diff --git a/src/include/types.h b/src/include/types.h index 1da61867b..c5496911b 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -6156,6 +6156,24 @@ struct extplayerconfig { s32 usereloads; }; +struct setupblock { + u8 bytes[MPSETUP_BLOCKSIZE]; +}; + +struct setupgroup { + char name[MPSETUP_MAXGROUPNAME]; + u8 offset_index; +}; + +struct mpsetupfile { + u8 version; + u8 defaultsetup; + u8 numsetups; + u8 numgroups; + struct setupblock setups[MPSETUP_MAXSETUPS]; + struct setupgroup groups[MPSETUP_MAXGROUPS]; +}; + #endif #endif From ad9ed9ff3b766878087463f99d3d656c4a540c48 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Tue, 25 Mar 2025 01:05:57 +0800 Subject: [PATCH 02/32] mpsetupfile: scrapped groups, manage settings: rename/delete --- port/include/mpsetups.h | 4 +- port/src/mpsetups.c | 414 ++++++++++++++++++++++----------------- src/game/mplayer/setup.c | 51 ++--- src/include/data.h | 1 - src/include/types.h | 7 - 5 files changed, 257 insertions(+), 220 deletions(-) diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h index 0341658a2..5c7455c4d 100644 --- a/port/include/mpsetups.h +++ b/port/include/mpsetups.h @@ -4,7 +4,7 @@ #include "types.h" s32 mpsetupLoadFile(); -void mpsetupLoadSetup(s32 index); -s32 mpsetupSaveFile(const char *name, u8 overwrite); +void mpsetupLoadSetup(s32 slotindex); +s32 mpsetupSaveSetup(s32 slotindex); #endif diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 8702d362e..1609007d2 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -1,11 +1,13 @@ #include "types.h" #include "constants.h" +#include "bss.h" #include "game/menu.h" #include "game/savebuffer.h" #include "game/mplayer/mplayer.h" #include #include "fs.h" #include "system.h" +#include "mpsetups.h" /* MP Setup File Format @@ -13,28 +15,19 @@ MP Setup File Format [version{1}] [defaultsetup{1}] [numsetups{1}] - [numgroups{1}] # setups [setup_1{64}] ... [setup_n{64}] - # groups - [offset_index_1{1}] - [name_1{1-32}] - ... - [offset_index_n{1}] - [name_n{1-32}] */ extern struct menudialogdef g_MpSaveSetupNameMenuDialog; -struct menudialogdef g_MpGroupNameMenuDialog; #define MPSETUP_FILE "mpsetups.bin" u8 filebuffer[sizeof(struct mpsetupfile)]; struct mpsetupfile g_MpSetupFile; s16 g_MpCurrentSetup = -1; -s16 g_MpCurrentGroup = -1; #define BUF_READ(dst, buf, type) { dst = *(type*)((buf)); buf += sizeof(type); } #define BUF_WRITE(src, buf, type) { *((type*)(buf)) = src; buf += sizeof(type); } @@ -42,62 +35,139 @@ s16 g_MpCurrentGroup = -1; memcpy(buf, src, size); \ buf += size; } -// #### Group List -MenuItemHandlerResult mpSelectGroupHandler(s32 operation, struct menuitem *item, union handlerdata *data); +// #### Rename setup dialogue +MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) +{ + char *name = data->keyboard.string; + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + struct setupblock *setup = &g_MpSetupFile.setups[slotindex]; + s32 err; -struct menuitem g_MpSetupGroupsDialogItems[] = { + switch (operation) { + case MENUOP_GETTEXT: +// strcpy(name, g_MpSetup.name); + strcpy(name, setup->bytes); + break; + case MENUOP_SETTEXT: + strcpy(g_MpSetup.name, name); + break; + case MENUOP_SET: + strcpy(setup->bytes, name); + err = mpsetupSaveSetup(slotindex); + if (!err) { + menuPopDialog(); + } + else { + // TODO +// menuPushDialog(&g_FilemgrFileSavedMenuDialog); + } + break; + } + + return 0; +} + +struct menuitem g_RenameSetupItems[] = { +#if VERSION != VERSION_JPN_FINAL { - MENUITEMTYPE_LIST, + MENUITEMTYPE_LABEL, 0, - MENUITEMFLAG_LABEL_CUSTOMCOLOUR, - 0x00000078, - 0x00000042, - mpSelectGroupHandler, + MENUITEMFLAG_LESSLEFTPADDING, + L_MPMENU_189, // "Enter a name for your game setup file:" + 0, + NULL, + }, +#endif + { + MENUITEMTYPE_KEYBOARD, + 0, + 0, + 0, + 0, + menuhandlerRenameSetup, }, { MENUITEMTYPE_END }, }; -struct menudialogdef g_MpSetupGroupsDialog = { +struct menudialogdef g_RenameSetupDialog = { MENUDIALOGTYPE_DEFAULT, - (uintptr_t) "Groups", - g_MpSetupGroupsDialogItems, + (uintptr_t)"Setup Name:\n", + g_RenameSetupItems, NULL, - MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_CLOSEONSELECT, + MENUDIALOGFLAG_LITERAL_TEXT, NULL, }; -MenuItemHandlerResult mpSelectGroupHandler(s32 operation, struct menuitem *item, union handlerdata *data) +// #### Delete setup dialog +static s32 deleteSetup(); +MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data) { - switch (operation) { - case MENUOP_GETCOLOUR: { - u8 disabled = data->list.value == g_MpSetupFile.numgroups && g_MpSetupFile.numsetups == 0; - u32 colour = data->list.unk04; - data->list.unk04 = disabled ? 0xff333300 | (colour & 0xff) : colour; - } - case MENUOP_GETOPTIONCOUNT: - data->list.value = g_MpSetupFile.numgroups + 1; - break; - case MENUOP_GETOPTIONTEXT: - if (data->list.value == g_MpSetupFile.numgroups) { - return (uintptr_t) "[New Group]\n"; - } - return (uintptr_t) g_MpSetupFile.groups[data->list.value].name; - case MENUOP_SET: - { - u8 newgroupdisabled = data->list.value == g_MpSetupFile.numgroups && g_MpSetupFile.numsetups == 0; - if (newgroupdisabled) { - return 0; - } - - menuPopDialog(); - if (data->list.value == g_MpSetupFile.numgroups) { - menuPushDialog(&g_MpGroupNameMenuDialog); + if (operation == MENUOP_SET) { + s32 err = deleteSetup(); + if (!err) { + menuPopDialog(); + menuPopDialog(); } else { - g_MpCurrentGroup = data->list.value; - menuPushDialog(&g_MpSaveSetupNameMenuDialog); + // TODO } } + + return 0; +} + +struct menuitem g_DeleteSetupItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)"Delete Setup?\n", + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE, + L_OPTIONS_385, // "No" + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CENTRE, + L_OPTIONS_386, // "Yes" + 0, + menuhandlerDeleteSetup, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_DeleteSetupDialog = { + MENUDIALOGTYPE_DANGER, + (uintptr_t)"Delete Setup\n", + g_DeleteSetupItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +// #### Manage settings dialog +struct menudialogdef g_ManageSetupDialog; +MenuItemHandlerResult mpSelectSettingHandler(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->list.value = g_MpSetupFile.numsetups; + break; + case MENUOP_GETOPTIONTEXT: + return (uintptr_t) g_MpSetupFile.setups[data->list.value].bytes; + case MENUOP_SET: + { + g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value; + menuPushDialog(&g_ManageSetupDialog); + } case MENUOP_GETSELECTEDINDEX: data->list.value = 0xfffff; break; @@ -106,68 +176,93 @@ MenuItemHandlerResult mpSelectGroupHandler(s32 operation, struct menuitem *item, return 0; } -static void createGroup(char *name) -{ - g_MpCurrentGroup = g_MpSetupFile.numgroups++; - struct setupgroup *group = &g_MpSetupFile.groups[g_MpCurrentGroup]; - strcpy(group->name, name); - group->offset_index = g_MpSetupFile.numsetups; -} +struct menuitem g_MpManageSettingsListItems[] = { + { + MENUITEMTYPE_LIST, + 0, + 0, + 0x00000078, + 0x00000042, + mpSelectSettingHandler, + }, + { MENUITEMTYPE_END }, -// #### Group name dialog -MenuItemHandlerResult menuhandlerMpSetupGroupName(s32 operation, struct menuitem *item, union handlerdata *data) -{ - char *name = data->keyboard.string; - struct setupgroup *group = &g_MpSetupFile.groups[g_MpSetupFile.numgroups]; +}; - switch (operation) { - case MENUOP_GETTEXT: - strcpy(name, group->name); - break; - case MENUOP_SETTEXT: - strcpy(group->name, name); - break; - case MENUOP_SET: - createGroup(name); - menuPushDialog(&g_MpSaveSetupNameMenuDialog); - break; +struct menudialogdef g_MpManageSettingsDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Manage Settings\n", + g_MpManageSettingsListItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +struct menudialogdef g_RenameSetupDialog; +static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_RenameSetupDialog); } return 0; } -struct menuitem g_MpGroupNameMenuItems[] = { -#if VERSION != VERSION_JPN_FINAL - { - MENUITEMTYPE_LABEL, - 0, - MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t) "Enter the group name:\n", - 0, - NULL, - }, -#endif - { - MENUITEMTYPE_KEYBOARD, - 0, - 0, - 0, - 0, - menuhandlerMpSetupGroupName, - }, - { MENUITEMTYPE_END }, +static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_DeleteSetupDialog); + } + + return 0; +} + +struct menuitem g_ManageSetupItems[] = { + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Rename\n", + 0, + menuhandlerSetupRename, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Delete\n", + 0, + menuhandlerSetupDelete, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, }; -struct menudialogdef g_MpGroupNameMenuDialog = { - MENUDIALOGTYPE_DEFAULT, - (uintptr_t) "Group Name\n", - g_MpGroupNameMenuItems, - NULL, - MENUDIALOGFLAG_LITERAL_TEXT, - NULL, +struct menudialogdef g_ManageSetupDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Manage Setup", + g_ManageSetupItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, }; - +// ----------------------- static size_t readStr(char *dst, char *src) { char *start = dst; @@ -181,50 +276,49 @@ static size_t readStr(char *dst, char *src) return dst - start; } -static void mpsetupfileRead(struct mpsetupfile *setupfile, u8 *buffer) +static void mpsetupDeserialize(struct mpsetupfile *setupfile, u8 *buffer) { BUF_READ(setupfile->version, buffer, u8); BUF_READ(setupfile->defaultsetup, buffer, u8); BUF_READ(setupfile->numsetups, buffer, u8); - BUF_READ(setupfile->numgroups, buffer, u8); for (int i = 0; i < setupfile->numsetups; ++i) { memcpy(setupfile->setups[i].bytes, buffer, MPSETUP_BLOCKSIZE); buffer += MPSETUP_BLOCKSIZE; } - - for (int i = 0; i < setupfile->numgroups; ++i) { - struct setupgroup *group = &setupfile->groups[i]; - BUF_READ(group->offset_index, buffer, u8); - buffer += readStr(group->name, buffer); - } } -void mpsetupfileWrite(u8 *buffer, struct mpsetupfile *setupfile, struct savebuffer *setup, u8 setupidx, u8 overwrite) +void mpsetupSerialize(u8 *buffer, struct mpsetupfile *setupfile) { BUF_WRITE(setupfile->version, buffer, u8); BUF_WRITE(setupfile->defaultsetup, buffer, u8); BUF_WRITE(setupfile->numsetups, buffer, u8); - BUF_WRITE(setupfile->numgroups, buffer, u8); for (int i = 0; i < setupfile->numsetups; ++i) { - if (i == setupidx) { - BUF_WRITEBLOCK(buffer, setup->bytes, MPSETUP_BLOCKSIZE); - - if (!overwrite) { - BUF_WRITEBLOCK(buffer, setupfile->setups[i].bytes, MPSETUP_BLOCKSIZE) - } - continue; - } - BUF_WRITEBLOCK(buffer, setupfile->setups[i].bytes, MPSETUP_BLOCKSIZE) } +} - for (int i = 0; i < setupfile->numgroups; ++i) { - struct setupgroup *group = &setupfile->groups[i]; - BUF_WRITE(group->offset_index, buffer, u8); - buffer += readStr(buffer, group->name); +static s32 mpsetupSaveFile(); +static s32 deleteSetup() +{ + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + for (int i = slotindex; i < g_MpSetupFile.numsetups - 1; ++i) { + u8* dst = g_MpSetupFile.setups[i].bytes; + u8* src = g_MpSetupFile.setups[i+1].bytes; + + memcpy(dst, src, MPSETUP_BLOCKSIZE); } + + if (g_MpCurrentSetup > slotindex) { + g_MpCurrentSetup--; + } + else if (g_MpCurrentSetup == slotindex) { + g_MpCurrentSetup = -1; + } + + g_MpSetupFile.numsetups--; + return mpsetupSaveFile(); } static FILE *openMpSetupFile(u8 mode) { @@ -248,7 +342,7 @@ static FILE *openMpSetupFile(u8 mode) { } if (f == NULL) { - sysLogPrintf(LOG_NOTE, "Unable to open mp setup file"); + sysLogPrintf(LOG_ERROR, "Unable to open mp setup file"); return NULL; } @@ -275,68 +369,21 @@ s32 mpsetupLoadFile() fread(filebuffer, 1, size, f); - mpsetupfileRead(&g_MpSetupFile, filebuffer); + mpsetupDeserialize(&g_MpSetupFile, filebuffer); if (g_MpSetupFile.defaultsetup != 0) { g_MpCurrentSetup = g_MpSetupFile.defaultsetup; } - // setup a default group if there is none - if (g_MpSetupFile.numgroups == 0) { - g_MpSetupFile.numgroups++; - strcpy(g_MpSetupFile.groups[0].name, "Default\n"); - g_MpSetupFile.groups[0].offset_index = 0; - g_MpCurrentSetup = -1; - } - fsFileFree(f); return 0; } -static s32 getNewSetupIndex() +static s32 mpsetupSaveFile() { - if (g_MpSetupFile.numsetups == 0) { - return 0; - } - - if (g_MpCurrentGroup == g_MpSetupFile.numgroups - 1) - return g_MpSetupFile.numsetups; - - return g_MpSetupFile.groups[g_MpCurrentGroup + 1].offset_index; -} - -static s32 getGroupIndex(s32 setupIndex) -{ - s32 index = 0; - struct setupgroup *groups = g_MpSetupFile.groups; - for (int i = 0; i < g_MpSetupFile.numgroups; ++i) { - if (setupIndex >= groups[i].offset_index) { - index = groups[i].offset_index; - } - } - - return index; -} - -s32 mpsetupSaveFile(const char *name, u8 overwrite) -{ - struct savebuffer setup; - - if (!overwrite) { - g_MpCurrentSetup = getNewSetupIndex(); - g_MpSetupFile.numsetups++; - - for (int i = g_MpCurrentGroup + 1; i < g_MpSetupFile.numgroups; ++i) { - g_MpSetupFile.groups[i].offset_index++; - } - } - - savebufferClear(&setup); - mpsetupfileSaveWad(&setup); - zeroFileBuffer(); - mpsetupfileWrite(filebuffer, &g_MpSetupFile, &setup, g_MpCurrentSetup, overwrite); + mpsetupSerialize(filebuffer, &g_MpSetupFile); FILE *f = openMpSetupFile('w'); if (f == NULL) { @@ -347,8 +394,24 @@ s32 mpsetupSaveFile(const char *name, u8 overwrite) fsFileFree(f); // reload the file to update in-memory structs - mpsetupLoadFile(); - return 0; + return mpsetupLoadFile(); +} + +s32 mpsetupSaveSetup(s32 slotindex) +{ + struct savebuffer setup; + + // request to add a new setup + if (slotindex == g_MpSetupFile.numsetups) { + g_MpCurrentSetup = g_MpSetupFile.numsetups++; + } + + savebufferClear(&setup); + mpsetupfileSaveWad(&setup); + + memcpy(g_MpSetupFile.setups[slotindex].bytes, &setup.bytes, MPSETUP_BLOCKSIZE); + + return mpsetupSaveFile(); } void mpsetupLoadSetup(s32 index) @@ -359,5 +422,4 @@ void mpsetupLoadSetup(s32 index) memcpy(&buffer.bytes, block->bytes, MPSETUP_BLOCKSIZE); mpsetupfileLoadWad(&buffer); g_MpCurrentSetup = index; - g_MpCurrentGroup = getGroupIndex(index); } diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index a94d32dd1..12c387cbc 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -33,7 +33,7 @@ struct menudialogdef g_MpChangeTeamNameMenuDialog; struct menudialogdef g_MpEditSimulantMenuDialog; struct menudialogdef g_MpSaveSetupNameMenuDialog; -extern struct menudialogdef g_MpSetupGroupsDialog; +extern struct menudialogdef g_MpManageSettingsDialog; extern struct menudialogdef g_FilemgrFileSavedMenuDialog; extern struct menudialogdef g_FilemgrErrorMenuDialog; @@ -470,7 +470,7 @@ MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *ite strcpy(g_MpSetup.name, name); break; case MENUOP_SET: - err = mpsetupSaveFile(name, false); + err = mpsetupSaveSetup(g_MpSetupFile.numsetups); if (!err) { menuPushDialog(&g_FilemgrFileSavedMenuDialog); } @@ -488,7 +488,8 @@ MenuItemHandlerResult menuhandlerMpSaveSetupOverwrite(s32 operation, struct menu { if (operation == MENUOP_SET) { menuPopDialog(); - mpsetupSaveFile(g_MpSetupFile.setups[g_MpCurrentSetup].bytes, true); + mpsetupSaveSetup(g_MpCurrentSetup); + menuPushDialog(&g_FilemgrFileSavedMenuDialog); } return 0; @@ -498,7 +499,7 @@ MenuItemHandlerResult menuhandlerMpSaveSetupCopy(s32 operation, struct menuitem { if (operation == MENUOP_SET) { menuPopDialog(); - menuPushDialog(&g_MpSetupGroupsDialog); + menuPushDialog(&g_MpSaveSetupNameMenuDialog); } return 0; @@ -2289,25 +2290,15 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * data->list.value = 0xfffff; break; case MENUOP_GETOPTGROUPCOUNT: - data->list.value = g_MpSetupFile.numgroups + 1; + data->list.value = 2; break; case MENUOP_GETOPTGROUPTEXT: if (data->list.value == 0) { return (uintptr_t)langGet(L_MPMENU_141); // "Presets" } - - return (uintptr_t)&g_MpSetupFile.groups[data->list.value - 1].name; - break; + return (uintptr_t)"Custom"; case MENUOP_GETGROUPSTARTINDEX: - if (data->list.value == 0) { - data->list.groupstartindex = 0; - } else { - data->list.groupstartindex = mpGetNumUnlockedPresets(); - - if (g_MpSetupFile.numgroups > 0) { - data->list.groupstartindex += g_MpSetupFile.groups[data->list.value - 1].offset_index; - } - } + data->list.groupstartindex = data->list.value == 0 ? 0 : mpGetNumUnlockedPresets(); break; case MENUOP_LISTITEMFOCUS: if (data->list.value < mpGetNumUnlockedPresets()) { @@ -2639,22 +2630,6 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { 0, NULL, }, - { - MENUITEMTYPE_SEPARATOR, - 0, - MENUITEMFLAG_00000002, - 0, - 0, - NULL, - }, - { - MENUITEMTYPE_LABEL, - 0, - MENUITEMFLAG_00000002 | MENUITEMFLAG_SELECTABLE_CENTRE, - L_MPMENU_414, // "Press the B Button to go back." - 0, - NULL, - }, { MENUITEMTYPE_END }, }; @@ -5039,7 +5014,7 @@ MenuItemHandlerResult menuhandlerMpSaveSettings(s32 operation, struct menuitem * { if (operation == MENUOP_SET) { if (g_MpCurrentSetup < 0) { - menuPushDialog(&g_MpSetupGroupsDialog); + menuPushDialog(&g_MpSaveSetupNameMenuDialog); } else { menuPushDialog(&g_MpSaveSetupExistsMenuDialog); @@ -5779,6 +5754,14 @@ struct menuitem g_MpAdvancedSetupMenuItems[] = { 0, NULL, }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LOCKABLEMAJOR, + (uintptr_t)"Manage Settings\n", + 0, + (void *)&g_MpManageSettingsDialog, + }, { MENUITEMTYPE_SELECTABLE, 0, diff --git a/src/include/data.h b/src/include/data.h index 8b71f7fdd..f576e606b 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -564,7 +564,6 @@ extern s32 g_MpWeaponRandomFilterNum; extern struct mpsetupfile g_MpSetupFile; extern s16 g_MpCurrentSetup; -extern s16 g_MpCurrentGroup; #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) diff --git a/src/include/types.h b/src/include/types.h index c5496911b..f5de55dd2 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -6160,18 +6160,11 @@ struct setupblock { u8 bytes[MPSETUP_BLOCKSIZE]; }; -struct setupgroup { - char name[MPSETUP_MAXGROUPNAME]; - u8 offset_index; -}; - struct mpsetupfile { u8 version; u8 defaultsetup; u8 numsetups; - u8 numgroups; struct setupblock setups[MPSETUP_MAXSETUPS]; - struct setupgroup groups[MPSETUP_MAXGROUPS]; }; #endif From 3d75365f2b4017ee394f3689d06ffcdf771c03e2 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Wed, 26 Mar 2025 23:43:42 +0800 Subject: [PATCH 03/32] mpsetups: increased max name length to 18 --- port/src/mpsetups.c | 8 ++++---- src/game/challenge.c | 3 +++ src/game/menuitem.c | 7 ++++--- src/{ => game}/mpconfigs.c | 0 src/game/mplayer/mplayer.c | 4 ++-- src/game/mplayer/setup.c | 11 +++++------ src/game/savebuffer.c | 29 ++++++++++++++++++++++------- src/include/constants.h | 5 ++--- src/include/game/savebuffer.h | 3 +++ src/include/types.h | 4 ++-- 10 files changed, 47 insertions(+), 27 deletions(-) rename src/{ => game}/mpconfigs.c (100%) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 1609007d2..b3d1aa063 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -35,7 +35,7 @@ s16 g_MpCurrentSetup = -1; memcpy(buf, src, size); \ buf += size; } -// #### Rename setup dialogue +// #### Rename setup dialog MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) { char *name = data->keyboard.string; @@ -80,10 +80,10 @@ struct menuitem g_RenameSetupItems[] = { #endif { MENUITEMTYPE_KEYBOARD, + 18, 0, 0, - 0, - 0, + 1, menuhandlerRenameSetup, }, { MENUITEMTYPE_END }, @@ -181,7 +181,7 @@ struct menuitem g_MpManageSettingsListItems[] = { MENUITEMTYPE_LIST, 0, 0, - 0x00000078, + 160, 0x00000042, mpSelectSettingHandler, }, diff --git a/src/game/challenge.c b/src/game/challenge.c index 8c738105f..2377e43ff 100644 --- a/src/game/challenge.c +++ b/src/game/challenge.c @@ -400,6 +400,8 @@ struct mpconfigfull *challengeLoadConfig(s32 confignum, u8 *buffer, s32 len) extern struct mpstrings EXT_SEG _mpstringsSSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsISegmentRomEnd; + extern struct mpconfig g_MpConfigs[]; + BTYPE banks[][2] = { { (BTYPE)REF_SEG _mpstringsESegmentRomStart, (BTYPE)REF_SEG _mpstringsESegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsJSegmentRomStart, (BTYPE)REF_SEG _mpstringsJSegmentRomEnd }, @@ -421,6 +423,7 @@ struct mpconfigfull *challengeLoadConfig(s32 confignum, u8 *buffer, s32 len) bank = banks[language_id][0]; loadedstrings = dmaExecWithAutoAlign(buffer2, bank + confignum * sizeof(struct mpstrings), sizeof(struct mpstrings)); + mpconfig->config = g_MpConfigs[confignum]; mpconfig->strings = *loadedstrings; return mpconfig; diff --git a/src/game/menuitem.c b/src/game/menuitem.c index 18f80c1a6..85fa18105 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -1529,6 +1529,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 inputs->start = false; } + u8 maxlen = item->param == 0 ? 10 : item->param; #ifndef PLATFORM_N64 if (g_MenuKeyboardPlayer == g_MpPlayerNum) { // match caps state to keyboard shift/caps if typing with keyboard @@ -1540,7 +1541,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 // handle text input s32 prevpos = strlen(kb->string); s32 pos = prevpos; - s32 result = inputTextHandler(kb->string, sizeof(kb->string), &pos, true); + s32 result = inputTextHandler(kb->string, maxlen-1, &pos, true); if (result == -1) { // cancel kb->row = 5; @@ -1617,7 +1618,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 s32 textwidth; s32 textheight; - if (kb->string[9] == '\0') { + if (kb->string[maxlen-1] == '\0') { // String is not full i = 0; @@ -1653,7 +1654,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 // Handle deleting if (delete && kb->string[0] != '\0') { s32 deleted = false; - s32 i = 10; + s32 i = maxlen; menuPlaySound(MENUSOUND_FOCUS); diff --git a/src/mpconfigs.c b/src/game/mpconfigs.c similarity index 100% rename from src/mpconfigs.c rename to src/game/mpconfigs.c diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 3d9381522..a9ea46998 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3918,7 +3918,7 @@ void mpsetupfileLoadWad(struct savebuffer *buffer) s32 i; s32 j; - savebufferReadString(buffer, g_MpSetup.name, false); + savebufferReadString_ext(buffer, g_MpSetup.name, false, MPSETUP_MAXSETUPS+1); savebufferReadBits(buffer, 4); g_MpSetup.stagenum = savebufferReadBits(buffer, 7); @@ -3973,7 +3973,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) s32 mpbodynum; s32 i; - func0f0d55a4(buffer, g_MpSetup.name); + func0f0d55a4_ext(buffer, g_MpSetup.name, MPSETUP_MAXSETUPS+1); for (i = 0; i < MAX_BOTS; i++) { if (g_MpSetup.chrslots & (1 << (i + 4))) { diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 12c387cbc..2f129d7b4 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -1098,10 +1098,10 @@ struct menuitem g_MpSaveSetupNameMenuItems[] = { #endif { MENUITEMTYPE_KEYBOARD, + MPSETUP_MAXNAME, 0, 0, - 0, - 0, + 1, menuhandlerMpSetupName, }, { MENUITEMTYPE_END }, @@ -2254,8 +2254,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * { switch (operation) { case MENUOP_GETOPTIONCOUNT: - data->list.value = mpGetNumUnlockedPresets(); - data->list.value += g_MpSetupFile.numsetups; + data->list.value = mpGetNumUnlockedPresets() + g_MpSetupFile.numsetups; break; case MENUOP_GETOPTIONTEXT: if (data->list.value < mpGetNumUnlockedPresets()) { @@ -2263,7 +2262,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * } if (g_MpSetupFile.numsetups > 0) { struct setupblock *block = &g_MpSetupFile.setups[data->list.value - mpGetNumUnlockedPresets()]; - func0f0d564c(block->bytes, g_StringPointer, false); + func0f0d564c_ext(block->bytes, g_StringPointer, false, MPSETUP_MAXSETUPS+1); return (uintptr_t)g_StringPointer; } break; @@ -2618,7 +2617,7 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { MENUITEMTYPE_LIST, 0, 0, - 0x00000078, + 160, 0x00000042, mpLoadSettingsMenuHandler, }, diff --git a/src/game/savebuffer.c b/src/game/savebuffer.c index f9d4705fd..0dc22dee5 100644 --- a/src/game/savebuffer.c +++ b/src/game/savebuffer.c @@ -481,13 +481,13 @@ void func0f0d54c4(struct savebuffer *buffer) * Read a zero-terminated string from the buffer and move the buffer's internal * pointer past the end of the string. */ -void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebreak) +void savebufferReadString_ext(struct savebuffer *buffer, char *dst, bool addlinebreak, u8 len) { bool foundnull = false; s32 index = 0; s32 i; - for (i = 0; i < 10; i++) { + for (i = 0; i < len; i++) { s32 byte = savebufferReadBits(buffer, 8); if (!foundnull) { @@ -509,12 +509,17 @@ void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebrea dst[index] = '\0'; } -void func0f0d55a4(struct savebuffer *buffer, char *src) +void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebreak) +{ + savebufferReadString_ext(buffer, dst, addlinebreak, 10); +} + +void func0f0d55a4_ext(struct savebuffer *buffer, char *src, u8 len) { bool done = false; s32 i; - for (i = 0; i < 10; i++) { + for (i = 0; i < len; i++) { if (!done) { if (src[i] == '\0') { done = true; @@ -532,12 +537,22 @@ void func0f0d55a4(struct savebuffer *buffer, char *src) } } -void func0f0d564c(u8 *data, char *dst, bool addlinebreak) +void func0f0d55a4(struct savebuffer *buffer, char *src) +{ + func0f0d55a4_ext(buffer, src, 10); +} + +void func0f0d564c_ext(u8 *data, char *dst, bool addlinebreak, u8 len) { struct savebuffer buffer; - savebufferWriteData(&buffer, data, 10); - savebufferReadString(&buffer, dst, addlinebreak); + savebufferWriteData(&buffer, data, len); + savebufferReadString_ext(&buffer, dst, addlinebreak, len); +} + +void func0f0d564c(u8 *data, char *dst, bool addlinebreak) +{ + func0f0d564c_ext(data, dst, addlinebreak, 10); } #if VERSION >= VERSION_NTSC_1_0 diff --git a/src/include/constants.h b/src/include/constants.h index 1baa3d998..e1c4b4ea3 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -4754,9 +4754,8 @@ enum weaponnum { #define EXTRA_SLEEP_TIME 1000LL // 100us -#define MPSETUP_MAXSETUPS 100 -#define MPSETUP_MAXGROUPS 100 -#define MPSETUP_MAXGROUPNAME 33 +#define MPSETUP_MAXSETUPS 128 +#define MPSETUP_MAXNAME 18 #define MPSETUP_BLOCKSIZE 64 #endif diff --git a/src/include/game/savebuffer.h b/src/include/game/savebuffer.h index 9529248ce..320708af2 100644 --- a/src/include/game/savebuffer.h +++ b/src/include/game/savebuffer.h @@ -17,8 +17,11 @@ u32 savebufferReadBits(struct savebuffer *buffer, s32 offset); void savebufferClear(struct savebuffer *buffer); void savebufferWriteData(struct savebuffer *buffer, u8 *data, u8 len); void func0f0d54c4(struct savebuffer *buffer); +void savebufferReadString_ext(struct savebuffer *buffer, char *dst, bool addlinebreak, u8 len); void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebreak); +void func0f0d55a4_ext(struct savebuffer *buffer, char *src, u8 len); void func0f0d55a4(struct savebuffer *buffer, char *src); +void func0f0d564c_ext(u8 *data, char *dst, bool addlinebreak, u8 len); void func0f0d564c(u8 *data, char *dst, bool addlinebreak); void func0f0d5690(u8 *dst, char *buffer); void savebufferWriteGuid(struct savebuffer *buffer, struct fileguid *guid); diff --git a/src/include/types.h b/src/include/types.h index f5de55dd2..61d8a96f5 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -3295,7 +3295,7 @@ struct menuitemdata_dropdown { }; struct menuitemdata_keyboard { - char string[11]; + char string[MPSETUP_MAXNAME+1]; s8 col; s8 row; u8 capslock : 1; // Pressed A on caps button @@ -4080,7 +4080,7 @@ struct missionconfig { }; struct mpsetup { - /*0x800acb88*/ char name[12]; + /*0x800acb88*/ char name[MPSETUP_MAXNAME+1]; /*0x800acb94*/ u32 options; /*0x800acb98*/ u8 scenario; /*0x800acb99*/ u8 stagenum; From dcef8deed8deb168efbddee3b82088c6c989ebc8 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Fri, 28 Mar 2025 21:08:18 +0800 Subject: [PATCH 04/32] mpsetups: import/export settings --- port/include/fs.h | 1 + port/include/mpsetups.h | 2 +- port/src/fs.c | 11 + port/src/mpsetups.c | 468 ++++++++++++++++++++++++++++++++++++--- src/game/mplayer/setup.c | 6 +- 5 files changed, 448 insertions(+), 40 deletions(-) diff --git a/port/include/fs.h b/port/include/fs.h index bfb21b3ec..05b91aa04 100644 --- a/port/include/fs.h +++ b/port/include/fs.h @@ -22,5 +22,6 @@ FILE *fsFileOpenRead(const char *name); void fsFileFree(FILE *f); const char *fsGetModDir(void); +s32 fsCreateDir(const char *path); #endif diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h index 5c7455c4d..c2a24056f 100644 --- a/port/include/mpsetups.h +++ b/port/include/mpsetups.h @@ -3,7 +3,7 @@ #include "types.h" -s32 mpsetupLoadFile(); +s32 mpsetupLoadCurrentSetupFile(); void mpsetupLoadSetup(s32 slotindex); s32 mpsetupSaveSetup(s32 slotindex); diff --git a/port/src/fs.c b/port/src/fs.c index 40335a3fa..7d4507aa7 100644 --- a/port/src/fs.c +++ b/port/src/fs.c @@ -11,6 +11,9 @@ #include "platform.h" #include "utils.h" #include "fs.h" +#ifdef PLATFORM_WIN32 +#include +#endif #define DEFAULT_BASEDIR_NAME "data" @@ -280,3 +283,11 @@ void fsFileFree(FILE *f) { fclose(f); } + +s32 fsCreateDir(const char *path) { +#ifdef PLATFORM_WIN32 + return _mkdir(fsFullPath(path)); +#else + return mkdir(fsFullPath(path), S_IRWXU | S_IRWXG) +#endif +} diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index b3d1aa063..27eb82c6b 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -21,13 +21,27 @@ MP Setup File Format [setup_n{64}] */ +#define MPSETUP_EXPORTDIR "$S/exported/" +#define MPSETUP_FILENAME "mpsetups" +#define MPSETUP_FILENAME_EXP "mpsetups-ext" + +#define MPSETUP_OP_DEFAULT 0 +#define MPSETUP_OP_IMPORT 1 +#define MPSETUP_OP_EXPORT 2 + +#define MPSETUP_IMPORT_OVERWRITE 0 +#define MPSETUP_IMPORT_ADD 1 + +#define MPSETUP_IMPORT_CONFLICT 0x1313 + extern struct menudialogdef g_MpSaveSetupNameMenuDialog; -#define MPSETUP_FILE "mpsetups.bin" u8 filebuffer[sizeof(struct mpsetupfile)]; struct mpsetupfile g_MpSetupFile; +struct mpsetupfile g_ImportMpSetupFile; s16 g_MpCurrentSetup = -1; +u64 g_MpImportExportFilter[2]; #define BUF_READ(dst, buf, type) { dst = *(type*)((buf)); buf += sizeof(type); } #define BUF_WRITE(src, buf, type) { *((type*)(buf)) = src; buf += sizeof(type); } @@ -35,6 +49,70 @@ s16 g_MpCurrentSetup = -1; memcpy(buf, src, size); \ buf += size; } +static inline void zeroFileBuffer() +{ + memset(filebuffer, 0, sizeof(struct mpsetupfile)); +} + +char g_StatusText[128]; +struct menuitem g_StatusOkMenuItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)g_StatusText, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE, + L_OPTIONS_347, // "OK" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_StatusOkDialog = { + MENUDIALOGTYPE_SUCCESS, + L_OPTIONS_345, // "Cool!" + g_StatusOkMenuItems, + NULL, + MENUDIALOGFLAG_DISABLEBANNER, + NULL, +}; + +struct menuitem g_StatusErrorMenuItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)g_StatusText, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE, + L_OPTIONS_347, // "OK" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_StatusErrorDialog = { + MENUDIALOGTYPE_DANGER, + L_OPTIONS_277, // "Failed" + g_StatusErrorMenuItems, + NULL, + MENUDIALOGFLAG_DISABLEBANNER, + NULL, +}; + // #### Rename setup dialog MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) { @@ -153,6 +231,194 @@ struct menudialogdef g_DeleteSetupDialog = { NULL, }; +// #### Import (or Export) settings dialog +s32 importMpSetupFile(u8 op, u8 skipOverlap); +s32 exportMpSetupFile(); +struct menudialogdef g_ManageSettingsDialog; +struct menudialogdef g_ImportOverrideDialog; +MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *labels[] = { + "Select All", + "Select None", + "Import", + "Export", + }; + + s32 op = g_Menus[g_MpPlayerNum].mpsetup.unke24; + + struct mpsetupfile *setupfile = op == MPSETUP_OP_IMPORT ? &g_ImportMpSetupFile : &g_MpSetupFile; + + u8 numsetups = setupfile->numsetups; + u8 numitems = numsetups + 3; + u8 bank = data->list.value < 64 ? 0 : 1; + u8 bit = data->list.value - bank * 64; + u8 hasselection = (g_MpImportExportFilter[0] | g_MpImportExportFilter[1]) != 0; + s32 err; + + switch (operation) { + case MENUOP_GETCOLOUR: { + u32 colour = data->list.unk04; + if (data->list.value == numitems - 1) { + data->list.unk04 = hasselection ? colour : (colour & 0xffffff00) | 0x66; + } + break; + } + case MENUOP_GETOPTIONCOUNT: + data->list.value = numitems; + break; + case MENUOP_GETOPTIONTEXT: + if (data->list.value < numsetups) { + return (uintptr_t) setupfile->setups[data->list.value].bytes; + } + else { + // creates an offset to select the "Export" or "Import" label for the last item + u8 ofs = (data->list.value == numitems - 1) ? op - 1 : 0; + return (intptr_t)labels[data->list.value - numsetups + ofs]; + } + case MENUOP_SET: { + if (data->list.value < setupfile->numsetups) { + g_MpImportExportFilter[bank] ^= (1 << bit); + } + else { + s32 index = data->list.value - setupfile->numsetups; + + switch (index) { + // Select All + case 0: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + break; + // Select None + case 1: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = 0; + break; + // Export (or Import) + case 2: + if (hasselection) { + err = op == MPSETUP_OP_IMPORT ? importMpSetupFile(0, false) : exportMpSetupFile(); + if (!err) { + menuPopDialog(); + if (op == MPSETUP_OP_IMPORT) { + // back to the 'Manage Settings' screen + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } + else { + sprintf(g_StatusText, "File %s.bin\nwritten to the folder 'exported'\n", MPSETUP_FILENAME_EXP); + menuPushDialog(&g_StatusOkDialog); + } + } + else { + if (err == MPSETUP_IMPORT_CONFLICT) { + menuPushDialog(&g_ImportOverrideDialog); + } + else { + menuPushDialog(&g_StatusErrorDialog); + } + } + } + break; + } + } + } + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0x000fffff; + break; + case MENUOP_GETLISTITEMCHECKBOX: { + if (data->list.value < numsetups) { + data->list.unk04 = (g_MpImportExportFilter[bank] & (1 << bit)); + } + break; + } + } + + return 0; +} + +struct menuitem g_ImportExportItems[] = { + { + MENUITEMTYPE_LIST, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LABEL_CUSTOMCOLOUR, + 140, + 0x0000004d, + menuhandlerImportOrExportSettings, + }, + { MENUITEMTYPE_END }, +}; + +char g_TitleImportExportDialog[20]; +struct menudialogdef g_ImportExportDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) g_TitleImportExportDialog, + g_ImportExportItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + + +// #### Manage Import/Export settings dialog +s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op); +MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_SET: + { + if (item->param == MPSETUP_OP_IMPORT) { + strcpy(g_TitleImportExportDialog, "Import Settings\n"); + if (fsFileSize("$S/" MPSETUP_FILENAME_EXP ".bin") < 0) { + sprintf(g_StatusText, + "No import file found.\n" + "Place the file %s.bin\n" + "Next to your %s.bin file\n", MPSETUP_FILENAME_EXP, MPSETUP_FILENAME); + menuPushDialog(&g_StatusErrorDialog); + return 0; + } + + mpsetupLoadFile(&g_ImportMpSetupFile, MPSETUP_OP_IMPORT); + } + else { + strcpy(g_TitleImportExportDialog, "Export Settings\n"); + } + g_Menus[g_MpPlayerNum].mpsetup.unke24 = item->param; + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + menuPushDialog(&g_ImportExportDialog); + } + } + + return 0; +} + +struct menuitem g_ManageImportExportItems[] = { + { + MENUITEMTYPE_SELECTABLE, + MPSETUP_OP_IMPORT, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Import Settings\n", + 0, + menuhandlerOpenImportExportDialog, + }, + { + MENUITEMTYPE_SELECTABLE, + MPSETUP_OP_EXPORT, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Export Settings\n", + 0, + menuhandlerOpenImportExportDialog, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ManageImportExportDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Import/Export\n", + g_ManageImportExportItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + // #### Manage settings dialog struct menudialogdef g_ManageSetupDialog; MenuItemHandlerResult mpSelectSettingHandler(s32 operation, struct menuitem *item, union handlerdata *data) @@ -189,13 +455,13 @@ struct menuitem g_MpManageSettingsListItems[] = { }; -struct menudialogdef g_MpManageSettingsDialog = { +struct menudialogdef g_ManageSettingsDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t) "Manage Settings\n", g_MpManageSettingsListItems, NULL, MENUDIALOGFLAG_LITERAL_TEXT, - NULL, + &g_ManageImportExportDialog, }; struct menudialogdef g_RenameSetupDialog; @@ -262,20 +528,61 @@ struct menudialogdef g_ManageSetupDialog = { NULL, }; -// ----------------------- -static size_t readStr(char *dst, char *src) +// #### Import: overwrite dialog +MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data) { - char *start = dst; - while (*src) { - *dst = *src; - dst++; - src++; + if (operation == MENUOP_SET) { + s32 err = importMpSetupFile(item->param, true); + if (!err) { + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } + else { + menuPushDialog(&g_StatusErrorDialog); + } } - *dst = '\0'; - dst++; - return dst - start; + + return 0; } +struct menuitem g_ImportOverrideItems[] = { + { + MENUITEMTYPE_LABEL, + 0, +// MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_SMALLFONT, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t) "How to resolve setups\nwith the same name?\n", + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + MPSETUP_IMPORT_ADD, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + (uintptr_t) "Add\n", + 0, + menuhandlerImportAction, + }, + { + MENUITEMTYPE_SELECTABLE, + MPSETUP_IMPORT_OVERWRITE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + (uintptr_t) "Overwrite\n", + 0, + menuhandlerImportAction, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ImportOverrideDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Name Conflicts\n", + g_ImportOverrideItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; +// -------------------------------------------------------- static void mpsetupDeserialize(struct mpsetupfile *setupfile, u8 *buffer) { BUF_READ(setupfile->version, buffer, u8); @@ -299,7 +606,7 @@ void mpsetupSerialize(u8 *buffer, struct mpsetupfile *setupfile) } } -static s32 mpsetupSaveFile(); +static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile); static s32 deleteSetup() { s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; @@ -318,23 +625,39 @@ static s32 deleteSetup() } g_MpSetupFile.numsetups--; - return mpsetupSaveFile(); + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); } -static FILE *openMpSetupFile(u8 mode) { +static FILE *openMpSetupFile(u8 mode, u8 op) { + const char *filename = fsFullPath("$S/" MPSETUP_FILENAME ".bin"); + + if (op == MPSETUP_OP_EXPORT) { + // create export directory if it doesn't exist + if (fsFileSize(MPSETUP_EXPORTDIR) < 0) { + if (fsCreateDir(MPSETUP_EXPORTDIR) != 0) { + return NULL; + } + } + filename = fsFullPath(MPSETUP_EXPORTDIR MPSETUP_FILENAME_EXP ".bin"); + } + else if (op == MPSETUP_OP_IMPORT) { + // same name as export but different folder + filename = fsFullPath("$S/" MPSETUP_FILENAME_EXP ".bin"); + } + FILE *f; - if (fsFileSize(MPSETUP_FILE) < 0) { + if (fsFileSize(filename) < 0) { // setup file doesn't exist: create one - f = fsFileOpenWrite(MPSETUP_FILE); + f = fsFileOpenWrite(filename); fsFileFree(f); } if (mode == 'r') { - f = fsFileOpenRead(MPSETUP_FILE); + f = fsFileOpenRead(filename); } else if (mode == 'w') { - f = fsFileOpenWrite(MPSETUP_FILE); + f = fsFileOpenWrite(filename); } else { sysLogPrintf(LOG_ERROR, "openMpSetupFile: invalid file mode"); @@ -349,14 +672,9 @@ static FILE *openMpSetupFile(u8 mode) { return f; } -void zeroFileBuffer() +s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) { - memset(filebuffer, 0, sizeof(struct mpsetupfile)); -} - -s32 mpsetupLoadFile() -{ - FILE *f = openMpSetupFile('r'); + FILE *f = openMpSetupFile('r', op); if (f == NULL) { return -1; } @@ -369,10 +687,10 @@ s32 mpsetupLoadFile() fread(filebuffer, 1, size, f); - mpsetupDeserialize(&g_MpSetupFile, filebuffer); + mpsetupDeserialize(setupfile, filebuffer); - if (g_MpSetupFile.defaultsetup != 0) { - g_MpCurrentSetup = g_MpSetupFile.defaultsetup; + if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup != 0) { + g_MpCurrentSetup = setupfile->defaultsetup; } fsFileFree(f); @@ -380,21 +698,99 @@ s32 mpsetupLoadFile() return 0; } -static s32 mpsetupSaveFile() +s32 mpsetupLoadCurrentSetupFile() +{ + return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); +} + +s32 importMpSetupFile(u8 op, u8 skipOverlap) +{ + if (!skipOverlap) { + // check for names overlap + u8 overlap = false; + for (int i = 0; i < g_ImportMpSetupFile.numsetups; ++i) { + for (int j = 0; j < g_MpSetupFile.numsetups; ++j) { + if (strcmp(g_ImportMpSetupFile.setups[i].bytes, g_MpSetupFile.setups[j].bytes) == 0) { + overlap = true; + return MPSETUP_IMPORT_CONFLICT; + } + } + } + } + + for (int i = 0; i < g_ImportMpSetupFile.numsetups; ++i) { + s16 overlapIdx = -1; + for (int j = g_MpSetupFile.numsetups-1; j >= 0; --j) { + if (strcmp(g_ImportMpSetupFile.setups[i].bytes, g_MpSetupFile.setups[j].bytes) == 0) { + overlapIdx = j; + break; + } + } + + s16 importIdx = overlapIdx; + if (overlapIdx < 0 || op == MPSETUP_IMPORT_ADD) { + if (g_MpSetupFile.numsetups == MPSETUP_MAXSETUPS) { + sprintf(g_StatusText, "Number of setups exceeds %d\n", MPSETUP_MAXSETUPS); + return -1; + } + importIdx = g_MpSetupFile.numsetups++; + } + + memcpy(g_MpSetupFile.setups[importIdx].bytes, g_ImportMpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE); + } + + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); +} + +s32 exportMpSetupFile() +{ + struct mpsetupfile expMpSetupFile; + expMpSetupFile.numsetups = 0; + + u8 maxsetups = g_MpImportExportFilter[1] == 0 ? 64 : 128; + maxsetups = MIN(maxsetups, g_MpSetupFile.numsetups); + for (int i = 0; i < maxsetups; ++i) { + u8 bank = i < 64 ? 0 : 1; + u8 bit = i - bank * 64; + if (g_MpImportExportFilter[bank] & (1 << bit)) { + u8 n = expMpSetupFile.numsetups; + expMpSetupFile.numsetups++; + char *name = g_MpSetupFile.setups[i].bytes; + sysLogPrintf(LOG_NOTE, "%s", name); + memcpy(expMpSetupFile.setups[n].bytes, g_MpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE); + expMpSetupFile.numsetups = n + 1; + } + } + + s32 err = mpsetupSaveFile(MPSETUP_OP_EXPORT, &expMpSetupFile); + if (err) { + sprintf(g_StatusText, "Unable to write\nsetup file\n"); + } + + return err; +} + +static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) { zeroFileBuffer(); - mpsetupSerialize(filebuffer, &g_MpSetupFile); + mpsetupSerialize(filebuffer, setupfile); - FILE *f = openMpSetupFile('w'); + FILE *f = openMpSetupFile('w', op); if (f == NULL) { return -1; } - fwrite(filebuffer, sizeof(struct mpsetupfile), 1, f); + size_t nwritten = fwrite(filebuffer, sizeof(struct mpsetupfile), 1, f); + if (nwritten < 1) { + fsFileFree(f); + sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file"); + return -1; + } + fsFileFree(f); // reload the file to update in-memory structs - return mpsetupLoadFile(); + return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); } s32 mpsetupSaveSetup(s32 slotindex) @@ -411,7 +807,7 @@ s32 mpsetupSaveSetup(s32 slotindex) memcpy(g_MpSetupFile.setups[slotindex].bytes, &setup.bytes, MPSETUP_BLOCKSIZE); - return mpsetupSaveFile(); + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); } void mpsetupLoadSetup(s32 index) diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 2f129d7b4..694b72649 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -33,7 +33,7 @@ struct menudialogdef g_MpChangeTeamNameMenuDialog; struct menudialogdef g_MpEditSimulantMenuDialog; struct menudialogdef g_MpSaveSetupNameMenuDialog; -extern struct menudialogdef g_MpManageSettingsDialog; +extern struct menudialogdef g_ManageSettingsDialog; extern struct menudialogdef g_FilemgrFileSavedMenuDialog; extern struct menudialogdef g_FilemgrErrorMenuDialog; @@ -5360,7 +5360,7 @@ MenuDialogHandlerResult menudialogCombatSimulator(s32 operation, struct menudial g_Vars.waitingtojoin[3] = false; // load the setup file when entering the Combat Simulator - mpsetupLoadFile(); + mpsetupLoadCurrentSetupFile(); } if (g_Menus[g_MpPlayerNum].curdialog @@ -5759,7 +5759,7 @@ struct menuitem g_MpAdvancedSetupMenuItems[] = { MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LOCKABLEMAJOR, (uintptr_t)"Manage Settings\n", 0, - (void *)&g_MpManageSettingsDialog, + (void *)&g_ManageSettingsDialog, }, { MENUITEMTYPE_SELECTABLE, From 9e19055348eb07383ffccb483ac1d892f091c516 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Sat, 29 Mar 2025 04:17:36 +0800 Subject: [PATCH 05/32] mpsetups: fixed bugs caused by using a wrong constant + fixed the setup marquee display --- src/game/mplayer/mplayer.c | 8 ++++---- src/game/mplayer/scenarios.c | 2 +- src/game/mplayer/setup.c | 9 ++++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index a9ea46998..c324afb47 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3918,7 +3918,7 @@ void mpsetupfileLoadWad(struct savebuffer *buffer) s32 i; s32 j; - savebufferReadString_ext(buffer, g_MpSetup.name, false, MPSETUP_MAXSETUPS+1); + savebufferReadString_ext(buffer, g_MpSetup.name, false, MPSETUP_MAXNAME+1); savebufferReadBits(buffer, 4); g_MpSetup.stagenum = savebufferReadBits(buffer, 7); @@ -3973,7 +3973,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) s32 mpbodynum; s32 i; - func0f0d55a4_ext(buffer, g_MpSetup.name, MPSETUP_MAXSETUPS+1); + func0f0d55a4_ext(buffer, g_MpSetup.name, MPSETUP_MAXNAME+1); for (i = 0; i < MAX_BOTS; i++) { if (g_MpSetup.chrslots & (1 << (i + 4))) { @@ -4033,9 +4033,9 @@ void mpsetupfileGetOverview(char *arg0, char *filename, u16 *numsims, u16 *stage { struct savebuffer buffer; - savebufferWriteData(&buffer, arg0, 15); + savebufferWriteData(&buffer, arg0, 22); - savebufferReadString(&buffer, filename, 0); + savebufferReadString_ext(&buffer, filename, 0, MPSETUP_MAXNAME+1); *numsims = savebufferReadBits(&buffer, 4); *stagenum = savebufferReadBits(&buffer, 7); diff --git a/src/game/mplayer/scenarios.c b/src/game/mplayer/scenarios.c index f7a504382..0c4df1f51 100644 --- a/src/game/mplayer/scenarios.c +++ b/src/game/mplayer/scenarios.c @@ -421,7 +421,7 @@ void scenarioReadSave(struct savebuffer *buffer) if (g_MpScenarios[g_MpSetup.scenario].readsavefunc) { g_MpScenarios[g_MpSetup.scenario].readsavefunc(buffer); } else { - savebufferReadBits(buffer, 8); + savebufferReadBits(buffer, 32); } } diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 694b72649..02ec00730 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -2262,7 +2262,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * } if (g_MpSetupFile.numsetups > 0) { struct setupblock *block = &g_MpSetupFile.setups[data->list.value - mpGetNumUnlockedPresets()]; - func0f0d564c_ext(block->bytes, g_StringPointer, false, MPSETUP_MAXSETUPS+1); + func0f0d564c_ext(block->bytes, g_StringPointer, false, MPSETUP_MAXNAME+1); return (uintptr_t)g_StringPointer; } break; @@ -2303,6 +2303,9 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * if (data->list.value < mpGetNumUnlockedPresets()) { g_Menus[g_MpPlayerNum].mpsetup.slotindex = 0xffff; } + else { + g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value - mpGetNumUnlockedPresets(); + } break; } @@ -2318,14 +2321,14 @@ char *mpMenuTextMpconfigMarquee(struct menuitem *item) s32 arenanum; s32 i; - if (g_Menus[g_MpPlayerNum].mpsetup.slotindex < 0xffff && g_FileLists[1]) { + if (g_Menus[g_MpPlayerNum].mpsetup.slotindex < 0xffff && g_MpSetupFile.numsetups > 0) { #if VERSION >= VERSION_NTSC_1_0 arenanum = -1; #else arenanum = 0; #endif - mpsetupfileGetOverview(g_FileLists[1]->files[g_Menus[g_MpPlayerNum].mpsetup.slotindex].name, + mpsetupfileGetOverview(g_MpSetupFile.setups[g_Menus[g_MpPlayerNum].mpsetup.slotindex].bytes, filename, &numsims, &stagenum, &scenarionum); for (i = 0; i < ARRAYCOUNT(g_MpArenas); i++) { From a1d6916871be190e61cedc15171cf10689ddfe8c Mon Sep 17 00:00:00 2001 From: Raf <> Date: Sat, 29 Mar 2025 04:19:46 +0800 Subject: [PATCH 06/32] mpsetups: manage default setup + save the selected random weapons --- port/include/mpsetups.h | 3 +- port/src/mpsetups.c | 72 ++++++++++++++++++++++++++++++++------ src/game/mplayer/mplayer.c | 23 ++++++++++++ src/game/mplayer/setup.c | 2 +- 4 files changed, 87 insertions(+), 13 deletions(-) diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h index c2a24056f..9a2b2f58a 100644 --- a/port/include/mpsetups.h +++ b/port/include/mpsetups.h @@ -3,7 +3,8 @@ #include "types.h" -s32 mpsetupLoadCurrentSetupFile(); +s32 mpsetupLoadCurrentFile(); +s32 mpsetupSaveCurrentFile(); void mpsetupLoadSetup(s32 slotindex); s32 mpsetupSaveSetup(s32 slotindex); diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 27eb82c6b..a1c9d658d 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -421,9 +421,17 @@ struct menudialogdef g_ManageImportExportDialog = { // #### Manage settings dialog struct menudialogdef g_ManageSetupDialog; -MenuItemHandlerResult mpSelectSettingHandler(s32 operation, struct menuitem *item, union handlerdata *data) +char g_LabelSetDefault[20] = "Set Default\n"; +MenuItemHandlerResult mpSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { + case MENUOP_GETCOLOUR: { + u32 colour = data->list.unk04; + if (data->list.value + 1 == g_MpSetupFile.defaultsetup) { + data->list.unk04 = (colour & 0xffff0000) | 0xff; + } + break; + } case MENUOP_GETOPTIONCOUNT: data->list.value = g_MpSetupFile.numsetups; break; @@ -432,6 +440,12 @@ MenuItemHandlerResult mpSelectSettingHandler(s32 operation, struct menuitem *ite case MENUOP_SET: { g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value; + if (data->list.value == g_MpSetupFile.defaultsetup - 1) { + strcpy(g_LabelSetDefault, "Clear Default\n"); + } + else { + strcpy(g_LabelSetDefault, "Set Default\n"); + } menuPushDialog(&g_ManageSetupDialog); } case MENUOP_GETSELECTEDINDEX: @@ -446,10 +460,10 @@ struct menuitem g_MpManageSettingsListItems[] = { { MENUITEMTYPE_LIST, 0, - 0, + MENUITEMFLAG_LABEL_CUSTOMCOLOUR, 160, 0x00000042, - mpSelectSettingHandler, + mpSelectSetupHandler, }, { MENUITEMTYPE_END }, @@ -483,6 +497,25 @@ static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuit return 0; } +static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + if (selected == g_MpSetupFile.defaultsetup - 1) { + strcpy(g_LabelSetDefault, "Set Default\n"); + g_MpSetupFile.defaultsetup = -1; + } + else { + strcpy(g_LabelSetDefault, "Clear Default\n"); + g_MpSetupFile.defaultsetup = selected + 1; + } + + mpsetupSaveCurrentFile(); + } + + return 0; +} + struct menuitem g_ManageSetupItems[] = { { MENUITEMTYPE_SELECTABLE, @@ -500,6 +533,14 @@ struct menuitem g_ManageSetupItems[] = { 0, menuhandlerSetupDelete, }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)g_LabelSetDefault, + 0, + menuhandlerSetupSetDefault, + }, { MENUITEMTYPE_SEPARATOR, 0, @@ -624,8 +665,12 @@ static s32 deleteSetup() g_MpCurrentSetup = -1; } + if (g_MpSetupFile.defaultsetup == slotindex + 1) { + g_MpSetupFile.defaultsetup = 0; + } + g_MpSetupFile.numsetups--; - return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); + return mpsetupSaveCurrentFile(); } static FILE *openMpSetupFile(u8 mode, u8 op) { @@ -689,8 +734,8 @@ s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) mpsetupDeserialize(setupfile, filebuffer); - if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup != 0) { - g_MpCurrentSetup = setupfile->defaultsetup; + if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup > 0) { + mpsetupLoadSetup(setupfile->defaultsetup - 1); } fsFileFree(f); @@ -698,11 +743,16 @@ s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) return 0; } -s32 mpsetupLoadCurrentSetupFile() +s32 mpsetupLoadCurrentFile() { return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); } +s32 mpsetupSaveCurrentFile() +{ + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); +} + s32 importMpSetupFile(u8 op, u8 skipOverlap) { if (!skipOverlap) { @@ -739,7 +789,7 @@ s32 importMpSetupFile(u8 op, u8 skipOverlap) memcpy(g_MpSetupFile.setups[importIdx].bytes, g_ImportMpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE); } - return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); + return mpsetupSaveCurrentFile(); } s32 exportMpSetupFile() @@ -790,7 +840,7 @@ static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) fsFileFree(f); // reload the file to update in-memory structs - return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); + return mpsetupLoadCurrentFile(); } s32 mpsetupSaveSetup(s32 slotindex) @@ -805,9 +855,9 @@ s32 mpsetupSaveSetup(s32 slotindex) savebufferClear(&setup); mpsetupfileSaveWad(&setup); - memcpy(g_MpSetupFile.setups[slotindex].bytes, &setup.bytes, MPSETUP_BLOCKSIZE); + memcpy(g_MpSetupFile.setups[slotindex].bytes, setup.bytes, MPSETUP_BLOCKSIZE); - return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); + return mpsetupSaveCurrentFile(); } void mpsetupLoadSetup(s32 index) diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index c324afb47..a8629c8e4 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3913,6 +3913,23 @@ void mp0f18dec4(s32 slot) #endif } +static u64 packWeaponSetRandomFilters() +{ + u64 packed = 0; + for (int i = 0; i < NUM_MPWEAPONS; ++i) { + packed |= g_MpWeaponSetRandomFilters[i] != 0 ? (1 << i) : 0; + } + + return packed; +} + +static void unpackWeaponSetRandomFilters(u64 packed) +{ + for (int i = 0; i < NUM_MPWEAPONS; ++i) { + g_MpWeaponSetRandomFilters[i] = (packed & (1 << i)) >> i; + } +} + void mpsetupfileLoadWad(struct savebuffer *buffer) { s32 i; @@ -3954,6 +3971,9 @@ void mpsetupfileLoadWad(struct savebuffer *buffer) g_MpSetup.weapons[i] = savebufferReadBits(buffer, 7); } + u64 wpnRndPacked = savebufferReadBits(buffer, 64); + unpackWeaponSetRandomFilters(wpnRndPacked); + func0f18913c(); g_MpSetup.timelimit = savebufferReadBits(buffer, 6); @@ -4020,6 +4040,9 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) savebufferOr(buffer, g_MpSetup.weapons[i], 7); } + u64 wpnRndPacked = packWeaponSetRandomFilters(); + savebufferOr(buffer, wpnRndPacked, 64); + savebufferOr(buffer, g_MpSetup.timelimit, 6); savebufferOr(buffer, g_MpSetup.scorelimit, 7); savebufferOr(buffer, g_MpSetup.teamscorelimit, 9); diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 02ec00730..a70087eda 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -5363,7 +5363,7 @@ MenuDialogHandlerResult menudialogCombatSimulator(s32 operation, struct menudial g_Vars.waitingtojoin[3] = false; // load the setup file when entering the Combat Simulator - mpsetupLoadCurrentSetupFile(); + mpsetupLoadCurrentFile(); } if (g_Menus[g_MpPlayerNum].curdialog From 4d4fa8f68104b50d7071716b7ef66fb0cc3487a5 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Sun, 30 Mar 2025 23:21:52 +0800 Subject: [PATCH 07/32] savebuffer: extended read/write to accept 64 bits, renamed a function, removed an unused function --- src/game/bossfile.c | 6 +----- src/game/gamefile.c | 6 +----- src/game/mplayer/mplayer.c | 14 +++++-------- src/game/savebuffer.c | 33 ++++++++++++------------------ src/include/game/mplayer/mplayer.h | 2 +- src/include/game/savebuffer.h | 11 +++++----- 6 files changed, 26 insertions(+), 46 deletions(-) diff --git a/src/game/bossfile.c b/src/game/bossfile.c index 9c8e99705..0a9690bed 100644 --- a/src/game/bossfile.c +++ b/src/game/bossfile.c @@ -138,8 +138,6 @@ void bossfileLoad(void) g_BossFile.usingmultipletunes = savebufferReadBits(&buffer, 1); g_AltTitleUnlocked = savebufferReadBits(&buffer, 1); g_AltTitleEnabled = savebufferReadBits(&buffer, 1); - - func0f0d54c4(&buffer); } if (failed) { @@ -168,7 +166,7 @@ void bossfileSave(void) savebufferOr(&buffer, g_Vars.language, 4); for (i = 0; i < ARRAYCOUNT(g_BossFile.teamnames); i++) { - func0f0d55a4(&buffer, g_BossFile.teamnames[i]); + savebufferWriteString(&buffer, g_BossFile.teamnames[i]); } if (g_BossFile.tracknum == -1) { @@ -185,8 +183,6 @@ void bossfileSave(void) savebufferOr(&buffer, g_AltTitleUnlocked, 1); savebufferOr(&buffer, g_AltTitleEnabled, 1); - func0f0d54c4(&buffer); - fileid = bossfileFindFileId(); if (fileid == 0) { diff --git a/src/game/gamefile.c b/src/game/gamefile.c index e17411d6b..ed6ac2c98 100644 --- a/src/game/gamefile.c +++ b/src/game/gamefile.c @@ -395,8 +395,6 @@ s32 gamefileLoad(s32 device) frSetWeaponFound(WEAPON_REMOTEMINE); } #endif - - func0f0d54c4(&buffer); gamefileApplyOptions(&g_GameFile); return 0; @@ -499,7 +497,7 @@ s32 gamefileSave(s32 device, s32 fileid, u16 deviceserial) if (device >= 0) { savebufferClear(&buffer); - func0f0d55a4(&buffer, g_GameFile.name); + savebufferWriteString(&buffer, g_GameFile.name); savebufferOr(&buffer, g_GameFile.thumbnail, 5); savebufferOr(&buffer, g_GameFile.totaltime, 32); @@ -556,8 +554,6 @@ s32 gamefileSave(s32 device, s32 fileid, u16 deviceserial) savebufferOr(&buffer, g_GameFile.weaponsfound[i], 8); } - func0f0d54c4(&buffer); - ret = pakSaveAtGuid(device, fileid, PAKFILETYPE_GAME, buffer.bytes, &newfileid, 0); g_FilemgrLastPakError = ret; diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index a8629c8e4..69facd19f 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3533,7 +3533,7 @@ void mpplayerfileSaveWad(s32 playernum, struct savebuffer *buffer) s32 j; u32 stack; - func0f0d55a4(buffer, g_PlayerConfigsArray[playernum].base.name); + savebufferWriteString(buffer, g_PlayerConfigsArray[playernum].base.name); if (g_PlayerConfigsArray[playernum].time > 0x0fffffff) { // over 3106 days g_PlayerConfigsArray[playernum].time = 0x0fffffff; @@ -3684,8 +3684,6 @@ s32 mpplayerfileSave(s32 playernum, s32 device, s32 fileid, u16 deviceserial) if (device >= 0) { savebufferClear(&buffer); mpplayerfileSaveWad(playernum, &buffer); - func0f0d54c4(&buffer); - var80075bd0[2] = true; ret = pakSaveAtGuid(device, fileid, PAKFILETYPE_MPPLAYER, buffer.bytes, &newfileid, 0); @@ -3718,8 +3716,6 @@ s32 mpplayerfileLoad(s32 playernum, s32 device, s32 fileid, u16 deviceserial) g_PlayerConfigsArray[playernum].fileguid.deviceserial = deviceserial; mpplayerfileLoadWad(playernum, &buffer, 1); - func0f0d54c4(&buffer); - g_PlayerConfigsArray[playernum].handicap = 0x80; return 0; } @@ -3917,7 +3913,7 @@ static u64 packWeaponSetRandomFilters() { u64 packed = 0; for (int i = 0; i < NUM_MPWEAPONS; ++i) { - packed |= g_MpWeaponSetRandomFilters[i] != 0 ? (1 << i) : 0; + packed |= g_MpWeaponSetRandomFilters[i] != 0 ? (1LL << i) : 0; } return packed; @@ -3926,11 +3922,11 @@ static u64 packWeaponSetRandomFilters() static void unpackWeaponSetRandomFilters(u64 packed) { for (int i = 0; i < NUM_MPWEAPONS; ++i) { - g_MpWeaponSetRandomFilters[i] = (packed & (1 << i)) >> i; + g_MpWeaponSetRandomFilters[i] = (packed & (1LL << i)) != 0; } } -void mpsetupfileLoadWad(struct savebuffer *buffer) +void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version) { s32 i; s32 j; @@ -3993,7 +3989,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) s32 mpbodynum; s32 i; - func0f0d55a4_ext(buffer, g_MpSetup.name, MPSETUP_MAXNAME+1); + savebufferWriteString_ext(buffer, g_MpSetup.name, MPSETUP_MAXNAME + 1); for (i = 0; i < MAX_BOTS; i++) { if (g_MpSetup.chrslots & (1 << (i + 4))) { diff --git a/src/game/savebuffer.c b/src/game/savebuffer.c index 0dc22dee5..6a9bcf581 100644 --- a/src/game/savebuffer.c +++ b/src/game/savebuffer.c @@ -374,13 +374,13 @@ Gfx *menugfxDrawPlane(Gfx *gdl, s32 x1, s32 y1, s32 x2, s32 y2, u32 colour1, u32 /** * Write the specified amount of bits to the buffer, advancing the internal pointer. * - * numbits is expected to be 32 or less. + * numbits is expected to be 64 or less. * * This function only sets bits to on and does not unset them. */ -void savebufferOr(struct savebuffer *buffer, u32 value, s32 numbits) +void savebufferOr(struct savebuffer *buffer, u64 value, s32 numbits) { - u32 bit = 1 << (numbits - 1); + u64 bit = 1LL << (numbits - 1LL); for (; bit; bit >>= 1) { if (bit & value) { @@ -399,11 +399,11 @@ void savebufferOr(struct savebuffer *buffer, u32 value, s32 numbits) /** * Write the specified amount of bits to the buffer, advancing the internal pointer. * - * numbits is expected to be 32 or less. + * numbits is expected to be 64 or less. */ -void savebufferWriteBits(struct savebuffer *buffer, u32 value, s32 numbits, u8 *dst) +void savebufferWriteBits(struct savebuffer *buffer, u64 value, s32 numbits, u8 *dst) { - u32 bit = 1 << (numbits - 1); + u64 bit = 1LL << (numbits - 1); for (; bit; bit >>= 1) { s32 bitindex = buffer->bitpos % 8; @@ -425,12 +425,12 @@ void savebufferWriteBits(struct savebuffer *buffer, u32 value, s32 numbits, u8 * * Read the specified amount of bits from the buffer and return it as an * integer, advancing the internal pointer. * - * numbits is expected to be 32 or less. + * numbits is expected to be 64 or less. */ -u32 savebufferReadBits(struct savebuffer *buffer, s32 numbits) +u64 savebufferReadBits(struct savebuffer *buffer, s32 numbits) { - u32 bit = 1 << (numbits - 1); - u32 value = 0; + u64 bit = 1LL << (numbits - 1); + u64 value = 0; for (; bit; bit >>= 1) { s32 bitindex = buffer->bitpos % 8; @@ -470,13 +470,6 @@ void savebufferWriteData(struct savebuffer *buffer, u8 *data, u8 len) } } -void func0f0d54c4(struct savebuffer *buffer) -{ - s32 tmp = buffer->bitpos; - - if (tmp / 8 && buffer->bitpos); -} - /** * Read a zero-terminated string from the buffer and move the buffer's internal * pointer past the end of the string. @@ -514,7 +507,7 @@ void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebrea savebufferReadString_ext(buffer, dst, addlinebreak, 10); } -void func0f0d55a4_ext(struct savebuffer *buffer, char *src, u8 len) +void savebufferWriteString_ext(struct savebuffer *buffer, char *src, u8 len) { bool done = false; s32 i; @@ -537,9 +530,9 @@ void func0f0d55a4_ext(struct savebuffer *buffer, char *src, u8 len) } } -void func0f0d55a4(struct savebuffer *buffer, char *src) +void savebufferWriteString(struct savebuffer *buffer, char *src) { - func0f0d55a4_ext(buffer, src, 10); + savebufferWriteString_ext(buffer, src, 10); } void func0f0d564c_ext(u8 *data, char *dst, bool addlinebreak, u8 len) diff --git a/src/include/game/mplayer/mplayer.h b/src/include/game/mplayer/mplayer.h index b097e5a39..a5b37e34f 100644 --- a/src/include/game/mplayer/mplayer.h +++ b/src/include/game/mplayer/mplayer.h @@ -118,7 +118,7 @@ s32 mpGetNumUnlockedPresets(void); char *mpGetPresetNameBySlot(s32 slot); void mpApplyConfig(struct mpconfigfull *mpconfig); void mp0f18dec4(s32 arg0); -void mpsetupfileLoadWad(struct savebuffer *buffer); +void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version); void mpsetupfileSaveWad(struct savebuffer *buffer); void mpsetupfileGetOverview(char *arg0, char *filename, u16 *numsims, u16 *stagenum, u16 *scenarionum); s32 mpsetupfileSave(s32 device, s32 filenum, u16 deviceserial); diff --git a/src/include/game/savebuffer.h b/src/include/game/savebuffer.h index 320708af2..28e4b6cb2 100644 --- a/src/include/game/savebuffer.h +++ b/src/include/game/savebuffer.h @@ -11,16 +11,15 @@ Gfx *func0f0d49c8(Gfx *gdl); Gfx *func0f0d4a3c(Gfx *gdl, s32 arg1); Gfx *func0f0d4c80(Gfx *gdl); Gfx *menugfxDrawPlane(Gfx *gdl, s32 x1, s32 y1, s32 x2, s32 y2, u32 colour1, u32 colour2, s32 type); -void savebufferOr(struct savebuffer *buffer, u32 arg1, s32 arg2); -void savebufferWriteBits(struct savebuffer *buffer, u32 value, s32 numbits, u8 *dst); -u32 savebufferReadBits(struct savebuffer *buffer, s32 offset); +void savebufferOr(struct savebuffer *buffer, u64 arg1, s32 arg2); +void savebufferWriteBits(struct savebuffer *buffer, u64 value, s32 numbits, u8 *dst); +u64 savebufferReadBits(struct savebuffer *buffer, s32 offset); void savebufferClear(struct savebuffer *buffer); void savebufferWriteData(struct savebuffer *buffer, u8 *data, u8 len); -void func0f0d54c4(struct savebuffer *buffer); void savebufferReadString_ext(struct savebuffer *buffer, char *dst, bool addlinebreak, u8 len); void savebufferReadString(struct savebuffer *buffer, char *dst, bool addlinebreak); -void func0f0d55a4_ext(struct savebuffer *buffer, char *src, u8 len); -void func0f0d55a4(struct savebuffer *buffer, char *src); +void savebufferWriteString_ext(struct savebuffer *buffer, char *src, u8 len); +void savebufferWriteString(struct savebuffer *buffer, char *src); void func0f0d564c_ext(u8 *data, char *dst, bool addlinebreak, u8 len); void func0f0d564c(u8 *data, char *dst, bool addlinebreak); void func0f0d5690(u8 *dst, char *buffer); From 8f477a36b60242436e9124b1780c17a4eb53ce15 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Mon, 31 Mar 2025 10:22:12 +0800 Subject: [PATCH 08/32] mpsetups: convert existing setups from the eeprom, removed "combat settings" items from file menu increased block size from 64 to 80 (it was insufficient) --- port/include/mpsetups.h | 3 +- port/src/mpsetups.c | 51 +++++++++++++++++--- src/game/filemgr.c | 21 -------- src/game/fmb.c | 16 ------ src/game/menutick.c | 1 - src/game/mplayer/mplayer.c | 34 ++++++++++--- src/game/mplayer/scenarios.c | 9 ++-- src/game/mplayer/scenarios/kingofthehill.inc | 5 +- src/game/mplayer/setup.c | 5 +- src/include/constants.h | 2 +- src/include/game/mplayer/scenarios.h | 2 +- 11 files changed, 87 insertions(+), 62 deletions(-) diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h index 9a2b2f58a..c54aee371 100644 --- a/port/include/mpsetups.h +++ b/port/include/mpsetups.h @@ -6,6 +6,7 @@ s32 mpsetupLoadCurrentFile(); s32 mpsetupSaveCurrentFile(); void mpsetupLoadSetup(s32 slotindex); -s32 mpsetupSaveSetup(s32 slotindex); +s32 mpsetupSaveSetup(s32 slotindex, u8 savefile); +void mpsetupCopyAllFromPak(); #endif diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index a1c9d658d..dc9e0fc2e 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -1,6 +1,8 @@ #include "types.h" #include "constants.h" #include "bss.h" +#include "game/pak.h" +#include "game/filelist.h" #include "game/menu.h" #include "game/savebuffer.h" #include "game/mplayer/mplayer.h" @@ -21,6 +23,8 @@ MP Setup File Format [setup_n{64}] */ +#define MPSETUP_VERSION 1 + #define MPSETUP_EXPORTDIR "$S/exported/" #define MPSETUP_FILENAME "mpsetups" #define MPSETUP_FILENAME_EXP "mpsetups-ext" @@ -131,7 +135,7 @@ MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *ite break; case MENUOP_SET: strcpy(setup->bytes, name); - err = mpsetupSaveSetup(slotindex); + err = mpsetupSaveSetup(slotindex, true); if (!err) { menuPopDialog(); } @@ -501,13 +505,14 @@ static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct me { if (operation == MENUOP_SET) { s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + // clicked on "clear default" if (selected == g_MpSetupFile.defaultsetup - 1) { - strcpy(g_LabelSetDefault, "Set Default\n"); g_MpSetupFile.defaultsetup = -1; + strcpy(g_LabelSetDefault, "Set Default\n"); } else { - strcpy(g_LabelSetDefault, "Clear Default\n"); g_MpSetupFile.defaultsetup = selected + 1; + strcpy(g_LabelSetDefault, "Clear Default\n"); } mpsetupSaveCurrentFile(); @@ -750,6 +755,7 @@ s32 mpsetupLoadCurrentFile() s32 mpsetupSaveCurrentFile() { + g_MpSetupFile.version = MPSETUP_VERSION; return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); } @@ -843,7 +849,7 @@ static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) return mpsetupLoadCurrentFile(); } -s32 mpsetupSaveSetup(s32 slotindex) +s32 mpsetupSaveSetup(s32 slotindex, u8 savefile) { struct savebuffer setup; @@ -857,7 +863,7 @@ s32 mpsetupSaveSetup(s32 slotindex) memcpy(g_MpSetupFile.setups[slotindex].bytes, setup.bytes, MPSETUP_BLOCKSIZE); - return mpsetupSaveCurrentFile(); + return savefile ? mpsetupSaveCurrentFile() : 0; } void mpsetupLoadSetup(s32 index) @@ -866,6 +872,39 @@ void mpsetupLoadSetup(s32 index) savebufferClear(&buffer); struct setupblock *block = &g_MpSetupFile.setups[index]; memcpy(&buffer.bytes, block->bytes, MPSETUP_BLOCKSIZE); - mpsetupfileLoadWad(&buffer); + mpsetupfileLoadWad(&buffer, g_MpSetupFile.version); g_MpCurrentSetup = index; } + +void mpsetupCopyAllFromPak() +{ + if (fsFileSize("$S/" MPSETUP_FILENAME ".bin") > 0) { + return; + } + + filelistCreate(1, FILETYPE_MPSETUP); + filelistsTick(); + + for (int i = 0; i < g_FileLists[1]->numfiles; ++i) { + struct savebuffer buffer; + savebufferClear(&buffer); + struct filelistfile *file = &g_FileLists[1]->files[i]; + s32 device = pakFindBySerial(file->deviceserial); + s32 err = pakReadBodyAtGuid(device, file->fileid, buffer.bytes, 0); + + if (err != 0) { + sysLogPrintf(LOG_ERROR, "Unable to read pak. device %d fileid %d deviceserial %d err %d", + device, file->fileid, file->deviceserial, err); + continue; + } + + mpsetupfileLoadWad(&buffer, 0); + + // save the file when writing the last setup + u8 savefile = i == g_FileLists[1]->numfiles - 1; + mpsetupSaveSetup(g_MpSetupFile.numsetups, savefile); + } + + // to reset the mp setup + mpInit(); +} diff --git a/src/game/filemgr.c b/src/game/filemgr.c index 9c87e071d..a5dbc53e8 100644 --- a/src/game/filemgr.c +++ b/src/game/filemgr.c @@ -499,11 +499,6 @@ char *filemgrMenuTextFileType(struct menuitem *item) case FILEOP_LOAD_GAME: case FILEOP_READ_GAME: return langGet(names[0]); - case FILEOP_SAVE_MPSETUP: - case FILEOP_WRITE_MPSETUP: - case FILEOP_LOAD_MPSETUP: - case FILEOP_READ_MPSETUP: - return langGet(names[1]); case FILEOP_SAVE_MPPLAYER: case FILEOP_WRITE_MPPLAYER: case FILEOP_LOAD_MPPLAYER: @@ -3296,14 +3291,6 @@ struct menuitem g_FilemgrOperationsMenuItems[] = { 0, filemgrOpenCopyFileMenuHandler, }, - { - MENUITEMTYPE_SELECTABLE, - FILETYPE_MPSETUP, - 0, - L_OPTIONS_104, // "Combat Simulator Settings File" - 0, - filemgrOpenCopyFileMenuHandler, - }, { MENUITEMTYPE_SELECTABLE, FILETYPE_MPPLAYER, @@ -3336,14 +3323,6 @@ struct menuitem g_FilemgrOperationsMenuItems[] = { 0, filemgrOpenDeleteFileMenuHandler, }, - { - MENUITEMTYPE_SELECTABLE, - FILETYPE_MPSETUP, - 0, - L_OPTIONS_104, // "Combat Simulator Settings File" - 0, - filemgrOpenDeleteFileMenuHandler, - }, { MENUITEMTYPE_SELECTABLE, FILETYPE_MPPLAYER, diff --git a/src/game/fmb.c b/src/game/fmb.c index f6de5aadd..3cd114d0f 100644 --- a/src/game/fmb.c +++ b/src/game/fmb.c @@ -133,14 +133,6 @@ struct menuitem g_GameFiles4MbMenuItems[] = { 0, filemgrOpenCopyFileMenuHandler, }, - { - MENUITEMTYPE_SELECTABLE, - 1, - 0, - L_OPTIONS_104, // "Combat Simulator Settings File" - 0, - filemgrOpenCopyFileMenuHandler, - }, { MENUITEMTYPE_SELECTABLE, 2, @@ -173,14 +165,6 @@ struct menuitem g_GameFiles4MbMenuItems[] = { 0, filemgrOpenDeleteFileMenuHandler, }, - { - MENUITEMTYPE_SELECTABLE, - 1, - 0, - L_OPTIONS_104, // "Combat Simulator Settings File" - 0, - filemgrOpenDeleteFileMenuHandler, - }, { MENUITEMTYPE_SELECTABLE, 2, diff --git a/src/game/menutick.c b/src/game/menutick.c index 5a795da9d..fcd0df3b8 100644 --- a/src/game/menutick.c +++ b/src/game/menutick.c @@ -473,7 +473,6 @@ void menuTick(void) (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU)) { var80062948 = 1; filelistCreate(0, FILETYPE_MPPLAYER); - filelistCreate(1, FILETYPE_MPSETUP); } if (var80062944) { diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 69facd19f..4b8f70ecf 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3931,16 +3931,32 @@ void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version) s32 i; s32 j; - savebufferReadString_ext(buffer, g_MpSetup.name, false, MPSETUP_MAXNAME+1); + if (version > 0) { + savebufferReadString_ext(buffer, g_MpSetup.name, false, MPSETUP_MAXNAME + 1); + } + else { + savebufferReadString(buffer, g_MpSetup.name, false); + } + savebufferReadBits(buffer, 4); g_MpSetup.stagenum = savebufferReadBits(buffer, 7); g_MpSetup.scenario = savebufferReadBits(buffer, 3); - scenarioInit(); - scenarioReadSave(buffer); + // version == 0 means we're only reading to convert, so no actions are needed + if (version > 0) { + scenarioInit(); + } + + scenarioReadSave(buffer, version); + + if (version > 0) { + g_MpSetup.options = savebufferReadBits(buffer, 32); + } + else { + g_MpSetup.options = savebufferReadBits(buffer, 21); + } - g_MpSetup.options = savebufferReadBits(buffer, 32); g_MpSetup.chrslots &= 0x000f; for (i = 0; i < MAX_BOTS; i++) { @@ -3961,14 +3977,18 @@ void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version) g_BotConfigsArray[i].base.team = savebufferReadBits(buffer, 3); } - mpGenerateBotNames(); + if (version > 0) { + mpGenerateBotNames(); + } for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) { g_MpSetup.weapons[i] = savebufferReadBits(buffer, 7); } - u64 wpnRndPacked = savebufferReadBits(buffer, 64); - unpackWeaponSetRandomFilters(wpnRndPacked); + if (version > 0) { + u64 wpnRndPacked = savebufferReadBits(buffer, 64); + unpackWeaponSetRandomFilters(wpnRndPacked); + } func0f18913c(); diff --git a/src/game/mplayer/scenarios.c b/src/game/mplayer/scenarios.c index 0c4df1f51..b9ea325b3 100644 --- a/src/game/mplayer/scenarios.c +++ b/src/game/mplayer/scenarios.c @@ -70,7 +70,7 @@ struct mpscenario { bool (*isroomhighlightedfunc)(RoomNum room); void (*highlightroomfunc)(RoomNum room, s32 *arg1, s32 *arg2, s32 *arg3); void *unk3c; // never hooked into nor fired - void (*readsavefunc)(struct savebuffer *buffer); + void (*readsavefunc)(struct savebuffer *buffer, u8 version); void (*writesavefunc)(struct savebuffer *buffer); }; @@ -416,12 +416,13 @@ MenuItemHandlerResult menuhandlerMpOpenOptions(s32 operation, struct menuitem *i * * Used by KOH to read the mphilltime. */ -void scenarioReadSave(struct savebuffer *buffer) +void scenarioReadSave(struct savebuffer *buffer, u8 version) { if (g_MpScenarios[g_MpSetup.scenario].readsavefunc) { - g_MpScenarios[g_MpSetup.scenario].readsavefunc(buffer); + g_MpScenarios[g_MpSetup.scenario].readsavefunc(buffer, version); } else { - savebufferReadBits(buffer, 32); + u8 sz = version > 0 ? 32 : 8; + savebufferReadBits(buffer, sz); } } diff --git a/src/game/mplayer/scenarios/kingofthehill.inc b/src/game/mplayer/scenarios/kingofthehill.inc index 721550423..542256b9b 100644 --- a/src/game/mplayer/scenarios/kingofthehill.inc +++ b/src/game/mplayer/scenarios/kingofthehill.inc @@ -181,9 +181,10 @@ struct menudialogdef g_KohOptionsMenuDialog = { NULL, }; -void kohReadSave(struct savebuffer *buffer) +void kohReadSave(struct savebuffer *buffer, u8 version) { - g_Vars.mphilltime = savebufferReadBits(buffer, 32); + u8 sz = version > 0 ? 32 : 8; + g_Vars.mphilltime = savebufferReadBits(buffer, sz); } void kohWriteSave(struct savebuffer *buffer) diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index a70087eda..27e0b7fc3 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -470,7 +470,7 @@ MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *ite strcpy(g_MpSetup.name, name); break; case MENUOP_SET: - err = mpsetupSaveSetup(g_MpSetupFile.numsetups); + err = mpsetupSaveSetup(g_MpSetupFile.numsetups, true); if (!err) { menuPushDialog(&g_FilemgrFileSavedMenuDialog); } @@ -488,7 +488,7 @@ MenuItemHandlerResult menuhandlerMpSaveSetupOverwrite(s32 operation, struct menu { if (operation == MENUOP_SET) { menuPopDialog(); - mpsetupSaveSetup(g_MpCurrentSetup); + mpsetupSaveSetup(g_MpCurrentSetup, true); menuPushDialog(&g_FilemgrFileSavedMenuDialog); } @@ -5363,6 +5363,7 @@ MenuDialogHandlerResult menudialogCombatSimulator(s32 operation, struct menudial g_Vars.waitingtojoin[3] = false; // load the setup file when entering the Combat Simulator + mpsetupCopyAllFromPak(); mpsetupLoadCurrentFile(); } diff --git a/src/include/constants.h b/src/include/constants.h index e1c4b4ea3..f465b6263 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -4756,7 +4756,7 @@ enum weaponnum { #define MPSETUP_MAXSETUPS 128 #define MPSETUP_MAXNAME 18 -#define MPSETUP_BLOCKSIZE 64 +#define MPSETUP_BLOCKSIZE 80 #endif diff --git a/src/include/game/mplayer/scenarios.h b/src/include/game/mplayer/scenarios.h index a04f0d021..48c7bdcc1 100644 --- a/src/include/game/mplayer/scenarios.h +++ b/src/include/game/mplayer/scenarios.h @@ -17,7 +17,7 @@ struct mpscenariooverview { extern struct mpscenariooverview g_MpScenarioOverviews[6]; MenuItemHandlerResult menuhandlerMpOpenOptions(s32 operation, struct menuitem *item, union handlerdata *data); -void scenarioReadSave(struct savebuffer *buffer); +void scenarioReadSave(struct savebuffer *buffer, u8 version); void scenarioWriteSave(struct savebuffer *buffer); void scenarioInit(void); s32 scenarioNumProps(void); From 7a98095a0be2c23d72f0cd273a30c93d0bf93909 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Mon, 31 Mar 2025 10:25:24 +0800 Subject: [PATCH 09/32] mpsetups: bug fix - when deleting a setup that is behind the default setup in the list --- port/src/mpsetups.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index dc9e0fc2e..d347c56e2 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -663,16 +663,16 @@ static s32 deleteSetup() memcpy(dst, src, MPSETUP_BLOCKSIZE); } - if (g_MpCurrentSetup > slotindex) { + if (g_MpCurrentSetup >= slotindex) { g_MpCurrentSetup--; } - else if (g_MpCurrentSetup == slotindex) { - g_MpCurrentSetup = -1; - } - if (g_MpSetupFile.defaultsetup == slotindex + 1) { - g_MpSetupFile.defaultsetup = 0; - } + if (g_MpSetupFile.defaultsetup > slotindex + 1) { + g_MpSetupFile.defaultsetup--; + } + else if (g_MpSetupFile.defaultsetup == slotindex + 1) { + g_MpSetupFile.defaultsetup = 0; + } g_MpSetupFile.numsetups--; return mpsetupSaveCurrentFile(); From f9cfa0fa437faa1b087ef2e92919b2857c0f720b Mon Sep 17 00:00:00 2001 From: Raf <> Date: Mon, 31 Mar 2025 10:34:25 +0800 Subject: [PATCH 10/32] mpsetups: introduced "menu alt" button/key to toggle presets Currently the "menu alt" (L/R buttons, or Ctrl/Alt keys) is fixed, but we may make it bindable later --- port/include/input.h | 7 +++++ port/src/input.c | 27 +++++++++++++++++++ src/game/mplayer/mplayer.c | 4 +++ src/game/mplayer/setup.c | 53 ++++++++++++++++++++++++++++++-------- src/include/types.h | 1 + 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index 6d5d88bfd..98f5928fc 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -40,6 +40,7 @@ enum virtkey { VK_DELETE = 76, VK_LCTRL = 224, VK_LSHIFT = 225, + VK_LALT = 226, VK_RCTRL = 228, VK_RSHIFT = 229, @@ -56,6 +57,8 @@ enum virtkey { /* same order as SDL gamecontroller buttons plus two buttons for triggers */ VK_JOY_BEGIN, VK_JOY1_BEGIN = VK_JOY_BEGIN, + VK_JOY1_LSHOULDER = VK_JOY1_BEGIN + 9, + VK_JOY1_RSHOULDER = VK_JOY1_BEGIN + 10, VK_JOY1_LTRIG = VK_JOY1_BEGIN + 30, VK_JOY1_RTRIG = VK_JOY1_BEGIN + 31, VK_JOY2_BEGIN = VK_JOY1_BEGIN + INPUT_MAX_CONTROLLER_BUTTONS, @@ -267,4 +270,8 @@ const char *inputGetClipboard(void); // returns keymod values u32 inputGetKeyModState(void); +s32 inputMenuAlt1MenuPressed(s32 playerNum); +s32 inputMenuAlt2MenuPressed(s32 playerNum); +s32 inputMenuAltMenuPressed(s32 playerNum); + #endif diff --git a/port/src/input.c b/port/src/input.c index cfee620a5..7024fc822 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -1162,6 +1162,33 @@ s32 inputKeyJustPressed(u32 vk) return result; } +s32 inputMenuAlt1MenuPressed(s32 playerNum) +{ + s32 RSHOULDER = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(RSHOULDER); + } + + return inputKeyJustPressed(RSHOULDER); +} + +s32 inputMenuAlt2MenuPressed(s32 playerNum) +{ + s32 LSHOULDER = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(LSHOULDER); + } + + return inputKeyJustPressed(LSHOULDER); +} + +s32 inputMenuAltMenuPressed(s32 playerNum) +{ + return inputMenuAlt1MenuPressed(playerNum) || inputMenuAlt2MenuPressed(playerNum); +} + static inline u32 inputContToContKey(const u32 cont) { if (cont == 0) { diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 4b8f70ecf..add19e0aa 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -600,6 +600,10 @@ void mpInit(void) } g_MpSetup.chrslots = 0; + + for (i = 0; i < ARRAYCOUNT(g_Menus); i++) { + g_Menus[i].mpsetup.showpresets = 1; + } } #if VERSION >= VERSION_PAL_BETA diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 27e0b7fc3..a72e55759 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -20,10 +20,12 @@ #include "lib/vi.h" #include "lib/rng.h" #include "lib/str.h" +#include "lib/joy.h" #include "data.h" #include "gbiex.h" #include "types.h" #include "system.h" +#include "input.h" #include "mpsetups.h" struct menuitem g_MpCharacterMenuItems[]; @@ -2252,16 +2254,19 @@ MenuItemHandlerResult mpPlayerNameMenuHandler(s32 operation, struct menuitem *it MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem *item, union handlerdata *data) { + u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; + s32 numpresets = mpGetNumUnlockedPresets()*presets; + switch (operation) { case MENUOP_GETOPTIONCOUNT: - data->list.value = mpGetNumUnlockedPresets() + g_MpSetupFile.numsetups; + data->list.value = numpresets + g_MpSetupFile.numsetups; break; case MENUOP_GETOPTIONTEXT: - if (data->list.value < mpGetNumUnlockedPresets()) { + if (presets && data->list.value < mpGetNumUnlockedPresets()) { return (uintptr_t)mpGetPresetNameBySlot(data->list.value); } if (g_MpSetupFile.numsetups > 0) { - struct setupblock *block = &g_MpSetupFile.setups[data->list.value - mpGetNumUnlockedPresets()]; + struct setupblock *block = &g_MpSetupFile.setups[data->list.value - numpresets]; func0f0d564c_ext(block->bytes, g_StringPointer, false, MPSETUP_MAXNAME+1); return (uintptr_t)g_StringPointer; } @@ -2269,12 +2274,12 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * case MENUOP_SET: mpCloseDialogsForNewSetup(); - if (data->list.value < mpGetNumUnlockedPresets()) { + if (presets && data->list.value < mpGetNumUnlockedPresets()) { mp0f18dec4(data->list.value); g_MpCurrentSetup = -1; } else { - mpsetupLoadSetup(data->list.value - mpGetNumUnlockedPresets()); + mpsetupLoadSetup(data->list.value - numpresets); } if (item->param == 1) { @@ -2289,22 +2294,22 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * data->list.value = 0xfffff; break; case MENUOP_GETOPTGROUPCOUNT: - data->list.value = 2; + data->list.value = presets ? 2 : 1; break; case MENUOP_GETOPTGROUPTEXT: - if (data->list.value == 0) { + if (presets && data->list.value == 0) { return (uintptr_t)langGet(L_MPMENU_141); // "Presets" } return (uintptr_t)"Custom"; case MENUOP_GETGROUPSTARTINDEX: - data->list.groupstartindex = data->list.value == 0 ? 0 : mpGetNumUnlockedPresets(); + data->list.groupstartindex = data->list.value == 0 ? 0 : numpresets; break; case MENUOP_LISTITEMFOCUS: - if (data->list.value < mpGetNumUnlockedPresets()) { + if (presets && data->list.value < mpGetNumUnlockedPresets()) { g_Menus[g_MpPlayerNum].mpsetup.slotindex = 0xffff; } else { - g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value - mpGetNumUnlockedPresets(); + g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value - numpresets; } break; } @@ -2632,14 +2637,40 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { 0, NULL, }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_SMALLFONT, + (uintptr_t)"Menu Alt: Toggle Presets\n", + 0, + NULL, + }, { MENUITEMTYPE_END }, }; +MenuDialogHandlerResult loadSettingsDialogHandler(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data) +{ + if (operation == MENUOP_TICK) { + if (inputMenuAltMenuPressed(g_MpPlayerNum)) { + u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; + g_Menus[g_MpPlayerNum].mpsetup.showpresets = 1 - presets; + } + } +} + struct menudialogdef g_MpLoadSettingsMenuDialog = { MENUDIALOGTYPE_DEFAULT, L_MPMENU_139, // "Load Game Settings" g_MpLoadSettingsMenuItems, - NULL, + loadSettingsDialogHandler, MENUDIALOGFLAG_CLOSEONSELECT, NULL, }; diff --git a/src/include/types.h b/src/include/types.h index 61d8a96f5..cc0b2ed32 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -3774,6 +3774,7 @@ struct menudata_main { struct menudata_mpsetup { u32 slotindex; u32 slotcount; + u8 showpresets; u32 unke24; }; From 6198fbc47913b0bdcb01f9c81e21d2c408abc645 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Tue, 1 Apr 2025 17:00:59 +0800 Subject: [PATCH 11/32] mpsetups: a bunch of bug fixes - "load settings" becoming invalid when entering and exiting the challenges screen - when setting a setup as default, it would change the current setup - larger window width causing the marquee text to be rendered off-bounds - reduced the setup name's max length, as it wouldn't fit on the screen in some cases --- port/src/mpsetups.c | 20 ++++++++++++-------- src/game/mplayer/setup.c | 13 +++++++------ src/include/constants.h | 2 +- src/include/types.h | 3 ++- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index d347c56e2..7adf057db 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -154,8 +154,8 @@ struct menuitem g_RenameSetupItems[] = { { MENUITEMTYPE_LABEL, 0, - MENUITEMFLAG_LESSLEFTPADDING, - L_MPMENU_189, // "Enter a name for your game setup file:" + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)"Enter the setup name:\n", 0, NULL, }, @@ -663,9 +663,12 @@ static s32 deleteSetup() memcpy(dst, src, MPSETUP_BLOCKSIZE); } - if (g_MpCurrentSetup >= slotindex) { + if (g_MpCurrentSetup > slotindex) { g_MpCurrentSetup--; } + else if (g_MpCurrentSetup == slotindex) { + g_MpCurrentSetup = -1; + } if (g_MpSetupFile.defaultsetup > slotindex + 1) { g_MpSetupFile.defaultsetup--; @@ -802,8 +805,10 @@ s32 exportMpSetupFile() { struct mpsetupfile expMpSetupFile; expMpSetupFile.numsetups = 0; - - u8 maxsetups = g_MpImportExportFilter[1] == 0 ? 64 : 128; + expMpSetupFile.defaultsetup = 0; + expMpSetupFile.version = MPSETUP_VERSION; + + u8 maxsetups = MPSETUP_MAXSETUPS; maxsetups = MIN(maxsetups, g_MpSetupFile.numsetups); for (int i = 0; i < maxsetups; ++i) { u8 bank = i < 64 ? 0 : 1; @@ -840,13 +845,12 @@ static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) if (nwritten < 1) { fsFileFree(f); sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file"); + sprintf(g_StatusText, "Unable to write the setup file\n"); return -1; } fsFileFree(f); - - // reload the file to update in-memory structs - return mpsetupLoadCurrentFile(); + return 0; } s32 mpsetupSaveSetup(s32 slotindex, u8 savefile) diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 1a91d0821..a9d920c49 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -459,6 +459,7 @@ MenuItemHandlerResult menuhandlerMpConfirmSaveChr(s32 operation, struct menuitem return 0; } +extern struct menudialogdef g_StatusErrorDialog; MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *item, union handlerdata *data) { char *name = data->keyboard.string; @@ -475,10 +476,10 @@ MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *ite err = mpsetupSaveSetup(g_MpSetupFile.numsetups, true); if (!err) { menuPushDialog(&g_FilemgrFileSavedMenuDialog); + g_MpCurrentSetup = g_MpSetupFile.numsetups - 1; } else { -// menuPushDialog(&g_FilemgrErrorMenuDialog); - // TODO + menuPushDialog(&g_StatusErrorDialog); } break; } @@ -1092,8 +1093,8 @@ struct menuitem g_MpSaveSetupNameMenuItems[] = { { MENUITEMTYPE_LABEL, 0, - MENUITEMFLAG_LESSLEFTPADDING, - L_MPMENU_189, // "Enter a name for your game setup file:" + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)"Enter the setup name:\n", 0, NULL, }, @@ -2322,7 +2323,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * char *mpMenuTextMpconfigMarquee(struct menuitem *item) { - char filename[20]; + char filename[MPSETUP_MAXNAME+1]; u16 numsims; u16 stagenum; u16 scenarionum; @@ -2628,7 +2629,7 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { MENUITEMTYPE_LIST, 0, 0, - 160, + 0x00000078, 0x00000042, mpLoadSettingsMenuHandler, }, diff --git a/src/include/constants.h b/src/include/constants.h index 1b87a7952..6a77fda41 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -4757,7 +4757,7 @@ enum weaponnum { #define EXTRA_SLEEP_TIME 1000LL // 100us #define MPSETUP_MAXSETUPS 128 -#define MPSETUP_MAXNAME 18 +#define MPSETUP_MAXNAME 17 #define MPSETUP_BLOCKSIZE 80 #endif diff --git a/src/include/types.h b/src/include/types.h index cc0b2ed32..1ed8ee9d9 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -3983,13 +3983,14 @@ struct menu { union { struct menudata_endscreen endscreen; struct menudata_main main; - struct menudata_mpsetup mpsetup; struct menudata_mppause mppause; struct menudata_mpend mpend; struct menudata_filemgr fm; struct menudata_main4mb main4mb; struct menudata_training training; }; + + struct menudata_mpsetup mpsetup; }; struct gamefile { From b93015c51c4b0a2b1c49b0b3a5c56a46fc3662ed Mon Sep 17 00:00:00 2001 From: Raf <> Date: Wed, 2 Apr 2025 00:33:45 +0800 Subject: [PATCH 12/32] mpsetups: fixed reverse pitch/look ahead being reset when the mpsetups file is created for the first time --- port/src/mpsetups.c | 2 +- port/src/pdmain.c | 2 +- src/game/filemgr.c | 2 +- src/game/mplayer/mplayer.c | 20 +++++++++++--------- src/include/game/mplayer/mplayer.h | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 7adf057db..519a8a92d 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -910,5 +910,5 @@ void mpsetupCopyAllFromPak() } // to reset the mp setup - mpInit(); + mpInit(false); } diff --git a/port/src/pdmain.c b/port/src/pdmain.c index 58d37b192..344e954fd 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -259,7 +259,7 @@ void mainInit(void) frametimeInit(); profileInit(); smokesInit(); - mpInit(); + mpInit(true); pheadInit(); paksInit(); pheadInit2(); diff --git a/src/game/filemgr.c b/src/game/filemgr.c index a5dbc53e8..45b85efae 100644 --- a/src/game/filemgr.c +++ b/src/game/filemgr.c @@ -2764,7 +2764,7 @@ MenuDialogHandlerResult filemgrMainMenuDialog(s32 operation, struct menudialogde g_Menus[g_MpPlayerNum].fm.filetypeplusone = 0; filelistCreate(0, FILETYPE_GAME); - mpInit(); + mpInit(true); // Set MP player names to "Player 1" through 4 if blank for (i = 0; i < MAX_PLAYERS; i++) { diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index dcb183877..40daa9935 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -518,12 +518,6 @@ void mpPlayerSetDefaults(s32 playernum, bool autonames) for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) { g_PlayerConfigsArray[playernum].gunfuncs[i] = 0; } - -#ifndef PLATFORM_N64 - for (i = 0; i < ARRAYCOUNT(g_MpWeapons); i++) { - g_MpWeaponSetRandomFilters[i] = 1; - } -#endif } void func0f1881d4(s32 index) @@ -535,7 +529,7 @@ void func0f1881d4(s32 index) g_BotConfigsArray[index].difficulty = BOTDIFF_DISABLED; } -void mpInit(void) +void mpInit(bool resetplayers) { s32 i; s32 j; @@ -568,8 +562,10 @@ void mpInit(void) strcpy(g_MpSetup.name, ""); - for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) { - mpPlayerSetDefaults(i, false); + if (resetplayers) { + for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) { + mpPlayerSetDefaults(i, false); + } } for (i = 0; i < MAX_BOTS; i++) { @@ -606,6 +602,12 @@ void mpInit(void) for (i = 0; i < ARRAYCOUNT(g_Menus); i++) { g_Menus[i].mpsetup.showpresets = 1; } + +#ifndef PLATFORM_N64 + for (i = 0; i < ARRAYCOUNT(g_MpWeapons); i++) { + g_MpWeaponSetRandomFilters[i] = 1; + } +#endif } #if VERSION >= VERSION_PAL_BETA diff --git a/src/include/game/mplayer/mplayer.h b/src/include/game/mplayer/mplayer.h index a5b37e34f..7510390d5 100644 --- a/src/include/game/mplayer/mplayer.h +++ b/src/include/game/mplayer/mplayer.h @@ -13,7 +13,7 @@ void func0f187fbc(s32 playernum); void func0f187fec(void); void mpPlayerSetDefaults(s32 playernum, bool autonames); void func0f1881d4(s32 index); -void mpInit(void); +void mpInit(bool resetplayers); void mpGetTeamsWithDefaultName(u8 *mask); void mpSetTeamNamesToDefault(u8 mask); void mpSetDefaultNamesIfEmpty(void); From cb6dc1feab14dce33ed19fcc4f1460ad4726b444 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Wed, 2 Apr 2025 15:55:31 +0800 Subject: [PATCH 13/32] mpsetups: forgot to pop the dialog after import name conflict, removed debug print --- port/src/mpsetups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 519a8a92d..577e14cdf 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -314,6 +314,7 @@ MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct me } else { if (err == MPSETUP_IMPORT_CONFLICT) { + menuPopDialog(); menuPushDialog(&g_ImportOverrideDialog); } else { @@ -817,7 +818,6 @@ s32 exportMpSetupFile() u8 n = expMpSetupFile.numsetups; expMpSetupFile.numsetups++; char *name = g_MpSetupFile.setups[i].bytes; - sysLogPrintf(LOG_NOTE, "%s", name); memcpy(expMpSetupFile.setups[n].bytes, g_MpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE); expMpSetupFile.numsetups = n + 1; } From d4856a193bffde6ab8a0859fd5ad3d6dd9c0edbb Mon Sep 17 00:00:00 2001 From: Raf <> Date: Wed, 2 Apr 2025 20:13:19 +0800 Subject: [PATCH 14/32] mpsetups: added weapon set num to the save file --- port/src/mpsetups.c | 4 ++-- src/game/mplayer/mplayer.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 577e14cdf..8c4f4e1be 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -18,9 +18,9 @@ MP Setup File Format [defaultsetup{1}] [numsetups{1}] # setups - [setup_1{64}] + [setup_1{80}] ... - [setup_n{64}] + [setup_n{80}] */ #define MPSETUP_VERSION 1 diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 40daa9935..f3a8b20cd 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -3998,7 +3998,7 @@ void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version) unpackWeaponSetRandomFilters(wpnRndPacked); } - func0f18913c(); + g_MpWeaponSetNum = savebufferReadBits(buffer, 8); g_MpSetup.timelimit = savebufferReadBits(buffer, 6); g_MpSetup.scorelimit = savebufferReadBits(buffer, 7); @@ -4066,6 +4066,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) u64 wpnRndPacked = packWeaponSetRandomFilters(); savebufferOr(buffer, wpnRndPacked, 64); + savebufferOr(buffer, g_MpWeaponSetNum, 8); savebufferOr(buffer, g_MpSetup.timelimit, 6); savebufferOr(buffer, g_MpSetup.scorelimit, 7); From 1555a4be8463e0489770aca463db74a6d7e1110a Mon Sep 17 00:00:00 2001 From: Raf <> Date: Wed, 2 Apr 2025 22:19:24 +0800 Subject: [PATCH 15/32] missing semicolon after mkdir() in non-win platforms --- port/src/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/fs.c b/port/src/fs.c index 7d4507aa7..0eb4ef4c2 100644 --- a/port/src/fs.c +++ b/port/src/fs.c @@ -288,6 +288,6 @@ s32 fsCreateDir(const char *path) { #ifdef PLATFORM_WIN32 return _mkdir(fsFullPath(path)); #else - return mkdir(fsFullPath(path), S_IRWXU | S_IRWXG) + return mkdir(fsFullPath(path), S_IRWXU | S_IRWXG); #endif } From 053f7dc8a5495cf6f671d0e37a935aa9cdb18be0 Mon Sep 17 00:00:00 2001 From: AL2009man <67606569+AL2009man@users.noreply.github.com> Date: Fri, 11 Apr 2025 00:06:02 -0400 Subject: [PATCH 16/32] Mouse Sensitivity follows Quake/id Tech mouse sens this changes mouse sensitiivity scale to mirror id Tech's Mouse Sensitivity Scale. This also makes it easier for Steam Input users (Steam Deck and Steam Controller) to use Gyro To Mouse/Flick Stick's Natural Sensitivity Scale --- port/src/input.c | 12 ++++++------ src/game/player.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/port/src/input.c b/port/src/input.c index 2bacbd646..9cf2e6134 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -88,8 +88,8 @@ static s32 mouseLockMode = MLOCK_AUTO; static u64 mouseCursorTime = 0; static s32 mouseShowCursor = 1; -static f32 mouseSensX = 1.5f; -static f32 mouseSensY = 1.5f; +static f32 mouseSensX = 2.5f; +static f32 mouseSensY = 2.5f; static s32 lastKey = 0; static char lastChar = 0; @@ -1243,8 +1243,8 @@ void inputMouseGetScaledDelta(f32 *dx, f32 *dy) { f32 mdx, mdy; if (mouseLocked) { - mdx = mouseSensX * (f32)mouseDX / 100.0f; - mdy = mouseSensY * (f32)mouseDY / 100.0f; + mdx = mouseSensX * (f32)mouseDX / 159.2f; + mdy = mouseSensY * (f32)mouseDY / 159.2f; } else { mdx = 0.f; mdy = 0.f; @@ -1257,8 +1257,8 @@ void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy) { f32 mdx, mdy; if (mouseLocked) { - mdx = fabsf(mouseSensX) * (f32)mouseDX / 100.0f; - mdy = fabsf(mouseSensY) * (f32)mouseDY / 100.0f; + mdx = fabsf(mouseSensX) * (f32)mouseDX / 159.2f; + mdy = fabsf(mouseSensY) * (f32)mouseDY / 159.2f; } else { mdx = 0.f; mdy = 0.f; diff --git a/src/game/player.c b/src/game/player.c index a0983ef36..fa17dc472 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -3674,8 +3674,8 @@ void playerTick(bool arg0) if (g_Vars.currentplayerstats && !optionsGetForwardPitch(g_Vars.currentplayerstats->mpindex)) { mdy = -mdy; } - sp178 += mdy * 0.00025f; - sp174 -= mdx * 0.00025f; + sp178 += mdy * 0.022f; + sp174 -= mdx * 0.022f; } } #endif From 9695856be6bad02fe4556f22c8c58b56d840442e Mon Sep 17 00:00:00 2001 From: AL2009man <67606569+AL2009man@users.noreply.github.com> Date: Fri, 11 Apr 2025 19:24:25 -0400 Subject: [PATCH 17/32] updated player.c to account for 0.022f scale while also changing input.c delta scale to ensure it has a divider --- port/src/input.c | 40 ++++++++++++++++++---------------------- src/game/player.c | 8 ++++---- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/port/src/input.c b/port/src/input.c index 9cf2e6134..96e37255e 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -1239,32 +1239,28 @@ void inputMouseGetRawDelta(s32 *dx, s32 *dy) if (dy) *dy = mouseDY; } -void inputMouseGetScaledDelta(f32 *dx, f32 *dy) +void inputMouseGetScaledDelta(f32* dx, f32* dy) { - f32 mdx, mdy; - if (mouseLocked) { - mdx = mouseSensX * (f32)mouseDX / 159.2f; - mdy = mouseSensY * (f32)mouseDY / 159.2f; - } else { - mdx = 0.f; - mdy = 0.f; - } - if (dx) *dx = mdx; - if (dy) *dy = mdy; + f32 mdx = 0.f, mdy = 0.f; + + if (mouseLocked) { + mdx = mouseSensX * ((f32)mouseDX / 3.5f) * 0.022f; + mdy = mouseSensY * ((f32)mouseDY / 3.5f) * 0.022f; + } + if (dx) *dx = mdx; + if (dy) *dy = mdy; } -void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy) +void inputMouseGetAbsScaledDelta(f32* dx, f32* dy) { - f32 mdx, mdy; - if (mouseLocked) { - mdx = fabsf(mouseSensX) * (f32)mouseDX / 159.2f; - mdy = fabsf(mouseSensY) * (f32)mouseDY / 159.2f; - } else { - mdx = 0.f; - mdy = 0.f; - } - if (dx) *dx = mdx; - if (dy) *dy = mdy; + f32 mdx = 0.f, mdy = 0.f; + + if (mouseLocked) { + mdx = fabsf(mouseSensX) * ((f32)mouseDX / 3.5f) * 0.022f; + mdy = fabsf(mouseSensY) * ((f32)mouseDY / 3.5f) * 0.022f; + } + if (dx) *dx = mdx; + if (dy) *dy = mdy; } void inputMouseGetSpeed(f32 *x, f32 *y) diff --git a/src/game/player.c b/src/game/player.c index fa17dc472..cd5fe4137 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -3667,15 +3667,15 @@ void playerTick(bool arg0) f32 mdx, mdy; inputMouseGetScaledDelta(&mdx, &mdy); if (mdx || mdy) { - mdx *= 48.f; - mdy *= 48.f; + mdx *= 0.022f; + mdy *= 0.022f; mdx = (mdx < -128.f) ? -128.f : (mdx > 127.f) ? 127.f : mdx; mdy = (mdy < -128.f) ? -128.f : (mdy > 127.f) ? 127.f : mdy; if (g_Vars.currentplayerstats && !optionsGetForwardPitch(g_Vars.currentplayerstats->mpindex)) { mdy = -mdy; } - sp178 += mdy * 0.022f; - sp174 -= mdx * 0.022f; + sp178 += mdy; + sp174 -= mdx; } } #endif From 30071e8f645556285c3ed277eeb2eab15f2d9d73 Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 13 Apr 2025 10:35:43 -0700 Subject: [PATCH 18/32] port: call `joyReset()` when changing controllers This needs to be called for controller reassignments to take effect in MP modes --- port/src/optionsmenu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 4771fc28b..29105ab93 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -597,8 +597,10 @@ static MenuItemHandlerResult menuhandlerController(s32 operation, struct menuite if (data->dropdown.value == 0) { // unassign controller inputAssignController(g_ExtMenuPlayer, -1); + joyReset(); } else if (data->dropdown.value <= numCtrls) { inputAssignController(g_ExtMenuPlayer, ctrls[data->dropdown.value - 1]); + joyReset(); } break; case MENUOP_GETSELECTEDINDEX: From ab73fdcc308407cc3cb4e1608fcc6a35e5649863 Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 13 Apr 2025 10:44:26 -0700 Subject: [PATCH 19/32] fixup: include header for `joyReset` --- port/src/optionsmenu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 29105ab93..e077acb31 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -10,6 +10,7 @@ #include "game/menu.h" #include "game/gamefile.h" #include "game/player.h" +#include "lib/joy.h" #include "video.h" #include "input.h" #include "config.h" From f96a03e60f0dffbe45e8a58e9b9a56d2e6bf73d9 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 27 Apr 2025 15:34:25 +0200 Subject: [PATCH 20/32] port: clean up previous PR --- port/include/input.h | 4 - port/include/mpsetups.h | 6 +- port/src/fs.c | 5 +- port/src/input.c | 27 -- port/src/mpsetups.c | 792 +++++++++++++++++++-------------------- src/game/mplayer/setup.c | 55 ++- 6 files changed, 433 insertions(+), 456 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index 98f5928fc..6171c2696 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -270,8 +270,4 @@ const char *inputGetClipboard(void); // returns keymod values u32 inputGetKeyModState(void); -s32 inputMenuAlt1MenuPressed(s32 playerNum); -s32 inputMenuAlt2MenuPressed(s32 playerNum); -s32 inputMenuAltMenuPressed(s32 playerNum); - #endif diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h index c54aee371..3ef143906 100644 --- a/port/include/mpsetups.h +++ b/port/include/mpsetups.h @@ -3,10 +3,10 @@ #include "types.h" -s32 mpsetupLoadCurrentFile(); -s32 mpsetupSaveCurrentFile(); +s32 mpsetupLoadCurrentFile(void); +s32 mpsetupSaveCurrentFile(void); void mpsetupLoadSetup(s32 slotindex); s32 mpsetupSaveSetup(s32 slotindex, u8 savefile); -void mpsetupCopyAllFromPak(); +void mpsetupCopyAllFromPak(void); #endif diff --git a/port/src/fs.c b/port/src/fs.c index 0eb4ef4c2..459e72392 100644 --- a/port/src/fs.c +++ b/port/src/fs.c @@ -284,10 +284,11 @@ void fsFileFree(FILE *f) fclose(f); } -s32 fsCreateDir(const char *path) { +s32 fsCreateDir(const char *path) +{ #ifdef PLATFORM_WIN32 return _mkdir(fsFullPath(path)); #else - return mkdir(fsFullPath(path), S_IRWXU | S_IRWXG); + return mkdir(fsFullPath(path), 0777); #endif } diff --git a/port/src/input.c b/port/src/input.c index ae131dbf5..96e37255e 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -1197,33 +1197,6 @@ s32 inputKeyJustPressed(u32 vk) return result; } -s32 inputMenuAlt1MenuPressed(s32 playerNum) -{ - s32 RSHOULDER = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; - - if (playerNum == 0) { - return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(RSHOULDER); - } - - return inputKeyJustPressed(RSHOULDER); -} - -s32 inputMenuAlt2MenuPressed(s32 playerNum) -{ - s32 LSHOULDER = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; - - if (playerNum == 0) { - return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(LSHOULDER); - } - - return inputKeyJustPressed(LSHOULDER); -} - -s32 inputMenuAltMenuPressed(s32 playerNum) -{ - return inputMenuAlt1MenuPressed(playerNum) || inputMenuAlt2MenuPressed(playerNum); -} - static inline u32 inputContToContKey(const u32 cont) { if (cont == 0) { diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c index 8c4f4e1be..2c308fb9c 100644 --- a/port/src/mpsetups.c +++ b/port/src/mpsetups.c @@ -40,26 +40,29 @@ MP Setup File Format extern struct menudialogdef g_MpSaveSetupNameMenuDialog; -u8 filebuffer[sizeof(struct mpsetupfile)]; - -struct mpsetupfile g_MpSetupFile; -struct mpsetupfile g_ImportMpSetupFile; s16 g_MpCurrentSetup = -1; -u64 g_MpImportExportFilter[2]; +struct mpsetupfile g_MpSetupFile; -#define BUF_READ(dst, buf, type) { dst = *(type*)((buf)); buf += sizeof(type); } -#define BUF_WRITE(src, buf, type) { *((type*)(buf)) = src; buf += sizeof(type); } -#define BUF_WRITEBLOCK(buf, src, size) { \ - memcpy(buf, src, size); \ - buf += size; } +static struct mpsetupfile g_ImportMpSetupFile; +static u64 g_MpImportExportFilter[2]; -static inline void zeroFileBuffer() -{ - memset(filebuffer, 0, sizeof(struct mpsetupfile)); -} +static char g_StatusText[128]; +static char g_TitleImportExportDialog[20]; +static char g_LabelSetDefault[20] = "Set Default\n"; -char g_StatusText[128]; -struct menuitem g_StatusOkMenuItems[] = { +/* extended mpsetup menu definitions */ + +static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data); + +static struct menuitem g_StatusOkMenuItems[] = { { MENUITEMTYPE_LABEL, 0, @@ -79,7 +82,7 @@ struct menuitem g_StatusOkMenuItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_StatusOkDialog = { +static struct menudialogdef g_StatusOkDialog = { MENUDIALOGTYPE_SUCCESS, L_OPTIONS_345, // "Cool!" g_StatusOkMenuItems, @@ -88,7 +91,7 @@ struct menudialogdef g_StatusOkDialog = { NULL, }; -struct menuitem g_StatusErrorMenuItems[] = { +static struct menuitem g_StatusErrorMenuItems[] = { { MENUITEMTYPE_LABEL, 0, @@ -108,6 +111,7 @@ struct menuitem g_StatusErrorMenuItems[] = { { MENUITEMTYPE_END }, }; +/* public */ struct menudialogdef g_StatusErrorDialog = { MENUDIALOGTYPE_DANGER, L_OPTIONS_277, // "Failed" @@ -117,39 +121,7 @@ struct menudialogdef g_StatusErrorDialog = { NULL, }; -// #### Rename setup dialog -MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) -{ - char *name = data->keyboard.string; - s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; - struct setupblock *setup = &g_MpSetupFile.setups[slotindex]; - s32 err; - - switch (operation) { - case MENUOP_GETTEXT: -// strcpy(name, g_MpSetup.name); - strcpy(name, setup->bytes); - break; - case MENUOP_SETTEXT: - strcpy(g_MpSetup.name, name); - break; - case MENUOP_SET: - strcpy(setup->bytes, name); - err = mpsetupSaveSetup(slotindex, true); - if (!err) { - menuPopDialog(); - } - else { - // TODO -// menuPushDialog(&g_FilemgrFileSavedMenuDialog); - } - break; - } - - return 0; -} - -struct menuitem g_RenameSetupItems[] = { +static struct menuitem g_RenameSetupItems[] = { #if VERSION != VERSION_JPN_FINAL { MENUITEMTYPE_LABEL, @@ -171,7 +143,7 @@ struct menuitem g_RenameSetupItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_RenameSetupDialog = { +static struct menudialogdef g_RenameSetupDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t)"Setup Name:\n", g_RenameSetupItems, @@ -180,25 +152,7 @@ struct menudialogdef g_RenameSetupDialog = { NULL, }; -// #### Delete setup dialog -static s32 deleteSetup(); -MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data) -{ - if (operation == MENUOP_SET) { - s32 err = deleteSetup(); - if (!err) { - menuPopDialog(); - menuPopDialog(); - } - else { - // TODO - } - } - - return 0; -} - -struct menuitem g_DeleteSetupItems[] = { +static struct menuitem g_DeleteSetupItems[] = { { MENUITEMTYPE_LABEL, 0, @@ -226,7 +180,7 @@ struct menuitem g_DeleteSetupItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_DeleteSetupDialog = { +static struct menudialogdef g_DeleteSetupDialog = { MENUDIALOGTYPE_DANGER, (uintptr_t)"Delete Setup\n", g_DeleteSetupItems, @@ -235,112 +189,7 @@ struct menudialogdef g_DeleteSetupDialog = { NULL, }; -// #### Import (or Export) settings dialog -s32 importMpSetupFile(u8 op, u8 skipOverlap); -s32 exportMpSetupFile(); -struct menudialogdef g_ManageSettingsDialog; -struct menudialogdef g_ImportOverrideDialog; -MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data) -{ - static const char *labels[] = { - "Select All", - "Select None", - "Import", - "Export", - }; - - s32 op = g_Menus[g_MpPlayerNum].mpsetup.unke24; - - struct mpsetupfile *setupfile = op == MPSETUP_OP_IMPORT ? &g_ImportMpSetupFile : &g_MpSetupFile; - - u8 numsetups = setupfile->numsetups; - u8 numitems = numsetups + 3; - u8 bank = data->list.value < 64 ? 0 : 1; - u8 bit = data->list.value - bank * 64; - u8 hasselection = (g_MpImportExportFilter[0] | g_MpImportExportFilter[1]) != 0; - s32 err; - - switch (operation) { - case MENUOP_GETCOLOUR: { - u32 colour = data->list.unk04; - if (data->list.value == numitems - 1) { - data->list.unk04 = hasselection ? colour : (colour & 0xffffff00) | 0x66; - } - break; - } - case MENUOP_GETOPTIONCOUNT: - data->list.value = numitems; - break; - case MENUOP_GETOPTIONTEXT: - if (data->list.value < numsetups) { - return (uintptr_t) setupfile->setups[data->list.value].bytes; - } - else { - // creates an offset to select the "Export" or "Import" label for the last item - u8 ofs = (data->list.value == numitems - 1) ? op - 1 : 0; - return (intptr_t)labels[data->list.value - numsetups + ofs]; - } - case MENUOP_SET: { - if (data->list.value < setupfile->numsetups) { - g_MpImportExportFilter[bank] ^= (1 << bit); - } - else { - s32 index = data->list.value - setupfile->numsetups; - - switch (index) { - // Select All - case 0: - g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; - break; - // Select None - case 1: - g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = 0; - break; - // Export (or Import) - case 2: - if (hasselection) { - err = op == MPSETUP_OP_IMPORT ? importMpSetupFile(0, false) : exportMpSetupFile(); - if (!err) { - menuPopDialog(); - if (op == MPSETUP_OP_IMPORT) { - // back to the 'Manage Settings' screen - menuPopDialog(); - menuPushDialog(&g_ManageSettingsDialog); - } - else { - sprintf(g_StatusText, "File %s.bin\nwritten to the folder 'exported'\n", MPSETUP_FILENAME_EXP); - menuPushDialog(&g_StatusOkDialog); - } - } - else { - if (err == MPSETUP_IMPORT_CONFLICT) { - menuPopDialog(); - menuPushDialog(&g_ImportOverrideDialog); - } - else { - menuPushDialog(&g_StatusErrorDialog); - } - } - } - break; - } - } - } - case MENUOP_GETSELECTEDINDEX: - data->list.value = 0x000fffff; - break; - case MENUOP_GETLISTITEMCHECKBOX: { - if (data->list.value < numsetups) { - data->list.unk04 = (g_MpImportExportFilter[bank] & (1 << bit)); - } - break; - } - } - - return 0; -} - -struct menuitem g_ImportExportItems[] = { +static struct menuitem g_ImportExportItems[] = { { MENUITEMTYPE_LIST, 0, @@ -352,8 +201,7 @@ struct menuitem g_ImportExportItems[] = { { MENUITEMTYPE_END }, }; -char g_TitleImportExportDialog[20]; -struct menudialogdef g_ImportExportDialog = { +static struct menudialogdef g_ImportExportDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t) g_TitleImportExportDialog, g_ImportExportItems, @@ -362,40 +210,7 @@ struct menudialogdef g_ImportExportDialog = { NULL, }; - -// #### Manage Import/Export settings dialog -s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op); -MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data) -{ - switch (operation) { - case MENUOP_SET: - { - if (item->param == MPSETUP_OP_IMPORT) { - strcpy(g_TitleImportExportDialog, "Import Settings\n"); - if (fsFileSize("$S/" MPSETUP_FILENAME_EXP ".bin") < 0) { - sprintf(g_StatusText, - "No import file found.\n" - "Place the file %s.bin\n" - "Next to your %s.bin file\n", MPSETUP_FILENAME_EXP, MPSETUP_FILENAME); - menuPushDialog(&g_StatusErrorDialog); - return 0; - } - - mpsetupLoadFile(&g_ImportMpSetupFile, MPSETUP_OP_IMPORT); - } - else { - strcpy(g_TitleImportExportDialog, "Export Settings\n"); - } - g_Menus[g_MpPlayerNum].mpsetup.unke24 = item->param; - g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; - menuPushDialog(&g_ImportExportDialog); - } - } - - return 0; -} - -struct menuitem g_ManageImportExportItems[] = { +static struct menuitem g_ManageImportExportItems[] = { { MENUITEMTYPE_SELECTABLE, MPSETUP_OP_IMPORT, @@ -415,7 +230,7 @@ struct menuitem g_ManageImportExportItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_ManageImportExportDialog = { +static struct menudialogdef g_ManageImportExportDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t) "Import/Export\n", g_ManageImportExportItems, @@ -424,56 +239,20 @@ struct menudialogdef g_ManageImportExportDialog = { NULL, }; -// #### Manage settings dialog -struct menudialogdef g_ManageSetupDialog; -char g_LabelSetDefault[20] = "Set Default\n"; -MenuItemHandlerResult mpSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data) -{ - switch (operation) { - case MENUOP_GETCOLOUR: { - u32 colour = data->list.unk04; - if (data->list.value + 1 == g_MpSetupFile.defaultsetup) { - data->list.unk04 = (colour & 0xffff0000) | 0xff; - } - break; - } - case MENUOP_GETOPTIONCOUNT: - data->list.value = g_MpSetupFile.numsetups; - break; - case MENUOP_GETOPTIONTEXT: - return (uintptr_t) g_MpSetupFile.setups[data->list.value].bytes; - case MENUOP_SET: - { - g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value; - if (data->list.value == g_MpSetupFile.defaultsetup - 1) { - strcpy(g_LabelSetDefault, "Clear Default\n"); - } - else { - strcpy(g_LabelSetDefault, "Set Default\n"); - } - menuPushDialog(&g_ManageSetupDialog); - } - case MENUOP_GETSELECTEDINDEX: - data->list.value = 0xfffff; - break; - } - - return 0; -} - -struct menuitem g_MpManageSettingsListItems[] = { +static struct menuitem g_MpManageSettingsListItems[] = { { MENUITEMTYPE_LIST, 0, MENUITEMFLAG_LABEL_CUSTOMCOLOUR, 160, 0x00000042, - mpSelectSetupHandler, + menuhandlerSelectSetupHandler, }, { MENUITEMTYPE_END }, }; +/* public */ struct menudialogdef g_ManageSettingsDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t) "Manage Settings\n", @@ -483,46 +262,7 @@ struct menudialogdef g_ManageSettingsDialog = { &g_ManageImportExportDialog, }; -struct menudialogdef g_RenameSetupDialog; -static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data) -{ - if (operation == MENUOP_SET) { - menuPushDialog(&g_RenameSetupDialog); - } - - return 0; -} - -static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data) -{ - if (operation == MENUOP_SET) { - menuPushDialog(&g_DeleteSetupDialog); - } - - return 0; -} - -static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data) -{ - if (operation == MENUOP_SET) { - s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex; - // clicked on "clear default" - if (selected == g_MpSetupFile.defaultsetup - 1) { - g_MpSetupFile.defaultsetup = -1; - strcpy(g_LabelSetDefault, "Set Default\n"); - } - else { - g_MpSetupFile.defaultsetup = selected + 1; - strcpy(g_LabelSetDefault, "Clear Default\n"); - } - - mpsetupSaveCurrentFile(); - } - - return 0; -} - -struct menuitem g_ManageSetupItems[] = { +static struct menuitem g_ManageSetupItems[] = { { MENUITEMTYPE_SELECTABLE, 0, @@ -566,7 +306,7 @@ struct menuitem g_ManageSetupItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_ManageSetupDialog = { +static struct menudialogdef g_ManageSetupDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t)"Manage Setup", g_ManageSetupItems, @@ -575,28 +315,10 @@ struct menudialogdef g_ManageSetupDialog = { NULL, }; -// #### Import: overwrite dialog -MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data) -{ - if (operation == MENUOP_SET) { - s32 err = importMpSetupFile(item->param, true); - if (!err) { - menuPopDialog(); - menuPushDialog(&g_ManageSettingsDialog); - } - else { - menuPushDialog(&g_StatusErrorDialog); - } - } - - return 0; -} - -struct menuitem g_ImportOverrideItems[] = { +static struct menuitem g_ImportOverrideItems[] = { { MENUITEMTYPE_LABEL, 0, -// MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING | MENUITEMFLAG_SMALLFONT, MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, (uintptr_t) "How to resolve setups\nwith the same name?\n", 0, @@ -621,7 +343,7 @@ struct menuitem g_ImportOverrideItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_ImportOverrideDialog = { +static struct menudialogdef g_ImportOverrideDialog = { MENUDIALOGTYPE_DEFAULT, (uintptr_t)"Name Conflicts\n", g_ImportOverrideItems, @@ -629,60 +351,40 @@ struct menudialogdef g_ImportOverrideDialog = { MENUDIALOGFLAG_LITERAL_TEXT, NULL, }; -// -------------------------------------------------------- -static void mpsetupDeserialize(struct mpsetupfile *setupfile, u8 *buffer) + +/* common utils */ + +static s32 mpsetupDeserialize(FILE *f, struct mpsetupfile *setupfile) { - BUF_READ(setupfile->version, buffer, u8); - BUF_READ(setupfile->defaultsetup, buffer, u8); - BUF_READ(setupfile->numsetups, buffer, u8); + s32 rx = 0; + + rx += fread(&setupfile->version, sizeof(setupfile->version), 1, f); + rx += fread(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f); + rx += fread(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f); for (int i = 0; i < setupfile->numsetups; ++i) { - memcpy(setupfile->setups[i].bytes, buffer, MPSETUP_BLOCKSIZE); - buffer += MPSETUP_BLOCKSIZE; + rx += fread(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f); } + + return rx; } -void mpsetupSerialize(u8 *buffer, struct mpsetupfile *setupfile) +static s32 mpsetupSerialize(FILE *f, struct mpsetupfile *setupfile) { - BUF_WRITE(setupfile->version, buffer, u8); - BUF_WRITE(setupfile->defaultsetup, buffer, u8); - BUF_WRITE(setupfile->numsetups, buffer, u8); + s32 wx = 0; + + wx += fwrite(&setupfile->version, sizeof(setupfile->version), 1, f); + wx += fwrite(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f); + wx += fwrite(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f); for (int i = 0; i < setupfile->numsetups; ++i) { - BUF_WRITEBLOCK(buffer, setupfile->setups[i].bytes, MPSETUP_BLOCKSIZE) + wx += fwrite(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f); } + + return wx; } -static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile); -static s32 deleteSetup() -{ - s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; - for (int i = slotindex; i < g_MpSetupFile.numsetups - 1; ++i) { - u8* dst = g_MpSetupFile.setups[i].bytes; - u8* src = g_MpSetupFile.setups[i+1].bytes; - - memcpy(dst, src, MPSETUP_BLOCKSIZE); - } - - if (g_MpCurrentSetup > slotindex) { - g_MpCurrentSetup--; - } - else if (g_MpCurrentSetup == slotindex) { - g_MpCurrentSetup = -1; - } - - if (g_MpSetupFile.defaultsetup > slotindex + 1) { - g_MpSetupFile.defaultsetup--; - } - else if (g_MpSetupFile.defaultsetup == slotindex + 1) { - g_MpSetupFile.defaultsetup = 0; - } - - g_MpSetupFile.numsetups--; - return mpsetupSaveCurrentFile(); -} - -static FILE *openMpSetupFile(u8 mode, u8 op) { +static FILE *mpsetupOpenFile(bool write, u8 op) { const char *filename = fsFullPath("$S/" MPSETUP_FILENAME ".bin"); if (op == MPSETUP_OP_EXPORT) { @@ -693,8 +395,7 @@ static FILE *openMpSetupFile(u8 mode, u8 op) { } } filename = fsFullPath(MPSETUP_EXPORTDIR MPSETUP_FILENAME_EXP ".bin"); - } - else if (op == MPSETUP_OP_IMPORT) { + } else if (op == MPSETUP_OP_IMPORT) { // same name as export but different folder filename = fsFullPath("$S/" MPSETUP_FILENAME_EXP ".bin"); } @@ -707,15 +408,10 @@ static FILE *openMpSetupFile(u8 mode, u8 op) { fsFileFree(f); } - if (mode == 'r') { - f = fsFileOpenRead(filename); - } - else if (mode == 'w') { + if (write) { f = fsFileOpenWrite(filename); - } - else { - sysLogPrintf(LOG_ERROR, "openMpSetupFile: invalid file mode"); - return NULL; + } else { + f = fsFileOpenRead(filename); } if (f == NULL) { @@ -726,22 +422,33 @@ static FILE *openMpSetupFile(u8 mode, u8 op) { return f; } -s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) +static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) { - FILE *f = openMpSetupFile('r', op); + FILE *f = mpsetupOpenFile(true, op); if (f == NULL) { return -1; } - zeroFileBuffer(); + s32 nwritten = mpsetupSerialize(f, setupfile); + if (nwritten < 1) { + fsFileFree(f); + sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file"); + snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write the setup file\n"); + return -1; + } - fseek(f, 0L, SEEK_END); - size_t size = ftell(f); - fseek(f, 0, SEEK_SET); + fsFileFree(f); + return 0; +} - fread(filebuffer, 1, size, f); +static s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) +{ + FILE *f = mpsetupOpenFile(false, op); + if (f == NULL) { + return -1; + } - mpsetupDeserialize(setupfile, filebuffer); + mpsetupDeserialize(f, setupfile); if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup > 0) { mpsetupLoadSetup(setupfile->defaultsetup - 1); @@ -752,18 +459,7 @@ s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) return 0; } -s32 mpsetupLoadCurrentFile() -{ - return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); -} - -s32 mpsetupSaveCurrentFile() -{ - g_MpSetupFile.version = MPSETUP_VERSION; - return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); -} - -s32 importMpSetupFile(u8 op, u8 skipOverlap) +static s32 mpsetupImportFile(u8 op, u8 skipOverlap) { if (!skipOverlap) { // check for names overlap @@ -790,7 +486,7 @@ s32 importMpSetupFile(u8 op, u8 skipOverlap) s16 importIdx = overlapIdx; if (overlapIdx < 0 || op == MPSETUP_IMPORT_ADD) { if (g_MpSetupFile.numsetups == MPSETUP_MAXSETUPS) { - sprintf(g_StatusText, "Number of setups exceeds %d\n", MPSETUP_MAXSETUPS); + snprintf(g_StatusText, sizeof(g_StatusText), "Number of setups exceeds %d\n", MPSETUP_MAXSETUPS); return -1; } importIdx = g_MpSetupFile.numsetups++; @@ -802,7 +498,7 @@ s32 importMpSetupFile(u8 op, u8 skipOverlap) return mpsetupSaveCurrentFile(); } -s32 exportMpSetupFile() +static s32 mpsetupExportFile(void) { struct mpsetupfile expMpSetupFile; expMpSetupFile.numsetups = 0; @@ -825,34 +521,320 @@ s32 exportMpSetupFile() s32 err = mpsetupSaveFile(MPSETUP_OP_EXPORT, &expMpSetupFile); if (err) { - sprintf(g_StatusText, "Unable to write\nsetup file\n"); + snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write\nsetup file\n"); } return err; } -static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) +static s32 mpsetupDelete(void) { - zeroFileBuffer(); - mpsetupSerialize(filebuffer, setupfile); - - FILE *f = openMpSetupFile('w', op); - if (f == NULL) { - return -1; + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + for (int i = slotindex; i < g_MpSetupFile.numsetups - 1; ++i) { + u8* dst = g_MpSetupFile.setups[i].bytes; + u8* src = g_MpSetupFile.setups[i+1].bytes; + memcpy(dst, src, MPSETUP_BLOCKSIZE); } - size_t nwritten = fwrite(filebuffer, sizeof(struct mpsetupfile), 1, f); - if (nwritten < 1) { - fsFileFree(f); - sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file"); - sprintf(g_StatusText, "Unable to write the setup file\n"); - return -1; + if (g_MpCurrentSetup > slotindex) { + g_MpCurrentSetup--; + } + else if (g_MpCurrentSetup == slotindex) { + g_MpCurrentSetup = -1; + } + + if (g_MpSetupFile.defaultsetup > slotindex + 1) { + g_MpSetupFile.defaultsetup--; + } + else if (g_MpSetupFile.defaultsetup == slotindex + 1) { + g_MpSetupFile.defaultsetup = 0; + } + + g_MpSetupFile.numsetups--; + return mpsetupSaveCurrentFile(); +} + +/* menu handlers */ + +// Rename setup dialog +static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) +{ + char *name = data->keyboard.string; + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + struct setupblock *setup = &g_MpSetupFile.setups[slotindex]; + s32 err; + + switch (operation) { + case MENUOP_GETTEXT: + strcpy(name, setup->bytes); + break; + case MENUOP_SETTEXT: + strcpy(g_MpSetup.name, name); + break; + case MENUOP_SET: + strcpy(setup->bytes, name); + err = mpsetupSaveSetup(slotindex, true); + if (!err) { + menuPopDialog(); + } else { + // TODO + // menuPushDialog(&g_FilemgrFileSavedMenuDialog); + } + break; } - fsFileFree(f); return 0; } +// Delete setup dialog +static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 err = mpsetupDelete(); + if (!err) { + menuPopDialog(); + menuPopDialog(); + } else { + // TODO + } + } + + return 0; +} + +// Import (or Export) settings dialog +static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *labels[] = { + "Select All", + "Select None", + "Import", + "Export", + }; + + s32 op = g_Menus[g_MpPlayerNum].mpsetup.unke24; + + struct mpsetupfile *setupfile = op == MPSETUP_OP_IMPORT ? &g_ImportMpSetupFile : &g_MpSetupFile; + + u8 numsetups = setupfile->numsetups; + u8 numitems = numsetups + 3; + u8 bank = data->list.value < 64 ? 0 : 1; + u8 bit = data->list.value - bank * 64; + u8 hasselection = (g_MpImportExportFilter[0] | g_MpImportExportFilter[1]) != 0; + u8 ofs; + s32 err; + u32 colour; + s32 index; + + switch (operation) { + case MENUOP_GETCOLOUR: + colour = data->list.unk04; + if (data->list.value == numitems - 1) { + data->list.unk04 = hasselection ? colour : (colour & 0xffffff00) | 0x66; + } + break; + + case MENUOP_GETOPTIONCOUNT: + data->list.value = numitems; + break; + + case MENUOP_GETOPTIONTEXT: + if (data->list.value < numsetups) { + return (uintptr_t) setupfile->setups[data->list.value].bytes; + } else { + // creates an offset to select the "Export" or "Import" label for the last item + ofs = (data->list.value == numitems - 1) ? op - 1 : 0; + return (intptr_t)labels[data->list.value - numsetups + ofs]; + } + + case MENUOP_SET: + if (data->list.value < setupfile->numsetups) { + g_MpImportExportFilter[bank] ^= (1 << bit); + } else { + index = data->list.value - setupfile->numsetups; + + switch (index) { + // Select All + case 0: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + break; + // Select None + case 1: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = 0; + break; + // Export (or Import) + case 2: + if (hasselection) { + err = op == MPSETUP_OP_IMPORT ? mpsetupImportFile(0, false) : mpsetupExportFile(); + if (!err) { + menuPopDialog(); + if (op == MPSETUP_OP_IMPORT) { + // back to the 'Manage Settings' screen + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } else { + snprintf(g_StatusText, sizeof(g_StatusText), "File %s.bin\nwritten to the folder 'exported'\n", MPSETUP_FILENAME_EXP); + menuPushDialog(&g_StatusOkDialog); + } + } else { + if (err == MPSETUP_IMPORT_CONFLICT) { + menuPopDialog(); + menuPushDialog(&g_ImportOverrideDialog); + } else { + menuPushDialog(&g_StatusErrorDialog); + } + } + } + break; + } + } + break; + + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0x000fffff; + break; + + case MENUOP_GETLISTITEMCHECKBOX: + if (data->list.value < numsetups) { + data->list.unk04 = (g_MpImportExportFilter[bank] & (1 << bit)); + } + break; + } + + return 0; +} + +// Import: overwrite dialog +static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 err = mpsetupImportFile(item->param, true); + if (!err) { + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } else { + menuPushDialog(&g_StatusErrorDialog); + } + } + + return 0; +} + +// Manage Import/Export settings dialog +static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_SET: + if (item->param == MPSETUP_OP_IMPORT) { + strcpy(g_TitleImportExportDialog, "Import Settings\n"); + if (fsFileSize("$S/" MPSETUP_FILENAME_EXP ".bin") < 0) { + snprintf( + g_StatusText, sizeof(g_StatusText), + "No import file found.\n" + "Place the file %s.bin\n" + "Next to your %s.bin file\n", + MPSETUP_FILENAME_EXP, MPSETUP_FILENAME + ); + menuPushDialog(&g_StatusErrorDialog); + return 0; + } + + mpsetupLoadFile(&g_ImportMpSetupFile, MPSETUP_OP_IMPORT); + } else { + strcpy(g_TitleImportExportDialog, "Export Settings\n"); + } + g_Menus[g_MpPlayerNum].mpsetup.unke24 = item->param; + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + menuPushDialog(&g_ImportExportDialog); + break; + } + + return 0; +} + +// Manage settings dialog +static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data) +{ + u32 colour; + switch (operation) { + case MENUOP_GETCOLOUR: + colour = data->list.unk04; + if (data->list.value + 1 == g_MpSetupFile.defaultsetup) { + data->list.unk04 = (colour & 0xffff0000) | 0xff; + } + break; + case MENUOP_GETOPTIONCOUNT: + data->list.value = g_MpSetupFile.numsetups; + break; + case MENUOP_GETOPTIONTEXT: + return (uintptr_t) g_MpSetupFile.setups[data->list.value].bytes; + case MENUOP_SET: + g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value; + if (data->list.value == g_MpSetupFile.defaultsetup - 1) { + strcpy(g_LabelSetDefault, "Clear Default\n"); + } else { + strcpy(g_LabelSetDefault, "Set Default\n"); + } + menuPushDialog(&g_ManageSetupDialog); + break; + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0xfffff; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_RenameSetupDialog); + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_DeleteSetupDialog); + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + // clicked on "clear default" + if (selected == g_MpSetupFile.defaultsetup - 1) { + g_MpSetupFile.defaultsetup = -1; + strcpy(g_LabelSetDefault, "Set Default\n"); + } else { + g_MpSetupFile.defaultsetup = selected + 1; + strcpy(g_LabelSetDefault, "Clear Default\n"); + } + + mpsetupSaveCurrentFile(); + } + + return 0; +} + +/* public functions */ + +s32 mpsetupLoadCurrentFile(void) +{ + return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); +} + +s32 mpsetupSaveCurrentFile(void) +{ + g_MpSetupFile.version = MPSETUP_VERSION; + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); +} + s32 mpsetupSaveSetup(s32 slotindex, u8 savefile) { struct savebuffer setup; @@ -880,7 +862,7 @@ void mpsetupLoadSetup(s32 index) g_MpCurrentSetup = index; } -void mpsetupCopyAllFromPak() +void mpsetupCopyAllFromPak(void) { if (fsFileSize("$S/" MPSETUP_FILENAME ".bin") > 0) { return; diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index a9d920c49..d06fb8c9f 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -43,6 +43,33 @@ extern struct menudialogdef g_FilemgrErrorMenuDialog; extern s32 g_MpWeaponSetNum; #endif +static inline s32 menuAlt1Pressed(s32 playerNum) +{ + const s32 rshoulderKey = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(rshoulderKey); + } + + return inputKeyJustPressed(rshoulderKey); +} + +static inline s32 menuAlt2Pressed(s32 playerNum) +{ + const s32 lshoulderKey = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(lshoulderKey); + } + + return inputKeyJustPressed(lshoulderKey); +} + +static inline s32 menuAltPressed(s32 playerNum) +{ + return menuAlt1Pressed(playerNum) || menuAlt2Pressed(playerNum); +} + MenuItemHandlerResult menuhandlerMpDropOut(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_SET) { @@ -2281,8 +2308,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * if (presets && data->list.value < mpGetNumUnlockedPresets()) { mp0f18dec4(data->list.value); g_MpCurrentSetup = -1; - } - else { + } else { mpsetupLoadSetup(data->list.value - numpresets); } @@ -2311,8 +2337,7 @@ MenuItemHandlerResult mpLoadSettingsMenuHandler(s32 operation, struct menuitem * case MENUOP_LISTITEMFOCUS: if (presets && data->list.value < mpGetNumUnlockedPresets()) { g_Menus[g_MpPlayerNum].mpsetup.slotindex = 0xffff; - } - else { + } else { g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value - numpresets; } break; @@ -2566,6 +2591,16 @@ MenuDialogHandlerResult menudialogMpSimulant(s32 operation, struct menudialogdef return false; } +MenuDialogHandlerResult mpLoadSettingsDialogHandler(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data) +{ + if (operation == MENUOP_TICK) { + if (menuAltPressed(g_MpPlayerNum)) { + u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; + g_Menus[g_MpPlayerNum].mpsetup.showpresets = 1 - presets; + } + } +} + struct menuitem g_MpCharacterMenuItems[] = { { MENUITEMTYPE_LABEL, @@ -2660,21 +2695,11 @@ struct menuitem g_MpLoadSettingsMenuItems[] = { { MENUITEMTYPE_END }, }; -MenuDialogHandlerResult loadSettingsDialogHandler(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data) -{ - if (operation == MENUOP_TICK) { - if (inputMenuAltMenuPressed(g_MpPlayerNum)) { - u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; - g_Menus[g_MpPlayerNum].mpsetup.showpresets = 1 - presets; - } - } -} - struct menudialogdef g_MpLoadSettingsMenuDialog = { MENUDIALOGTYPE_DEFAULT, L_MPMENU_139, // "Load Game Settings" g_MpLoadSettingsMenuItems, - loadSettingsDialogHandler, + mpLoadSettingsDialogHandler, MENUDIALOGFLAG_CLOSEONSELECT, NULL, }; From 4b8e1d52b502aaf7347dadc7d3397b539d28f63c Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 27 Apr 2025 15:54:39 +0200 Subject: [PATCH 21/32] port: move menuAlt functions to menu.c and make them global --- src/game/menu.c | 27 +++++++++++++++++++++++++++ src/game/mplayer/setup.c | 29 +---------------------------- src/include/game/menu.h | 4 ++++ 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/game/menu.c b/src/game/menu.c index 1e9159cae..7fdb9e2f3 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -147,6 +147,33 @@ s32 g_MouseDimmedMode = false; s32 g_MouseEndDeferredSlider = false; #endif +s32 menuAlt1Pressed(s32 playerNum) +{ + const s32 rshoulderKey = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(rshoulderKey); + } + + return inputKeyJustPressed(rshoulderKey); +} + +s32 menuAlt2Pressed(s32 playerNum) +{ + const s32 lshoulderKey = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(lshoulderKey); + } + + return inputKeyJustPressed(lshoulderKey); +} + +s32 menuAltAnyPressed(s32 playerNum) +{ + return menuAlt1Pressed(playerNum) || menuAlt2Pressed(playerNum); +} + void menuPlaySound(s32 menusound) { s32 sound = -1; diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index d06fb8c9f..8e7eefe61 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -43,33 +43,6 @@ extern struct menudialogdef g_FilemgrErrorMenuDialog; extern s32 g_MpWeaponSetNum; #endif -static inline s32 menuAlt1Pressed(s32 playerNum) -{ - const s32 rshoulderKey = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; - - if (playerNum == 0) { - return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(rshoulderKey); - } - - return inputKeyJustPressed(rshoulderKey); -} - -static inline s32 menuAlt2Pressed(s32 playerNum) -{ - const s32 lshoulderKey = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; - - if (playerNum == 0) { - return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(lshoulderKey); - } - - return inputKeyJustPressed(lshoulderKey); -} - -static inline s32 menuAltPressed(s32 playerNum) -{ - return menuAlt1Pressed(playerNum) || menuAlt2Pressed(playerNum); -} - MenuItemHandlerResult menuhandlerMpDropOut(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_SET) { @@ -2594,7 +2567,7 @@ MenuDialogHandlerResult menudialogMpSimulant(s32 operation, struct menudialogdef MenuDialogHandlerResult mpLoadSettingsDialogHandler(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data) { if (operation == MENUOP_TICK) { - if (menuAltPressed(g_MpPlayerNum)) { + if (menuAltAnyPressed(g_MpPlayerNum)) { u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; g_Menus[g_MpPlayerNum].mpsetup.showpresets = 1 - presets; } diff --git a/src/include/game/menu.h b/src/include/game/menu.h index 419fb659e..37bdbc5e5 100644 --- a/src/include/game/menu.h +++ b/src/include/game/menu.h @@ -113,6 +113,10 @@ MenuItemHandlerResult menuhandlerRepairPak(s32 operation, struct menuitem *item, MenuItemHandlerResult menuhandlerRetrySavePak(s32 operation, struct menuitem *item, union handlerdata *data); MenuItemHandlerResult menuhandlerWarnRepairPak(s32 operation, struct menuitem *item, union handlerdata *data); +s32 menuAlt1Pressed(s32 playerNum); +s32 menuAlt2Pressed(s32 playerNum); +s32 menuAltAnyPressed(s32 playerNum); + extern const char var7f1b2658[]; extern const char var7f1b265c[]; extern const char var7f1b2660[]; From 872aa3a4a4e83bc5b610f43bb67a69bceeaf022d Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Fri, 25 Oct 2024 19:28:29 -0700 Subject: [PATCH 22/32] rename `struct menudata.unk008 -> prevmenuroot` --- src/game/menu.c | 6 +++--- src/game/menutick.c | 26 +++++++++++++------------- src/include/types.h | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/game/menu.c b/src/game/menu.c index 7fdb9e2f3..cb10ffdec 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -3608,7 +3608,7 @@ void func0f0f820c(struct menudialogdef *dialogdef, s32 root) g_MpPlayerNum = prevplayernum; - g_MenuData.unk008 = root; + g_MenuData.prevmenuroot = root; g_MenuData.unk00c = dialogdef; } @@ -3683,7 +3683,7 @@ void menuPushRootDialog(struct menudialogdef *dialogdef, s32 root) g_Menus[g_MpPlayerNum].unk820 = 1; g_MenuData.root = root; - g_MenuData.unk008 = -1; + g_MenuData.prevmenuroot = -1; g_MenuData.unk5d5_02 = false; if (root == MENUROOT_MAINMENU @@ -4006,7 +4006,7 @@ void menuReset(void) g_MenuData.unk668 = -1; g_MenuData.unk00c = 0; - g_MenuData.unk008 = -1; + g_MenuData.prevmenuroot = -1; g_MenuData.count = 0; g_MenuData.root = 0; g_MenuData.unk010 = 0; diff --git a/src/game/menutick.c b/src/game/menutick.c index fcd0df3b8..1684caa06 100644 --- a/src/game/menutick.c +++ b/src/game/menutick.c @@ -285,7 +285,7 @@ void menuTick(void) var8006294c = 1; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { - if (g_MenuData.unk008 == -1) { + if (g_MenuData.prevmenuroot == -1) { g_MpSetup.chrslots &= 0xfff0; } @@ -295,7 +295,7 @@ void menuTick(void) if (g_Menus[i].curdialog) { g_Menus[i].playernum = g_MpNumJoined++; - if (g_MenuData.unk008 == -1) { + if (g_MenuData.prevmenuroot == -1) { g_MpSetup.chrslots |= (1 << i); } } @@ -493,23 +493,23 @@ void menuTick(void) } } - if ((g_MenuData.unk5d5_06 || g_MenuData.unk008 != -1) && sp344 == false) { + if ((g_MenuData.unk5d5_06 || g_MenuData.prevmenuroot != -1) && sp344 == false) { if ((g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) - && g_MenuData.unk008 == -1) { + && g_MenuData.prevmenuroot == -1) { if (g_Vars.mpsetupmenu == MPSETUPMENU_GENERAL) { - g_MenuData.unk008 = MENUROOT_MAINMENU; + g_MenuData.prevmenuroot = MENUROOT_MAINMENU; g_MenuData.unk00c = IS4MB() ? &g_CiMenuViaPauseMenuDialog : &g_CiMenuViaPcMenuDialog; } else if (IS4MB()) { - g_MenuData.unk008 = MENUROOT_4MBMAINMENU; + g_MenuData.prevmenuroot = MENUROOT_4MBMAINMENU; g_MenuData.unk00c = &g_MainMenu4MbMenuDialog; } else { - g_MenuData.unk008 = MENUROOT_MPSETUP; + g_MenuData.prevmenuroot = MENUROOT_MPSETUP; g_MenuData.unk00c = &g_CombatSimulatorMenuDialog; } } - if (g_MenuData.unk008 != -1) { - if (g_MenuData.unk008 == -5) { + if (g_MenuData.prevmenuroot != -1) { + if (g_MenuData.prevmenuroot == -5) { // Match is beginning mpStartMatch(); menuStop(); @@ -518,7 +518,7 @@ void menuTick(void) bossfileSave(); g_Vars.modifiedfiles &= ~MODFILE_MPSETUP; } - } else if (g_MenuData.unk008 == -6) { + } else if (g_MenuData.prevmenuroot == -6) { // Match is ending s32 playernum = 0; @@ -561,7 +561,7 @@ void menuTick(void) playernum++; } } - } else if (g_MenuData.unk008 == -7) { + } else if (g_MenuData.prevmenuroot == -7) { menuStop(); g_FileState = FILESTATE_CHANGINGAGENT; gamefileLoadDefaults(&g_GameFile); @@ -570,7 +570,7 @@ void menuTick(void) musicQueueStopAllEvent(); } else { bool startmusic = false; - menuPushRootDialog(g_MenuData.unk00c, g_MenuData.unk008); + menuPushRootDialog(g_MenuData.unk00c, g_MenuData.prevmenuroot); sp344 = true; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { @@ -598,7 +598,7 @@ void menuTick(void) } g_MenuData.unk00c = NULL; - g_MenuData.unk008 = -1; + g_MenuData.prevmenuroot = -1; } else { switch (g_MenuData.root) { case MENUROOT_ENDSCREEN: diff --git a/src/include/types.h b/src/include/types.h index 1ed8ee9d9..a2bcd9d6e 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -4785,7 +4785,7 @@ struct menudata_5d8 { struct menudata { /*0x000*/ s32 count; /*0x004*/ s32 root; - /*0x008*/ s32 unk008; // also a menuroot constant + /*0x008*/ s32 prevmenuroot; // also a menuroot constant /*0x00c*/ struct menudialogdef *unk00c; /*0x010*/ f32 unk010; /*0x014*/ u8 bg; From 039eb8b562b86aa5848b142cbaa8f4534776000c Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Fri, 25 Oct 2024 19:47:44 -0700 Subject: [PATCH 23/32] rename `struct menudata.unk00c-> prevmenudata` --- src/game/menu.c | 4 ++-- src/game/menutick.c | 10 +++++----- src/include/types.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/game/menu.c b/src/game/menu.c index cb10ffdec..705beed9a 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -3609,7 +3609,7 @@ void func0f0f820c(struct menudialogdef *dialogdef, s32 root) g_MpPlayerNum = prevplayernum; g_MenuData.prevmenuroot = root; - g_MenuData.unk00c = dialogdef; + g_MenuData.prevmenudialog = dialogdef; } void menuSetBackground(s32 bg) @@ -4005,7 +4005,7 @@ void menuReset(void) } g_MenuData.unk668 = -1; - g_MenuData.unk00c = 0; + g_MenuData.prevmenudialog = 0; g_MenuData.prevmenuroot = -1; g_MenuData.count = 0; g_MenuData.root = 0; diff --git a/src/game/menutick.c b/src/game/menutick.c index 1684caa06..ca7606067 100644 --- a/src/game/menutick.c +++ b/src/game/menutick.c @@ -498,13 +498,13 @@ void menuTick(void) && g_MenuData.prevmenuroot == -1) { if (g_Vars.mpsetupmenu == MPSETUPMENU_GENERAL) { g_MenuData.prevmenuroot = MENUROOT_MAINMENU; - g_MenuData.unk00c = IS4MB() ? &g_CiMenuViaPauseMenuDialog : &g_CiMenuViaPcMenuDialog; + g_MenuData.prevmenudialog = IS4MB() ? &g_CiMenuViaPauseMenuDialog : &g_CiMenuViaPcMenuDialog; } else if (IS4MB()) { g_MenuData.prevmenuroot = MENUROOT_4MBMAINMENU; - g_MenuData.unk00c = &g_MainMenu4MbMenuDialog; + g_MenuData.prevmenudialog = &g_MainMenu4MbMenuDialog; } else { g_MenuData.prevmenuroot = MENUROOT_MPSETUP; - g_MenuData.unk00c = &g_CombatSimulatorMenuDialog; + g_MenuData.prevmenudialog = &g_CombatSimulatorMenuDialog; } } @@ -570,7 +570,7 @@ void menuTick(void) musicQueueStopAllEvent(); } else { bool startmusic = false; - menuPushRootDialog(g_MenuData.unk00c, g_MenuData.prevmenuroot); + menuPushRootDialog(g_MenuData.prevmenudialog, g_MenuData.prevmenuroot); sp344 = true; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { @@ -597,7 +597,7 @@ void menuTick(void) } } - g_MenuData.unk00c = NULL; + g_MenuData.prevmenudialog = NULL; g_MenuData.prevmenuroot = -1; } else { switch (g_MenuData.root) { diff --git a/src/include/types.h b/src/include/types.h index a2bcd9d6e..ca5e2b916 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -4786,7 +4786,7 @@ struct menudata { /*0x000*/ s32 count; /*0x004*/ s32 root; /*0x008*/ s32 prevmenuroot; // also a menuroot constant - /*0x00c*/ struct menudialogdef *unk00c; + /*0x00c*/ struct menudialogdef *prevmenudialog; /*0x010*/ f32 unk010; /*0x014*/ u8 bg; /*0x015*/ u8 nextbg; From a2428aac1e4ee900fa4934aafa2b734cfdd8c16b Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Fri, 2 May 2025 11:53:02 -0500 Subject: [PATCH 24/32] working to document format used for 16 bit depth integers --- src/game/game_13c510.c | 28 ++++++++++++++++++++++++---- src/include/game/game_13c510.h | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index c2b4b81e9..95da0f8a8 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -44,11 +44,31 @@ void artifactsTick(void) schedIncrementFrontArtifacts(); } -u16 func0f13c574(f32 arg0) +u16 floatToIntDepth(f32 arg0) { + /** + * Method to convert a 32 bit floating point depth value + * to the unsigned 16 bit integer depth format used by + * the zbuffer available on the N64. The argument arg0 + * represents z normalized to [0, 1] * 32704.0f. + * + * It works by scaling the depth value up to a large + * integer and applying different scaling factors + * before scaling back down to an unsigned 16 bit + * integer. It is probably intended to reduce z-fighting + * by adding more differentiation to distant z values. + * The scaling is done using bitwise operations + * to form the most & least significant bytes of the + * resulting 16 bit integer, likely because bit shift + * operations are faster than division. + * + * TO DO: figure why the output appears with a >> 2 shift. + * It might be worth it to simplify some of the externally + * applied scaling factors if it can match the N64 compilation. + */ u32 value = arg0 * 8.0f; - u32 left; - u32 right = value; + u32 left; // forms the most significant byte of the final u16 + u32 right = value; // forms the least significant byte of the final u16 if (value > 0x3f800) { right = value & 0x7ff; @@ -359,7 +379,7 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) #ifndef PLATFORM_N64 artifact->unk02 = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); #endif - artifact->unk04 = func0f13c574(f0) >> 2; + artifact->unk04 = floatToIntDepth(f0) >> 2; artifact->unk08 = &g_ZbufPtr1[viGetWidth() * yi + xi]; artifact->light = &roomlights[i]; artifact->type = ARTIFACTTYPE_GLARE; diff --git a/src/include/game/game_13c510.h b/src/include/game/game_13c510.h index d8ee2f6dd..f84ad1395 100644 --- a/src/include/game/game_13c510.h +++ b/src/include/game/game_13c510.h @@ -6,7 +6,7 @@ void artifactsClear(void); void artifactsTick(void); -u16 func0f13c574(f32 arg0); +u16 floatToIntDepth(f32 arg0); s32 func0f13c710(f32 arg0); void artifactsCalculateGlaresForRoom(s32 roomnum); u8 func0f13d3c4(u8 arg0, u8 arg1); From 75e34ace8c57637f3e37f06133e6098024e5a699 Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sat, 3 May 2025 16:06:10 -0500 Subject: [PATCH 25/32] added Ryan Dwyer's comment describing how artifact handling behaves --- src/game/game_13c510.c | 88 +++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index 95da0f8a8..f66851b6e 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -23,6 +23,70 @@ #include "game/prop.h" #endif +/** + * Artifacts are points of interest in the z-buffer. + * + * They correspond to: + * - Individual circles in the lens flare effect from the sun. + * - Individual corners of each light fixture's box. + * + * The game needs to do line of sight checks to these points. The collision + * system would normally be used for line of sight checks, but it only works + * with basic geometric volumes and also doesn't take into account the player's + * gun. Instead, the checks are done by reading from the previous frame's + * z-buffer. + * + * Typically, the z-buffer would be read by the scheduler after a graphics frame + * is rendered, but this only works if the z-buffer is a complete image. + * PD draws the scene, then clears the z-buffer before drawing the player's gun, + * so the z-buffer at the end only contains the player's gun. To work around + * this, the GPU is given commands to read the z-buffer as a texture and copy + * depth values to a safe place before clearing it. Then once the frame is + * rendered, the scheduler compares the saved depths with the current z-buffer + * and updates the artifact, applying the minimum of the two depths. + * + * The scheduler maintains 3 arrays of artifacts, each referenced by indexes + * which rotate between them. + * - Artifacts are written by the main thread using the write index. + * The write and front indexes are then incremented. + * - The same artifacts are updated by the scheduler using the pending index. + * The pending index is then updated. + * - The artifacts are then rendered on a later frame using the front index. + * + * In each artifacts array, the first several slots are reserved for the sun + * flare artifacts. The quantity depends on the number of suns in the stage + * (Skedar Ruins has 3) and there are 8 artifacts per sun. The remaining slots + * are used for light fixture corners. The array is big enough to handle at + * least 90 lights on screen at a time. + * + * The initial state of the indexes are write=0, front=1, pending=0. + * These are set in sched_reset_artifacts. + * + * The detailed workflow is: + * - CPU: Light artifacts are determined and added to write artifacts (0) array + * - CPU: Constructs the gdl in this order: + * - Render scene + * - Copy relevant parts of z-buffer to write depths (0) array + * - Clear z-buffer and render gun + * - Render artifacts by reading from front artifacts (1) array + * - CPU: increments write (0 -> 1) and front (1 -> 2) indexes + * - GPU: Executes above task. g_ArtifactDepths0 now contains depths, and is + * pointed to by pending. + * - Scheduler: Reads the z-buffer, which at this point only contains the gun + * depth information, and updates the artifact with the minimum of the two + * - Scheduler: Increments pending (0 -> 1) + * + * It appears that the front index should be initialised to 2 instead, so that + * it's one frame behind the others rather than two frames behind. + * + * There's no doubt this went through several iterations before landing on this + * implementation. It's likely that this was implemented using a single and full + * z-buffer and it worked well until they decided to clear the z-buffer before + * rendering the gun. There is evidence in zbuf.c that they allocated a second + * z-buffer and swapped it. But when memory got too tight they had to change it + * to this texture read method. + */ + u8 *var800a41a0; u32 var800a41a4; u32 var800a41a8; @@ -47,20 +111,18 @@ void artifactsTick(void) u16 floatToIntDepth(f32 arg0) { /** - * Method to convert a 32 bit floating point depth value - * to the unsigned 16 bit integer depth format used by - * the zbuffer available on the N64. The argument arg0 - * represents z normalized to [0, 1] * 32704.0f. + * Method to convert a 32 bit floating point depth value to the + * unsigned 16 bit integer format used by the zbuffer on the N64. + * The argument arg0 represents z normalized to [0, 1] * 32704.0f. * - * It works by scaling the depth value up to a large - * integer and applying different scaling factors - * before scaling back down to an unsigned 16 bit - * integer. It is probably intended to reduce z-fighting - * by adding more differentiation to distant z values. - * The scaling is done using bitwise operations - * to form the most & least significant bytes of the - * resulting 16 bit integer, likely because bit shift - * operations are faster than division. + * It works by converting the depth value to a large, unsigned 32 bit + * integer before scaling back down to an unsigned 16 bit integer. + * The scaling is done using bit shift operations on the most & least + * significant bytes of the resulting 16 bit integer, likely because + * bit shift operations are faster than division. The segmentation of + * this calculation at values 0x3f800, 0x3f000, etc. is probably + * intended to reduce z-fighting by adding more differentiation for + * distant z values. * * TO DO: figure why the output appears with a >> 2 shift. * It might be worth it to simplify some of the externally From 0d6c55798260d955ef0731f9ee717e24be859663 Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sat, 3 May 2025 19:57:52 -0500 Subject: [PATCH 26/32] updated variable names to match Ryan Dwyer's work clarifying the zbuffer behavior --- src/game/bondgun.c | 10 +++--- src/game/game_13c510.c | 80 ++++++++++++++++++++++++++---------------- src/game/sky.c | 10 +++--- src/game/zbuf.c | 36 +++++++++---------- src/include/types.h | 18 +++++----- src/lib/sched.c | 16 ++++----- 6 files changed, 95 insertions(+), 75 deletions(-) diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 2f929cb35..3d463d16e 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -8477,7 +8477,7 @@ glabel var7f1aca90 /* f0a71e4: 100002d5 */ b .L0f0a7d3c /* f0a71e8: 8fbf0034 */ lw $ra,0x34($sp) .L0f0a71ec: -/* f0a71ec: 0fc5d9ad */ jal zbufDrawArtifactsOffscreen +/* f0a71ec: 0fc5d9ad */ jal zbufSaveArtifactDepths /* f0a71f0: 8fa4014c */ lw $a0,0x14c($sp) /* f0a71f4: afa2014c */ sw $v0,0x14c($sp) /* f0a71f8: 0c002ca0 */ jal viPrepareZbuf @@ -9318,7 +9318,7 @@ glabel var7f1aca90 /* f0a71e4: 100002d5 */ b .L0f0a7d3c /* f0a71e8: 8fbf0034 */ lw $ra,0x34($sp) .L0f0a71ec: -/* f0a71ec: 0fc5d9ad */ jal zbufDrawArtifactsOffscreen +/* f0a71ec: 0fc5d9ad */ jal zbufSaveArtifactDepths /* f0a71f0: 8fa4014c */ lw $a0,0x14c($sp) /* f0a71f4: afa2014c */ sw $v0,0x14c($sp) /* f0a71f8: 0c002ca0 */ jal viPrepareZbuf @@ -10159,7 +10159,7 @@ glabel var7f1aca90 /* f0a4f30: 100002cd */ beqz $zero,.NB0f0a5a68 /* f0a4f34: 8fbf0034 */ lw $ra,0x34($sp) .NB0f0a4f38: -/* f0a4f38: 0fc5c4d5 */ jal zbufDrawArtifactsOffscreen +/* f0a4f38: 0fc5c4d5 */ jal zbufSaveArtifactDepths /* f0a4f3c: 8fa40144 */ lw $a0,0x144($sp) /* f0a4f40: afa20144 */ sw $v0,0x144($sp) /* f0a4f44: 0c002d00 */ jal viPrepareZbuf @@ -10956,9 +10956,7 @@ void bgunRender(Gfx **gdlptr) return; } -#ifdef PLATFORM_N64 // TODO - gdl = zbufDrawArtifactsOffscreen(gdl); -#endif + gdl = zbufSaveArtifactDepths(gdl); gdl = viPrepareZbuf(gdl); gdl = vi0000b1d0(gdl); diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index f66851b6e..ae79b10dd 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -439,14 +439,14 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) if (index < MAX_ARTIFACTS) { #ifndef PLATFORM_N64 - artifact->unk02 = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); + artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); #endif - artifact->unk04 = floatToIntDepth(f0) >> 2; - artifact->unk08 = &g_ZbufPtr1[viGetWidth() * yi + xi]; + artifact->expecteddepth = floatToIntDepth(f0) >> 2; + artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi]; artifact->light = &roomlights[i]; artifact->type = ARTIFACTTYPE_GLARE; - artifact->unk0c.u16_2 = xi; - artifact->unk0c.u16_1 = yi; + artifact->screenx = xi; + artifact->screeny = yi; } } } @@ -457,7 +457,7 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) } } -u8 func0f13d3c4(u8 arg0, u8 arg1) +u8 artifactsClamp(u8 arg0, u8 arg1) { if (arg1 >= arg0 + 7) { return arg0 + 7; @@ -505,18 +505,18 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) u16 min; u16 max; f32 lightop_cur_frac; - s32 t2; + s32 numgood; struct light *light; u8 *s3; s32 k; s32 count; - u16 t4; + u16 actualdepth; f32 add; s32 l; f32 brightness; - s32 avg; + s32 tolerance; f32 f0; - s32 v1; + s32 difference; s32 r; s32 g; s32 b; @@ -542,6 +542,12 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) struct light *light2 = artifacts[i].light; count = 0; + /** + * light arifacts are created from several, closely spaced + * textures that give the appearance of a dynamic light glare + * as the character moves. Loop to count all the sub-artifacts + * in this light. + */ for (j = i; j < MAX_ARTIFACTS && artifacts[j].type == ARTIFACTTYPE_GLARE && artifacts[j].light == light2; j++) { count++; } @@ -552,51 +558,65 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) if (roomnum == light->roomnum) { lightindex = ((uintptr_t)light - (uintptr_t)g_BgLightsFileData) / sizeof(struct light); s3 = &var800a41a0[lightindex * 3]; - t2 = 0; + numgood = 0; min = 0xffff; max = 0; + /** + * loop to determine the min & max depth of + * the sub-artifacts composing this room light. + */ for (k = i; k < i + count; k++) { - if (artifacts[k].unk04 > max) { - max = artifacts[k].unk04; + if (artifacts[k].expecteddepth > max) { + max = artifacts[k].expecteddepth; } - if (artifacts[k].unk04 < min) { - min = artifacts[k].unk04; + if (artifacts[k].expecteddepth < min) { + min = artifacts[k].expecteddepth; } } - avg = (max - min) >> 1; + /** + * Define a depth tolerance from the min & max + * depths of sub-artifacts for a given light. + * This will be used to determine which light + * artifacts are visible when comparing depth + * values of lights to other items rendered + * on screen. Without this, the lights would + * constantly flicker due to z-fighting caused + * by the low precision of the N64 depth. + */ + tolerance = (max - min) >> 1; - if (avg < 25) { - avg = 25; + if (tolerance < 25) { + tolerance = 25; } for (k = i; k < i + count; k++) { -#ifdef PLATFORM_N64 - u16 tmp; - t4 = (artifacts[k].unk02 & 0xfffc) >> 2; - tmp = artifacts[k].unk04; + u16 expecteddepth; + actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2; + expecteddepth = artifacts[k].expecteddepth; - if (tmp < t4) { - v1 = t4 - tmp; + if (expecteddepth < actualdepth) { + difference = actualdepth - expecteddepth; } else { - v1 = tmp - t4; + difference = expecteddepth - actualdepth; } - if (avg >= v1) { - t2++; +#ifdef PLATFORM_N64 + if (difference <= tolerance) { + numgood++; } #else - t2 += artifacts[k].unk02; + numgood += artifacts[k].visiblelos; #endif artifacts[k].type = ARTIFACTTYPE_FREE; } - s3[0] = func0f13d3c4(s3[0], t2 * 2); + s3[0] = artifactsClamp(s3[0], numgood * 2); - if (t2 > 0) { + if (numgood > 0) { brightness = viGetFovY() * 0.017453292f; add = cosf(brightness) / sinf(brightness) * 14.6f; diff --git a/src/game/sky.c b/src/game/sky.c index e610cad83..d5252919c 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -2518,11 +2518,11 @@ void skyCreateSunArtifact(struct artifact *artifact, s32 x, s32 y) sunpos.x = env->suns[i].pos[0]; sunpos.y = env->suns[i].pos[1]; sunpos.z = env->suns[i].pos[2]; - artifact->unk02 = artifactTestLos(&sunpos, &zero, x, y) * 0xfffc; + artifact->visiblelos = artifactTestLos(&sunpos, &zero, x, y) * 0xfffc; #endif - artifact->unk08 = &g_ZbufPtr1[(s32)camGetScreenWidth() * y + x]; - artifact->unk0c.u16_2 = x; - artifact->unk0c.u16_1 = y; + artifact->zbufptr = &g_ZbufPtr1[(s32)camGetScreenWidth() * y + x]; + artifact->screenx = x; + artifact->screeny = y; artifact->type = ARTIFACTTYPE_CIRCLE; } } @@ -2533,7 +2533,7 @@ f32 skyGetArtifactGroupIntensityFrac(struct artifact *artifacts) s32 i; for (i = 0; i < 8; i++) { - if (artifacts[i].type == ARTIFACTTYPE_CIRCLE && artifacts[i].unk02 == 0xfffc) { + if (artifacts[i].type == ARTIFACTTYPE_CIRCLE && artifacts[i].actualdepth == 0xfffc) { sum += 0.125f; } } diff --git a/src/game/zbuf.c b/src/game/zbuf.c index cf1b62140..0d1d2bb53 100644 --- a/src/game/zbuf.c +++ b/src/game/zbuf.c @@ -177,23 +177,23 @@ u16 *zbufGetArtifactsCfb(s32 index) return addr; } -Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) +Gfx *zbufSaveArtifactDepths(Gfx *gdl) { struct artifact *artifacts = schedGetWriteArtifacts(); u32 stack; - u16 *sp4c = g_ZbufPtr1; - u32 s4 = 0; - u16 *sp44; - u16 *s2; - u16 *image; + u16 *zbuf = g_ZbufPtr1; + u32 numsamples = 0; + u16 *samples; + u16 *thissample; + u16 *zbufrow; s32 i; viGetBackBuffer(); - sp44 = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); + samples = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); g_SchedSpecialArtifactIndexes[g_SchedWriteArtifactsIndex] = 1; gDPPipeSync(gdl++); - gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, viGetBufWidth(), OS_PHYSICAL_TO_K0(sp44)); + gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, viGetBufWidth(), OS_PHYSICAL_TO_K0(samples)); gDPSetScissor(gdl++, G_SC_NON_INTERLACE, 0, 0, SCREEN_320, SCREEN_240); gDPSetCycleType(gdl++, G_CYC_COPY); gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0x0000, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); @@ -220,24 +220,24 @@ Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) if (1); if (artifacts[i].type != ARTIFACTTYPE_FREE) { - s2 = &sp44[s4]; - image = &sp4c[artifacts[i].unk0c.u16_1 * viGetWidth()]; + thissample = &samples[numsamples]; + zbufrow = &zbuf[artifacts[i].screeny * viGetWidth()]; gDPPipeSync(gdl++); - gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_320, image); + gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_320, zbufrow); gDPLoadSync(gdl++); gDPLoadBlock(gdl++, 5, 0, 0, viGetWidth() - 1, 0); gDPPipeSync(gdl++); gSPTextureRectangle(gdl++, - s4 << 2, 0, - (s4 + 3) << 2, 0, - G_TX_RENDERTILE, (artifacts[i].unk0c.u16_2 * 32) + 16, 0x0010, 0x1000, 0); + numsamples << 2, 0, + (numsamples + 3) << 2, 0, + G_TX_RENDERTILE, (artifacts[i].screenx * 32) + 16, 0x0010, 0x1000, 0); - artifacts[i].unk0c.u16p = s2; - s4++; + artifacts[i].depthptr = thissample; + numsamples++; - if (s2); + if (thissample); } } @@ -251,7 +251,7 @@ Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) gDPSetTexturePersp(gdl++, G_TP_PERSP); gDPSetColorDither(gdl++, G_CD_BAYER); - if (sp44); + if (samples); return gdl; } diff --git a/src/include/types.h b/src/include/types.h index 1da61867b..702c6d6d0 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -3494,17 +3494,19 @@ struct audioconfig { struct artifact { u16 type; - u16 unk02; - u16 unk04; - u16 unk06; - u16 *unk08; + u16 actualdepth; + u16 expecteddepth; + u16 *zbufptr; +#ifndef PLATFORM_N64 + u16 visiblelos; +#endif union { - u16 *u16p; + u16 *depthptr; struct { - u16 u16_1; - u16 u16_2; + u16 screeny; + u16 screenx; }; - } unk0c; + }; struct light *light; }; diff --git a/src/lib/sched.c b/src/lib/sched.c index 266e4cb68..b70906336 100644 --- a/src/lib/sched.c +++ b/src/lib/sched.c @@ -540,20 +540,20 @@ void schedUpdatePendingArtifacts(void) struct artifact *artifact = &artifacts[i]; if (artifact->type != ARTIFACTTYPE_FREE) { - u16 *unk08 = artifact->unk08; - u16 value08 = unk08[0]; + u16 *currdepthptr = artifact->zbufptr; + u16 currdepth = *currdepthptr; if (g_SchedSpecialArtifactIndexes[g_SchedPendingArtifactsIndex] == 1) { - u16 *unk0c = artifact->unk0c.u16p; - u16 value0c = unk0c[0]; + u16 *prevdepthptr = artifact->depthptr; + u16 prevdepth = *prevdepthptr; - if (value0c > value08) { - artifact->unk02 = value08; + if (currdepth < prevdepth) { + artifact->actualdepth = currdepth; } else { - artifact->unk02 = value0c; + artifact->actualdepth = prevdepth; } } else { - artifact->unk02 = value08; + artifact->actualdepth = currdepth; } } } From 400865ec256db0f0f5fddd654aaa409042fc3b97 Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sat, 3 May 2025 20:17:29 -0500 Subject: [PATCH 27/32] added debugging to pin point seg fault --- src/game/bondgun.c | 5 +++++ src/game/game_13c510.c | 16 ++++++++++++++++ src/game/zbuf.c | 8 ++++++-- src/lib/sched.c | 5 +++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 3d463d16e..87edc9b9b 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -57,6 +57,7 @@ #include "game/stagetable.h" #include "video.h" #include "platform.h" +#include // remove after debugging #endif #define GUNLOADSTATE_FLUX 0 @@ -10938,6 +10939,8 @@ glabel var7f1aca90 // Mismatch: Goal uses different codegen for accessing vertices void bgunRender(Gfx **gdlptr) { + printf("\nsrc/game/bondgun.c:bgunRender()"); + fflush(stdout); Gfx *gdl = *gdlptr; struct modelrenderdata renderdata = {NULL, true, 3}; // 10c struct player *player; @@ -11190,6 +11193,8 @@ void bgunRender(Gfx **gdlptr) viGetViewLeft() + viGetViewWidth(), viGetViewTop() + viGetViewHeight()); *gdlptr = gdl; + printf("\n done!"); + fflush(stdout); } #endif diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index ae79b10dd..fa215a31c 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -21,6 +21,7 @@ #include "lib/lib_17ce0.h" #include "game/player.h" #include "game/prop.h" +#include // remove after debugging #endif /** @@ -214,6 +215,9 @@ bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi) void artifactsCalculateGlaresForRoom(s32 roomnum) { + printf("\nsrc/game/game_13c510.c:artifactsCalculateGlaresForRoom(%d)", roomnum); + fflush(stdout); + s32 i; s32 j; s32 k; @@ -455,6 +459,8 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) } } } + printf("\n done!"); + fflush(stdout); } u8 artifactsClamp(u8 arg0, u8 arg1) @@ -472,6 +478,9 @@ u8 artifactsClamp(u8 arg0, u8 arg1) Gfx *artifactsConfigureForGlares(Gfx *gdl) { + printf("\nsrc/game/game_13c510.c:artifactsConfigureForGlares()"); + fflush(stdout); + struct stagetableentry *stage = stageGetCurrent(); texSelect(&gdl, &g_TexLightGlareConfigs[stage->light_type], 4, 0, 2, 1, NULL); @@ -486,6 +495,8 @@ Gfx *artifactsConfigureForGlares(Gfx *gdl) gDPSetAlphaDither(gdl++, G_AD_PATTERN); gDPSetTexturePersp(gdl++, G_TP_NONE); + printf("\n done!"); + fflush(stdout); return gdl; } @@ -498,6 +509,9 @@ Gfx *artifactsUnconfigureForGlares(Gfx *gdl) Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) { + printf("\nsrc/game/game_13c510.c:artifactsRenderGlaresForRoom(%d)", roomnum); + fflush(stdout); + s32 i; s32 j; s32 lightindex; @@ -740,5 +754,7 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) } } + printf("\n done!"); + fflush(stdout); return gdl; } diff --git a/src/game/zbuf.c b/src/game/zbuf.c index 0d1d2bb53..3668616f3 100644 --- a/src/game/zbuf.c +++ b/src/game/zbuf.c @@ -10,6 +10,7 @@ #include "lib/memp.h" #include "data.h" #include "types.h" +#include // remove after debugging u32 g_ZbufWidth; u32 g_ZbufHeight; @@ -125,7 +126,6 @@ Gfx *zbufConfigureRdp(Gfx *gdl) */ Gfx *zbufClear(Gfx *gdl) { -#ifdef PLATFORM_N64 s32 left; s32 right; @@ -149,7 +149,7 @@ Gfx *zbufClear(Gfx *gdl) gDPFillRectangle(gdl++, left, 0, right, playerGetFbHeight() - 1); gDPPipeSync(gdl++); -#else +#ifndef PLATFORM_N64 gDPClearDepthEXT(gdl++); #endif @@ -188,6 +188,8 @@ Gfx *zbufSaveArtifactDepths(Gfx *gdl) u16 *zbufrow; s32 i; + printf("\nsrc/game/zbuf.c:zbufSaveArtifactDepths()"); + fflush(stdout); viGetBackBuffer(); samples = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); g_SchedSpecialArtifactIndexes[g_SchedWriteArtifactsIndex] = 1; @@ -253,5 +255,7 @@ Gfx *zbufSaveArtifactDepths(Gfx *gdl) if (samples); + printf("\n done!"); + fflush(stdout); return gdl; } diff --git a/src/lib/sched.c b/src/lib/sched.c index b70906336..a954357ad 100644 --- a/src/lib/sched.c +++ b/src/lib/sched.c @@ -19,6 +19,7 @@ #include "lib/joy.h" #include "data.h" #include "types.h" +#include // remove after debugging /* * private typedefs and defines @@ -533,6 +534,8 @@ void schedResetArtifacts(void) void schedUpdatePendingArtifacts(void) { + printf("\nsrc/lib/sched.c:schedUpdatePendingArtifacts()"); + fflush(stdout); struct artifact *artifacts = schedGetPendingArtifacts(); s32 i; @@ -561,6 +564,8 @@ void schedUpdatePendingArtifacts(void) g_SchedSpecialArtifactIndexes[g_SchedPendingArtifactsIndex] = 0; schedIncrementPendingArtifacts(); + printf("\n done!"); + fflush(stdout); } /** From 062de5bb7badc98914635236cb80ac37ea83166c Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sun, 11 May 2025 11:59:11 -0500 Subject: [PATCH 28/32] clear up debugging statements --- src/game/bondgun.c | 7 ++----- src/game/game_13c510.c | 30 ++++++++---------------------- src/game/zbuf.c | 16 ++++++++++------ src/lib/sched.c | 5 ----- 4 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 87edc9b9b..a46e66721 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -57,7 +57,6 @@ #include "game/stagetable.h" #include "video.h" #include "platform.h" -#include // remove after debugging #endif #define GUNLOADSTATE_FLUX 0 @@ -10939,8 +10938,6 @@ glabel var7f1aca90 // Mismatch: Goal uses different codegen for accessing vertices void bgunRender(Gfx **gdlptr) { - printf("\nsrc/game/bondgun.c:bgunRender()"); - fflush(stdout); Gfx *gdl = *gdlptr; struct modelrenderdata renderdata = {NULL, true, 3}; // 10c struct player *player; @@ -10959,7 +10956,9 @@ void bgunRender(Gfx **gdlptr) return; } +#ifdef PLATFORM_N64 gdl = zbufSaveArtifactDepths(gdl); +#endif gdl = viPrepareZbuf(gdl); gdl = vi0000b1d0(gdl); @@ -11193,8 +11192,6 @@ void bgunRender(Gfx **gdlptr) viGetViewLeft() + viGetViewWidth(), viGetViewTop() + viGetViewHeight()); *gdlptr = gdl; - printf("\n done!"); - fflush(stdout); } #endif diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index fa215a31c..4dde60b57 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -21,7 +21,6 @@ #include "lib/lib_17ce0.h" #include "game/player.h" #include "game/prop.h" -#include // remove after debugging #endif /** @@ -109,7 +108,7 @@ void artifactsTick(void) schedIncrementFrontArtifacts(); } -u16 floatToIntDepth(f32 arg0) +u16 floatToN64Depth(f32 arg0) { /** * Method to convert a 32 bit floating point depth value to the @@ -124,10 +123,6 @@ u16 floatToIntDepth(f32 arg0) * this calculation at values 0x3f800, 0x3f000, etc. is probably * intended to reduce z-fighting by adding more differentiation for * distant z values. - * - * TO DO: figure why the output appears with a >> 2 shift. - * It might be worth it to simplify some of the externally - * applied scaling factors if it can match the N64 compilation. */ u32 value = arg0 * 8.0f; u32 left; // forms the most significant byte of the final u16 @@ -215,9 +210,6 @@ bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi) void artifactsCalculateGlaresForRoom(s32 roomnum) { - printf("\nsrc/game/game_13c510.c:artifactsCalculateGlaresForRoom(%d)", roomnum); - fflush(stdout); - s32 i; s32 j; s32 k; @@ -445,7 +437,13 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) #ifndef PLATFORM_N64 artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); #endif - artifact->expecteddepth = floatToIntDepth(f0) >> 2; + /** + * the game performs artifact depth comparison using + * the original N64 depth values divided by 4, which + * is accomplished by bit shifting the N64 depth value + * to the right using >> 2 + */ + artifact->expecteddepth = floatToN64Depth(f0) >> 2; artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi]; artifact->light = &roomlights[i]; artifact->type = ARTIFACTTYPE_GLARE; @@ -459,8 +457,6 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) } } } - printf("\n done!"); - fflush(stdout); } u8 artifactsClamp(u8 arg0, u8 arg1) @@ -478,9 +474,6 @@ u8 artifactsClamp(u8 arg0, u8 arg1) Gfx *artifactsConfigureForGlares(Gfx *gdl) { - printf("\nsrc/game/game_13c510.c:artifactsConfigureForGlares()"); - fflush(stdout); - struct stagetableentry *stage = stageGetCurrent(); texSelect(&gdl, &g_TexLightGlareConfigs[stage->light_type], 4, 0, 2, 1, NULL); @@ -495,8 +488,6 @@ Gfx *artifactsConfigureForGlares(Gfx *gdl) gDPSetAlphaDither(gdl++, G_AD_PATTERN); gDPSetTexturePersp(gdl++, G_TP_NONE); - printf("\n done!"); - fflush(stdout); return gdl; } @@ -509,9 +500,6 @@ Gfx *artifactsUnconfigureForGlares(Gfx *gdl) Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) { - printf("\nsrc/game/game_13c510.c:artifactsRenderGlaresForRoom(%d)", roomnum); - fflush(stdout); - s32 i; s32 j; s32 lightindex; @@ -754,7 +742,5 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) } } - printf("\n done!"); - fflush(stdout); return gdl; } diff --git a/src/game/zbuf.c b/src/game/zbuf.c index 3668616f3..d64f79f28 100644 --- a/src/game/zbuf.c +++ b/src/game/zbuf.c @@ -10,7 +10,6 @@ #include "lib/memp.h" #include "data.h" #include "types.h" -#include // remove after debugging u32 g_ZbufWidth; u32 g_ZbufHeight; @@ -126,6 +125,7 @@ Gfx *zbufConfigureRdp(Gfx *gdl) */ Gfx *zbufClear(Gfx *gdl) { +#ifdef PLATFORM_N64 s32 left; s32 right; @@ -149,7 +149,7 @@ Gfx *zbufClear(Gfx *gdl) gDPFillRectangle(gdl++, left, 0, right, playerGetFbHeight() - 1); gDPPipeSync(gdl++); -#ifndef PLATFORM_N64 +#else gDPClearDepthEXT(gdl++); #endif @@ -177,6 +177,14 @@ u16 *zbufGetArtifactsCfb(s32 index) return addr; } +/** + * This method is designed to save artifact depths + * prior to drawing the hands and weapon on-screen + * since drawing them requires clearing the zbuffer + * to avoid clipping the gun model on floors and walls. + * + * TODO: determine if this works with the PC port + */ Gfx *zbufSaveArtifactDepths(Gfx *gdl) { struct artifact *artifacts = schedGetWriteArtifacts(); @@ -188,8 +196,6 @@ Gfx *zbufSaveArtifactDepths(Gfx *gdl) u16 *zbufrow; s32 i; - printf("\nsrc/game/zbuf.c:zbufSaveArtifactDepths()"); - fflush(stdout); viGetBackBuffer(); samples = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); g_SchedSpecialArtifactIndexes[g_SchedWriteArtifactsIndex] = 1; @@ -255,7 +261,5 @@ Gfx *zbufSaveArtifactDepths(Gfx *gdl) if (samples); - printf("\n done!"); - fflush(stdout); return gdl; } diff --git a/src/lib/sched.c b/src/lib/sched.c index a954357ad..b70906336 100644 --- a/src/lib/sched.c +++ b/src/lib/sched.c @@ -19,7 +19,6 @@ #include "lib/joy.h" #include "data.h" #include "types.h" -#include // remove after debugging /* * private typedefs and defines @@ -534,8 +533,6 @@ void schedResetArtifacts(void) void schedUpdatePendingArtifacts(void) { - printf("\nsrc/lib/sched.c:schedUpdatePendingArtifacts()"); - fflush(stdout); struct artifact *artifacts = schedGetPendingArtifacts(); s32 i; @@ -564,8 +561,6 @@ void schedUpdatePendingArtifacts(void) g_SchedSpecialArtifactIndexes[g_SchedPendingArtifactsIndex] = 0; schedIncrementPendingArtifacts(); - printf("\n done!"); - fflush(stdout); } /** From f12ae756ef7436090cfdfbf24956d3654310f7ee Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sun, 11 May 2025 12:17:38 -0500 Subject: [PATCH 29/32] rename game_13c510 files to artifact --- src/game/artifact.c | 746 ++++++++++++++++++++++++++++++++++++ src/game/bg.c | 2 +- src/game/bondgun.c | 2 +- src/game/lv.c | 2 +- src/game/sky.c | 2 +- src/include/game/artifact.h | 20 + 6 files changed, 770 insertions(+), 4 deletions(-) create mode 100644 src/game/artifact.c create mode 100644 src/include/game/artifact.h diff --git a/src/game/artifact.c b/src/game/artifact.c new file mode 100644 index 000000000..03dae835d --- /dev/null +++ b/src/game/artifact.c @@ -0,0 +1,746 @@ +#include +#include "lib/sched.h" +#include "constants.h" +#include "game/camera.h" +#include "game/dlights.h" +#include "game/env.h" +#include "game/game_0b2150.h" +#include "game/tex.h" +#include "game/sky.h" +#include "game/artifact.h" +#include "game/bg.h" +#include "game/stagetable.h" +#include "game/room.h" +#include "bss.h" +#include "lib/vi.h" +#include "lib/mtx.h" +#include "data.h" +#include "types.h" +#ifndef PLATFORM_N64 +#include "lib/collision.h" +#include "lib/lib_17ce0.h" +#include "game/player.h" +#include "game/prop.h" +#endif + +/** + * Artifacts are points of interest in the z-buffer. + * + * They correspond to: + * - Individual circles in the lens flare effect from the sun. + * - Individual corners of each light fixture's box. + * + * The game needs to do line of sight checks to these points. The collision + * system would normally be used for line of sight checks, but it only works + * with basic geometric volumes and also doesn't take into account the player's + * gun. Instead, the checks are done by reading from the previous frame's + * z-buffer. + * + * Typically, the z-buffer would be read by the scheduler after a graphics frame + * is rendered, but this only works if the z-buffer is a complete image. + * PD draws the scene, then clears the z-buffer before drawing the player's gun, + * so the z-buffer at the end only contains the player's gun. To work around + * this, the GPU is given commands to read the z-buffer as a texture and copy + * depth values to a safe place before clearing it. Then once the frame is + * rendered, the scheduler compares the saved depths with the current z-buffer + * and updates the artifact, applying the minimum of the two depths. + * + * The scheduler maintains 3 arrays of artifacts, each referenced by indexes + * which rotate between them. + * - Artifacts are written by the main thread using the write index. + * The write and front indexes are then incremented. + * - The same artifacts are updated by the scheduler using the pending index. + * The pending index is then updated. + * - The artifacts are then rendered on a later frame using the front index. + * + * In each artifacts array, the first several slots are reserved for the sun + * flare artifacts. The quantity depends on the number of suns in the stage + * (Skedar Ruins has 3) and there are 8 artifacts per sun. The remaining slots + * are used for light fixture corners. The array is big enough to handle at + * least 90 lights on screen at a time. + * + * The initial state of the indexes are write=0, front=1, pending=0. + * These are set in sched_reset_artifacts. + * + * The detailed workflow is: + * - CPU: Light artifacts are determined and added to write artifacts (0) array + * - CPU: Constructs the gdl in this order: + * - Render scene + * - Copy relevant parts of z-buffer to write depths (0) array + * - Clear z-buffer and render gun + * - Render artifacts by reading from front artifacts (1) array + * - CPU: increments write (0 -> 1) and front (1 -> 2) indexes + * - GPU: Executes above task. g_ArtifactDepths0 now contains depths, and is + * pointed to by pending. + * - Scheduler: Reads the z-buffer, which at this point only contains the gun + * depth information, and updates the artifact with the minimum of the two + * - Scheduler: Increments pending (0 -> 1) + * + * It appears that the front index should be initialised to 2 instead, so that + * it's one frame behind the others rather than two frames behind. + * + * There's no doubt this went through several iterations before landing on this + * implementation. It's likely that this was implemented using a single and full + * z-buffer and it worked well until they decided to clear the z-buffer before + * rendering the gun. There is evidence in zbuf.c that they allocated a second + * z-buffer and swapped it. But when memory got too tight they had to change it + * to this texture read method. + */ + +u8 *var800a41a0; +u32 var800a41a4; +u32 var800a41a8; +u32 var800a41ac; + +void artifactsClear(void) +{ + struct artifact *artifacts = schedGetWriteArtifacts(); + s32 i; + + for (i = 0; i < MAX_ARTIFACTS; i++) { + artifacts[i].type = ARTIFACTTYPE_FREE; + } +} + +void artifactsTick(void) +{ + schedIncrementWriteArtifacts(); + schedIncrementFrontArtifacts(); +} + +u16 floatToN64Depth(f32 arg0) +{ + /** + * Method to convert a 32 bit floating point depth value to the + * unsigned 16 bit integer format used by the zbuffer on the N64. + * The argument arg0 represents z normalized to [0, 1] * 32704.0f. + * + * It works by converting the depth value to a large, unsigned 32 bit + * integer before scaling back down to an unsigned 16 bit integer. + * The scaling is done using bit shift operations on the most & least + * significant bytes of the resulting 16 bit integer, likely because + * bit shift operations are faster than division. The segmentation of + * this calculation at values 0x3f800, 0x3f000, etc. is probably + * intended to reduce z-fighting by adding more differentiation for + * distant z values. + */ + u32 value = arg0 * 8.0f; + u32 left; // forms the most significant byte of the final u16 + u32 right = value; // forms the least significant byte of the final u16 + + if (value > 0x3f800) { + right = value & 0x7ff; + right &= 0x7ff; + left = 7; + } else if (value > 0x3f000) { + right = value & 0x7ff; + right &= 0x7ff; + left = 6; + } else if (value > 0x3e000) { + right = (value >> 1) & 0x7ff; + right &= 0x7ff; + left = 5; + } else if (value > 0x3c000) { + right = (value >> 2) & 0x7ff; + right &= 0x7ff; + left = 4; + } else if (value > 0x38000) { + right = (value >> 3) & 0x7ff; + right &= 0x7ff; + left = 3; + } else if (value > 0x30000) { + right = (value >> 4) & 0x7ff; + right &= 0x7ff; + left = 2; + } else if (value > 0x20000) { + right = (value >> 5) & 0x7ff; + right &= 0x7ff; + left = 1; + } else { + right = (value >> 6) & 0x7ff; + right &= 0x7ff; + left = 0; + } + + return left << 13 | (right << 2); +} + +s32 artifactsFloatToInt(f32 arg0) +{ + if (arg0 > 0.0f) { + if (arg0 > 2147483520.0f) { + arg0 = 2147483520; + } + } else { + if (arg0 < -2147483520) { + arg0 = -2147483520; + } + } + + return arg0; +} + +#ifndef PLATFORM_N64 + +bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi) +{ + s32 i = 0; + + if (!g_Vars.currentplayer) { + return false; + } + + struct coord endpos; + endpos.x = roompos->x + spec->x; + endpos.y = roompos->y + spec->y; + endpos.z = roompos->z + spec->z; + + struct coord gundir2d; + struct coord gunpos2d = {{ 0.f, 0.f, 0.f }}; + struct coord gundir3d; + struct coord gunpos3d = g_Vars.currentplayer->cam_pos; + f32 crosspos[2] = { (f32)xi, (f32)yi }; + cam0f0b4c3c(crosspos, &gundir2d, 1.f); + mtx4RotateVec(camGetProjectionMtxF(), &gundir2d, &gundir3d); + + return shotTestLos(&gunpos2d, &gundir2d, &gunpos3d, &gundir3d, &endpos); +} + +#endif + +void artifactsCalculateGlaresForRoom(s32 roomnum) +{ + s32 i; + s32 j; + s32 k; + s32 l; + f32 f0; + s32 numlights; + f32 viewwidth; + f32 viewheight; + f32 viewleft; + f32 viewtop; + u8 *s1; + f32 x; + f32 y; + f32 f16; + f32 f20; + s32 xi; + s32 yi; + f32 sp190; + f32 brightnessfrac; + f32 thisfrac; + f32 tmp; + f32 tmp2; + f32 tmp3; + f32 sp178; + Mtxf sp138; + Mtxf spf8; + struct coord spec; + f32 spdc[4]; + struct coord origin; + struct coord spc4; + struct light *roomlights; + s32 index; + struct artifact *artifacts = schedGetWriteArtifacts(); + struct coord *campos = &g_Vars.currentplayer->cam_pos; + struct artifact *artifact; + + if (g_Rooms[roomnum].gfxdata != NULL && g_Rooms[roomnum].loaded240) { + numlights = g_Rooms[roomnum].gfxdata->numlights; + + if (numlights != 0) { + roomlights = (struct light *)&g_BgLightsFileData[g_Rooms[roomnum].gfxdata->lightsindex * 0x22]; + s1 = &var800a41a0[g_Rooms[roomnum].gfxdata->lightsindex * 3]; + + roomPopulateMtx(&sp138, roomnum); + mtx00015f88(bgGetScaleBg2Gfx(), &sp138); + mtx4MultMtx4(camGetMtxF006c(), &sp138, &spf8); + + viewwidth = viGetViewWidth(); + viewheight = viGetViewHeight(); + viewleft = viGetViewLeft(); + viewtop = viGetViewTop(); + + for (i = 0; i < numlights; i++) { + origin.x = 0.0f; + origin.y = 0.0f; + origin.z = 0.0f; + + for (j = 0; j < ARRAYCOUNT(roomlights[i].bbox); j++) { + origin.x += roomlights[i].bbox[j].x; + origin.y += roomlights[i].bbox[j].y; + origin.z += roomlights[i].bbox[j].z; + } + + origin.x /= 4.0f; + origin.y /= 4.0f; + origin.z /= 4.0f; + + for (j = 0; j != 3; j++) { + spc4.f[j] = origin.f[j] - (campos->f[j] - g_BgRooms[roomnum].pos.f[j]); + } + + s1[i * 3 + 1] = 0; + s1[i * 3 + 2] = 0; + + tmp = roomlights[i].dirx * roomlights[i].dirx + roomlights[i].diry * roomlights[i].diry + roomlights[i].dirz * roomlights[i].dirz; + f16 = spc4.f[0] * spc4.f[0] + spc4.f[1] * spc4.f[1] + spc4.f[2] * spc4.f[2]; + + if (tmp > 0.0001f && f16 > 0.0001f) { + sp190 = -((roomlights[i].dirx * spc4.f[0] + roomlights[i].diry * spc4.f[1] + roomlights[i].dirz * spc4.f[2]) / sqrtf(tmp * f16)); + + if (sp190 > 0.4f) { + sp190 = 0.4f; + } + + sp190 *= 2.5f; + } else { + sp190 = 0.0f; + } + + if (sp190 > 0.0f) { + for (l = 3; l >= 0; l--) { + spdc[l] = origin.f[0] * spf8.m[0][l] + origin.f[1] * spf8.m[1][l] + origin.f[2] * spf8.m[2][l] + spf8.m[3][l]; + + if (l == 3 && spdc[l] <= 0.0f) { + break; + } + } + + if (spdc[3] > 0.0001f) { + f20 = 1.0f / spdc[3]; + x = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); + y = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); + f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; + + if (f0 < 32576.0f) { + brightnessfrac = 1.0f; + tmp2 = (brightnessfrac - 1.00f); + + if (x <= 10.0f + viewleft) { + brightnessfrac = 0.0f; + } else if (y <= 30.0f + viewtop) { + brightnessfrac = 0.0f; + } else if (x >= -10.0f + viewleft + viewwidth) { + brightnessfrac = 0.0f; + } else if (y >= -30.0f + viewtop + viewheight) { + brightnessfrac = 0.0f; + } + + sp178 = 1.0f - 2.0f * tmp2; + + if (brightnessfrac != 0.0f) { + brightnessfrac = 1.0f; + + if (x < viewleft + 90.0f) { + thisfrac = (x - (10.0f + viewleft)) / 80.0f; + + if (thisfrac < brightnessfrac) { + brightnessfrac = thisfrac; + } + } + + if (y < viewtop + 100.0f) { + thisfrac = (y - (viewtop + 30.0f)) / 70.0f; + + if (thisfrac < brightnessfrac) { + brightnessfrac = thisfrac; + } + } + + if (x > viewleft + viewwidth - 90.0f) { + thisfrac = (viewleft + viewwidth - 10.0f - x) / 80.0f; + + if (thisfrac < brightnessfrac) { + brightnessfrac = thisfrac; + } + } + + if (y > viewtop + viewheight - 100.0f) { + thisfrac = (viewtop + viewheight - 30.0f - y) / 70.0f; + + if (thisfrac < brightnessfrac) { + brightnessfrac = thisfrac; + } + } + } + + tmp3 = 32300.0f - f0; + + if (tmp3 < 0.0f) { + tmp3 = 0.0f; + } + + if (tmp3 > 1300.0f) { + tmp3 = 1300.0f; + } + + tmp3 *= 1.0f / 1300.0f; + + if (2.0f * tmp2 > 1.0f) { + sp178 = 0.0f; + } + + s1[i * 3 + 1] = sp190 * 255.0f * sp178; + s1[i * 3 + 2] = brightnessfrac * tmp3 * sp190 * 64.0f * 1; + } + } + } + + if (s1[i * 3 + 1] > 0) { + for (j = 0; j < ARRAYCOUNT(roomlights[i].bbox); j++) { + spec.x = origin.x + (roomlights[i].bbox[j].x - origin.x) * 0.6f; + spec.y = origin.y + (roomlights[i].bbox[j].y - origin.y) * 0.6f; + spec.z = origin.z + (roomlights[i].bbox[j].z - origin.z) * 0.6f; + + for (k = 3; k >= 0; k--) { + spdc[k] = spec.f[0] * spf8.m[0][k] + spec.f[1] * spf8.m[1][k] + spec.f[2] * spf8.m[2][k] + spf8.m[3][k]; + + if (k == 3 && spdc[k] <= 0.0f) { + break; + } + } + + if (spdc[3] > 0.0f) { + f20 = 1.0f / spdc[3]; + + if (f20 > 9999.0f) { + f20 = 9999.0f; + } + + if (f20 < -9999.0f) { + f20 = -9999.0f; + } + + xi = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); + yi = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); + f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; + + if (g_ZbufPtr1 + && xi >= (s32)viewleft + && xi < (s32)(viewleft + viewwidth) + && yi >= (s32)viewtop + && yi < (s32)(viewtop + viewheight) + && f0 < 32576.0f) { + index = envGetCurrent()->numsuns; + index *= 8; + artifact = artifacts; + artifact += index; + + while (artifact->type != ARTIFACTTYPE_FREE) { + index++; + artifact++; + } + + if (index < MAX_ARTIFACTS) { +#ifndef PLATFORM_N64 + artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); +#endif + /** + * the original game performs artifact depth comparison + * using the N64 depth values divided by 4. This is + * accomplished by bit shifting the N64 depth value + * to the right using >> 2 + */ + artifact->expecteddepth = floatToN64Depth(f0) >> 2; + artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi]; + artifact->light = &roomlights[i]; + artifact->type = ARTIFACTTYPE_GLARE; + artifact->screenx = xi; + artifact->screeny = yi; + } + } + } + } + } + } + } + } +} + +u8 artifactsClamp(u8 arg0, u8 arg1) +{ + if (arg1 >= arg0 + 7) { + return arg0 + 7; + } + + if (arg1 <= arg0 - 7) { + return arg0 - 7; + } + + return arg1; +} + +Gfx *artifactsConfigureForGlares(Gfx *gdl) +{ + struct stagetableentry *stage = stageGetCurrent(); + + texSelect(&gdl, &g_TexLightGlareConfigs[stage->light_type], 4, 0, 2, 1, NULL); + + gDPSetCycleType(gdl++, G_CYC_1CYCLE); + gDPSetRenderMode(gdl++, G_RM_CLD_SURF, G_RM_CLD_SURF2); + gDPSetTextureFilter(gdl++, G_TF_BILERP); + gDPSetCombineLERP(gdl++, + 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, + 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0); + gDPSetColorDither(gdl++, G_CD_BAYER); + gDPSetAlphaDither(gdl++, G_AD_PATTERN); + gDPSetTexturePersp(gdl++, G_TP_NONE); + + return gdl; +} + +Gfx *artifactsUnconfigureForGlares(Gfx *gdl) +{ + gDPSetTexturePersp(gdl++, G_TP_PERSP); + + return gdl; +} + +Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) +{ + s32 i; + s32 j; + s32 lightindex; + struct artifact *artifacts; + u16 min; + u16 max; + f32 lightop_cur_frac; + s32 numgood; + struct light *light; + u8 *s3; + s32 k; + s32 count; + u16 actualdepth; + f32 add; + s32 l; + f32 brightness; + s32 tolerance; + f32 f0; + s32 difference; + s32 r; + s32 g; + s32 b; + s32 stack; + u8 colour[4]; + s16 lightroompos[3]; + struct coord lightworldpos; + struct coord lightscreenpos; + f32 spdc[2]; + f32 spd4[2]; + f32 f24; + bool extra; + f32 f26; + + artifacts = schedGetFrontArtifacts(); + lightop_cur_frac = roomGetLightOpCurFrac(roomnum); + + if (g_Rooms[roomnum].gfxdata == NULL || g_Rooms[roomnum].loaded240 == 0) { + return gdl; + } + + for (i = envGetCurrent()->numsuns * 8; i < MAX_ARTIFACTS; i++) { + struct light *light2 = artifacts[i].light; + count = 0; + + /** + * light arifacts are created from several, closely spaced + * textures that give the appearance of a dynamic light glare + * as the character moves. Loop to count all the sub-artifacts + * in this light. + */ + for (j = i; j < MAX_ARTIFACTS && artifacts[j].type == ARTIFACTTYPE_GLARE && artifacts[j].light == light2; j++) { + count++; + } + + light = artifacts[i].light; + + if (count > 0) { + if (roomnum == light->roomnum) { + lightindex = ((uintptr_t)light - (uintptr_t)g_BgLightsFileData) / sizeof(struct light); + s3 = &var800a41a0[lightindex * 3]; + numgood = 0; + min = 0xffff; + max = 0; + + /** + * loop to determine the min & max depth of + * the sub-artifacts composing this room light. + */ + for (k = i; k < i + count; k++) { + if (artifacts[k].expecteddepth > max) { + max = artifacts[k].expecteddepth; + } + + if (artifacts[k].expecteddepth < min) { + min = artifacts[k].expecteddepth; + } + } + + /** + * Define a depth tolerance from the min & max + * depths of sub-artifacts for a given light. + * This will be used to determine which light + * artifacts are visible when comparing depth + * values of lights to other items rendered + * on screen. Without this, the lights would + * constantly flicker due to z-fighting caused + * by the low precision of the N64 depth. + */ + tolerance = (max - min) >> 1; + + if (tolerance < 25) { + tolerance = 25; + } + + for (k = i; k < i + count; k++) { + u16 expecteddepth; + actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2; + expecteddepth = artifacts[k].expecteddepth; + + if (expecteddepth < actualdepth) { + difference = actualdepth - expecteddepth; + } else { + difference = expecteddepth - actualdepth; + } + +#ifdef PLATFORM_N64 + if (difference <= tolerance) { + numgood++; + } +#else + numgood += artifacts[k].visiblelos; +#endif + + artifacts[k].type = ARTIFACTTYPE_FREE; + } + + s3[0] = artifactsClamp(s3[0], numgood * 2); + + if (numgood > 0) { + brightness = viGetFovY() * 0.017453292f; + add = cosf(brightness) / sinf(brightness) * 14.6f; + + if (lightIsHealthy(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { + if (!lightIsOn(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { + continue; + } + + brightness = 1.0f; + } else if (lightTickBroken(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { + brightness = 0.4f; + } else { + continue; + } + + r = ((light->colour >> 12) & 0xf) * 17; + g = ((light->colour >> 8) & 0xf) * 17; + b = ((light->colour >> 4) & 0xf) * 17; + + if ((r == 0xff && g == 0xff && b == 0xff) || (r == 0xff && g + b < 35)) { + extra = false; + } else { + extra = true; + } + + if (USINGDEVICE(DEVICE_NIGHTVISION)) { + s3[2] *= (s32) (lightop_cur_frac * 7.0f); + } + + f0 = s3[2] * (1.0f / 255.0f); + + skySetOverexposure((s32) ((f32)f0 * r), (s32) ((f32)f0 * g), (s32) ((f32)f0 * b)); + + for (l = 0; l < 3; l++) { + lightroompos[l] = (light->bbox[0].s[l] + light->bbox[1].s[l] + light->bbox[2].s[l] + light->bbox[3].s[l]) / 4; + lightworldpos.f[l] = lightroompos[l] + g_BgRooms[roomnum].pos.f[l]; + lightscreenpos.f[l] = lightworldpos.f[l] - g_Vars.currentplayer->cam_pos.f[l]; + } + + mtx4RotateVecInPlace(camGetWorldToScreenMtxf(), &lightscreenpos); + + cam0f0b4d04(&lightscreenpos, spdc); + + brightness *= 27500.0f / (-lightscreenpos.z < 1.0f ? 1.0f : -lightscreenpos.z); + + if (light->brightnessmult != 0) { + brightness *= light->brightnessmult * (1.0f / 32.0f); + } + + brightness *= s3[1] * (1.0f / 255.0f); + + if (USINGDEVICE(DEVICE_NIGHTVISION)) { + brightness *= 14.0f * lightop_cur_frac; + } + + brightness += add; + brightness *= 2.0f * roomGetSettledLocalBrightnessFrac(roomnum); + + if (brightness > 750.0f) { + brightness = 750.0f; + } + + f24 = stageGetCurrent()->light_width * brightness * 0.01f; + f26 = stageGetCurrent()->light_height * brightness * 0.01f; + + f24 *= viGetViewWidth() * (1.0f / 240.0f) / camGetPerspAspect(); + f26 *= viGetViewHeight() * (1.0f / 240.0f); + + if (brightness > 3.0f) { + f32 alpha = (light->colour & 0xf) * 17; + + colour[0] = r; + colour[1] = g; + colour[2] = b; + + alpha *= stageGetCurrent()->light_alpha / 255.0f; + alpha *= (s3[1] / 255.0f); + alpha *= (s3[0] / 8.0f); + + if (USINGDEVICE(DEVICE_NIGHTVISION)) { + alpha *= lightop_cur_frac * 7.0f; + } + + if (alpha > 255.0f) { + alpha = 255.0f; + } + + colour[3] = alpha; + + gDPSetEnvColor(gdl++, colour[0], colour[1], colour[2], colour[3]); + + spd4[0] = f24; + spd4[1] = f26; + + func0f0b2740(&gdl, spdc, spd4, 64, 64, false, false, false, 1); + + if (extra) { + colour[0] = 0xff; + colour[1] = 0xff; + colour[2] = 0xff; + colour[3] = stageGetCurrent()->light_alpha; + colour[3] = s3[0] * colour[3] / 8; + + gDPSetEnvColor(gdl++, colour[0], colour[1], colour[2], colour[3]); + + spd4[0] = f24 * 0.4f; + spd4[1] = f26 * 0.4f; + + func0f0b2740(&gdl, spdc, spd4, 64, 64, false, false, false, 1); + } + } + } + + s3[1] = 0; + s3[2] = 0; + } + + // This is incrementing i past all the artifacts for this particular + // light, then subtracting 1 because the for loop will add 1. + i = i + count - 1; + } + } + + return gdl; +} diff --git a/src/game/bg.c b/src/game/bg.c index 2a3bab3dc..d1569bdc4 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -15,7 +15,7 @@ #include "game/sky.h" #include "game/stars.h" #include "game/dyntex.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/game_1531a0.h" #include "game/gfxmemory.h" #include "game/gfxreplace.h" diff --git a/src/game/bondgun.c b/src/game/bondgun.c index a46e66721..7415bf851 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -10956,7 +10956,7 @@ void bgunRender(Gfx **gdlptr) return; } -#ifdef PLATFORM_N64 +#ifdef PLATFORM_N64 // TODO gdl = zbufSaveArtifactDepths(gdl); #endif gdl = viPrepareZbuf(gdl); diff --git a/src/game/lv.c b/src/game/lv.c index 56d9e9ab3..8db23f155 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -28,7 +28,7 @@ #include "game/portal.h" #include "game/fmb.h" #include "game/sky.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/game_1531a0.h" #include "game/zbuf.h" #include "game/challenge.h" diff --git a/src/game/sky.c b/src/game/sky.c index d5252919c..f2b276f10 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -17,7 +17,7 @@ #ifndef PLATFORM_N64 #include "video.h" #include "game/gfxmemory.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/player.h" #endif diff --git a/src/include/game/artifact.h b/src/include/game/artifact.h new file mode 100644 index 000000000..9feae2a18 --- /dev/null +++ b/src/include/game/artifact.h @@ -0,0 +1,20 @@ +#ifndef _IN_GAME_GAME_13C510_H +#define _IN_GAME_GAME_13C510_H +#include +#include "data.h" +#include "types.h" + +void artifactsClear(void); +void artifactsTick(void); +u16 floatToN64Depth(f32 arg0); +s32 artifactsFloatToInt(f32 arg0); +void artifactsCalculateGlaresForRoom(s32 roomnum); +u8 artifactsClamp(u8 arg0, u8 arg1); +Gfx *artifactsConfigureForGlares(Gfx *gdl); +Gfx *artifactsUnconfigureForGlares(Gfx *gdl); +Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum); +#ifndef PLATFORM_N64 +bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi); +#endif + +#endif From 86e6e547922c11f995f51a326e44d5684fa253b8 Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sun, 11 May 2025 12:22:01 -0500 Subject: [PATCH 30/32] return ifdef to original position following the debugging tests --- src/game/artifact.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/artifact.c b/src/game/artifact.c index 03dae835d..390c161a6 100644 --- a/src/game/artifact.c +++ b/src/game/artifact.c @@ -595,6 +595,7 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) } for (k = i; k < i + count; k++) { +#ifdef PLATFORM_N64 u16 expecteddepth; actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2; expecteddepth = artifacts[k].expecteddepth; @@ -605,7 +606,6 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) difference = expecteddepth - actualdepth; } -#ifdef PLATFORM_N64 if (difference <= tolerance) { numgood++; } From 2d59929261513570a1738ffe573b422c8cd673a0 Mon Sep 17 00:00:00 2001 From: joshuarwood Date: Sun, 11 May 2025 12:23:32 -0500 Subject: [PATCH 31/32] syncing moved files --- src/game/game_13c510.c | 746 --------------------------------- src/include/game/game_13c510.h | 20 - 2 files changed, 766 deletions(-) delete mode 100644 src/game/game_13c510.c delete mode 100644 src/include/game/game_13c510.h diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c deleted file mode 100644 index 4dde60b57..000000000 --- a/src/game/game_13c510.c +++ /dev/null @@ -1,746 +0,0 @@ -#include -#include "lib/sched.h" -#include "constants.h" -#include "game/camera.h" -#include "game/dlights.h" -#include "game/env.h" -#include "game/game_0b2150.h" -#include "game/tex.h" -#include "game/sky.h" -#include "game/game_13c510.h" -#include "game/bg.h" -#include "game/stagetable.h" -#include "game/room.h" -#include "bss.h" -#include "lib/vi.h" -#include "lib/mtx.h" -#include "data.h" -#include "types.h" -#ifndef PLATFORM_N64 -#include "lib/collision.h" -#include "lib/lib_17ce0.h" -#include "game/player.h" -#include "game/prop.h" -#endif - -/** - * Artifacts are points of interest in the z-buffer. - * - * They correspond to: - * - Individual circles in the lens flare effect from the sun. - * - Individual corners of each light fixture's box. - * - * The game needs to do line of sight checks to these points. The collision - * system would normally be used for line of sight checks, but it only works - * with basic geometric volumes and also doesn't take into account the player's - * gun. Instead, the checks are done by reading from the previous frame's - * z-buffer. - * - * Typically, the z-buffer would be read by the scheduler after a graphics frame - * is rendered, but this only works if the z-buffer is a complete image. - * PD draws the scene, then clears the z-buffer before drawing the player's gun, - * so the z-buffer at the end only contains the player's gun. To work around - * this, the GPU is given commands to read the z-buffer as a texture and copy - * depth values to a safe place before clearing it. Then once the frame is - * rendered, the scheduler compares the saved depths with the current z-buffer - * and updates the artifact, applying the minimum of the two depths. - * - * The scheduler maintains 3 arrays of artifacts, each referenced by indexes - * which rotate between them. - * - Artifacts are written by the main thread using the write index. - * The write and front indexes are then incremented. - * - The same artifacts are updated by the scheduler using the pending index. - * The pending index is then updated. - * - The artifacts are then rendered on a later frame using the front index. - * - * In each artifacts array, the first several slots are reserved for the sun - * flare artifacts. The quantity depends on the number of suns in the stage - * (Skedar Ruins has 3) and there are 8 artifacts per sun. The remaining slots - * are used for light fixture corners. The array is big enough to handle at - * least 90 lights on screen at a time. - * - * The initial state of the indexes are write=0, front=1, pending=0. - * These are set in sched_reset_artifacts. - * - * The detailed workflow is: - * - CPU: Light artifacts are determined and added to write artifacts (0) array - * - CPU: Constructs the gdl in this order: - * - Render scene - * - Copy relevant parts of z-buffer to write depths (0) array - * - Clear z-buffer and render gun - * - Render artifacts by reading from front artifacts (1) array - * - CPU: increments write (0 -> 1) and front (1 -> 2) indexes - * - GPU: Executes above task. g_ArtifactDepths0 now contains depths, and is - * pointed to by pending. - * - Scheduler: Reads the z-buffer, which at this point only contains the gun - * depth information, and updates the artifact with the minimum of the two - * - Scheduler: Increments pending (0 -> 1) - * - * It appears that the front index should be initialised to 2 instead, so that - * it's one frame behind the others rather than two frames behind. - * - * There's no doubt this went through several iterations before landing on this - * implementation. It's likely that this was implemented using a single and full - * z-buffer and it worked well until they decided to clear the z-buffer before - * rendering the gun. There is evidence in zbuf.c that they allocated a second - * z-buffer and swapped it. But when memory got too tight they had to change it - * to this texture read method. - */ - -u8 *var800a41a0; -u32 var800a41a4; -u32 var800a41a8; -u32 var800a41ac; - -void artifactsClear(void) -{ - struct artifact *artifacts = schedGetWriteArtifacts(); - s32 i; - - for (i = 0; i < MAX_ARTIFACTS; i++) { - artifacts[i].type = ARTIFACTTYPE_FREE; - } -} - -void artifactsTick(void) -{ - schedIncrementWriteArtifacts(); - schedIncrementFrontArtifacts(); -} - -u16 floatToN64Depth(f32 arg0) -{ - /** - * Method to convert a 32 bit floating point depth value to the - * unsigned 16 bit integer format used by the zbuffer on the N64. - * The argument arg0 represents z normalized to [0, 1] * 32704.0f. - * - * It works by converting the depth value to a large, unsigned 32 bit - * integer before scaling back down to an unsigned 16 bit integer. - * The scaling is done using bit shift operations on the most & least - * significant bytes of the resulting 16 bit integer, likely because - * bit shift operations are faster than division. The segmentation of - * this calculation at values 0x3f800, 0x3f000, etc. is probably - * intended to reduce z-fighting by adding more differentiation for - * distant z values. - */ - u32 value = arg0 * 8.0f; - u32 left; // forms the most significant byte of the final u16 - u32 right = value; // forms the least significant byte of the final u16 - - if (value > 0x3f800) { - right = value & 0x7ff; - right &= 0x7ff; - left = 7; - } else if (value > 0x3f000) { - right = value & 0x7ff; - right &= 0x7ff; - left = 6; - } else if (value > 0x3e000) { - right = (value >> 1) & 0x7ff; - right &= 0x7ff; - left = 5; - } else if (value > 0x3c000) { - right = (value >> 2) & 0x7ff; - right &= 0x7ff; - left = 4; - } else if (value > 0x38000) { - right = (value >> 3) & 0x7ff; - right &= 0x7ff; - left = 3; - } else if (value > 0x30000) { - right = (value >> 4) & 0x7ff; - right &= 0x7ff; - left = 2; - } else if (value > 0x20000) { - right = (value >> 5) & 0x7ff; - right &= 0x7ff; - left = 1; - } else { - right = (value >> 6) & 0x7ff; - right &= 0x7ff; - left = 0; - } - - return left << 13 | (right << 2); -} - -s32 func0f13c710(f32 arg0) -{ - if (arg0 > 0.0f) { - if (arg0 > 2147483520.0f) { - arg0 = 2147483520; - } - } else { - if (arg0 < -2147483520) { - arg0 = -2147483520; - } - } - - return arg0; -} - -#ifndef PLATFORM_N64 - -bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi) -{ - s32 i = 0; - - if (!g_Vars.currentplayer) { - return false; - } - - struct coord endpos; - endpos.x = roompos->x + spec->x; - endpos.y = roompos->y + spec->y; - endpos.z = roompos->z + spec->z; - - struct coord gundir2d; - struct coord gunpos2d = {{ 0.f, 0.f, 0.f }}; - struct coord gundir3d; - struct coord gunpos3d = g_Vars.currentplayer->cam_pos; - f32 crosspos[2] = { (f32)xi, (f32)yi }; - cam0f0b4c3c(crosspos, &gundir2d, 1.f); - mtx4RotateVec(camGetProjectionMtxF(), &gundir2d, &gundir3d); - - return shotTestLos(&gunpos2d, &gundir2d, &gunpos3d, &gundir3d, &endpos); -} - -#endif - -void artifactsCalculateGlaresForRoom(s32 roomnum) -{ - s32 i; - s32 j; - s32 k; - s32 l; - f32 f0; - s32 numlights; - f32 viewwidth; - f32 viewheight; - f32 viewleft; - f32 viewtop; - u8 *s1; - f32 x; - f32 y; - f32 f16; - f32 f20; - s32 xi; - s32 yi; - f32 sp190; - f32 brightnessfrac; - f32 thisfrac; - f32 tmp; - f32 tmp2; - f32 tmp3; - f32 sp178; - Mtxf sp138; - Mtxf spf8; - struct coord spec; - f32 spdc[4]; - struct coord origin; - struct coord spc4; - struct light *roomlights; - s32 index; - struct artifact *artifacts = schedGetWriteArtifacts(); - struct coord *campos = &g_Vars.currentplayer->cam_pos; - struct artifact *artifact; - - if (g_Rooms[roomnum].gfxdata != NULL && g_Rooms[roomnum].loaded240) { - numlights = g_Rooms[roomnum].gfxdata->numlights; - - if (numlights != 0) { - roomlights = (struct light *)&g_BgLightsFileData[g_Rooms[roomnum].gfxdata->lightsindex * 0x22]; - s1 = &var800a41a0[g_Rooms[roomnum].gfxdata->lightsindex * 3]; - - roomPopulateMtx(&sp138, roomnum); - mtx00015f88(bgGetScaleBg2Gfx(), &sp138); - mtx4MultMtx4(camGetMtxF006c(), &sp138, &spf8); - - viewwidth = viGetViewWidth(); - viewheight = viGetViewHeight(); - viewleft = viGetViewLeft(); - viewtop = viGetViewTop(); - - for (i = 0; i < numlights; i++) { - origin.x = 0.0f; - origin.y = 0.0f; - origin.z = 0.0f; - - for (j = 0; j < ARRAYCOUNT(roomlights[i].bbox); j++) { - origin.x += roomlights[i].bbox[j].x; - origin.y += roomlights[i].bbox[j].y; - origin.z += roomlights[i].bbox[j].z; - } - - origin.x /= 4.0f; - origin.y /= 4.0f; - origin.z /= 4.0f; - - for (j = 0; j != 3; j++) { - spc4.f[j] = origin.f[j] - (campos->f[j] - g_BgRooms[roomnum].pos.f[j]); - } - - s1[i * 3 + 1] = 0; - s1[i * 3 + 2] = 0; - - tmp = roomlights[i].dirx * roomlights[i].dirx + roomlights[i].diry * roomlights[i].diry + roomlights[i].dirz * roomlights[i].dirz; - f16 = spc4.f[0] * spc4.f[0] + spc4.f[1] * spc4.f[1] + spc4.f[2] * spc4.f[2]; - - if (tmp > 0.0001f && f16 > 0.0001f) { - sp190 = -((roomlights[i].dirx * spc4.f[0] + roomlights[i].diry * spc4.f[1] + roomlights[i].dirz * spc4.f[2]) / sqrtf(tmp * f16)); - - if (sp190 > 0.4f) { - sp190 = 0.4f; - } - - sp190 *= 2.5f; - } else { - sp190 = 0.0f; - } - - if (sp190 > 0.0f) { - for (l = 3; l >= 0; l--) { - spdc[l] = origin.f[0] * spf8.m[0][l] + origin.f[1] * spf8.m[1][l] + origin.f[2] * spf8.m[2][l] + spf8.m[3][l]; - - if (l == 3 && spdc[l] <= 0.0f) { - break; - } - } - - if (spdc[3] > 0.0001f) { - f20 = 1.0f / spdc[3]; - x = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); - y = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); - f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; - - if (f0 < 32576.0f) { - brightnessfrac = 1.0f; - tmp2 = (brightnessfrac - 1.00f); - - if (x <= 10.0f + viewleft) { - brightnessfrac = 0.0f; - } else if (y <= 30.0f + viewtop) { - brightnessfrac = 0.0f; - } else if (x >= -10.0f + viewleft + viewwidth) { - brightnessfrac = 0.0f; - } else if (y >= -30.0f + viewtop + viewheight) { - brightnessfrac = 0.0f; - } - - sp178 = 1.0f - 2.0f * tmp2; - - if (brightnessfrac != 0.0f) { - brightnessfrac = 1.0f; - - if (x < viewleft + 90.0f) { - thisfrac = (x - (10.0f + viewleft)) / 80.0f; - - if (thisfrac < brightnessfrac) { - brightnessfrac = thisfrac; - } - } - - if (y < viewtop + 100.0f) { - thisfrac = (y - (viewtop + 30.0f)) / 70.0f; - - if (thisfrac < brightnessfrac) { - brightnessfrac = thisfrac; - } - } - - if (x > viewleft + viewwidth - 90.0f) { - thisfrac = (viewleft + viewwidth - 10.0f - x) / 80.0f; - - if (thisfrac < brightnessfrac) { - brightnessfrac = thisfrac; - } - } - - if (y > viewtop + viewheight - 100.0f) { - thisfrac = (viewtop + viewheight - 30.0f - y) / 70.0f; - - if (thisfrac < brightnessfrac) { - brightnessfrac = thisfrac; - } - } - } - - tmp3 = 32300.0f - f0; - - if (tmp3 < 0.0f) { - tmp3 = 0.0f; - } - - if (tmp3 > 1300.0f) { - tmp3 = 1300.0f; - } - - tmp3 *= 1.0f / 1300.0f; - - if (2.0f * tmp2 > 1.0f) { - sp178 = 0.0f; - } - - s1[i * 3 + 1] = sp190 * 255.0f * sp178; - s1[i * 3 + 2] = brightnessfrac * tmp3 * sp190 * 64.0f * 1; - } - } - } - - if (s1[i * 3 + 1] > 0) { - for (j = 0; j < ARRAYCOUNT(roomlights[i].bbox); j++) { - spec.x = origin.x + (roomlights[i].bbox[j].x - origin.x) * 0.6f; - spec.y = origin.y + (roomlights[i].bbox[j].y - origin.y) * 0.6f; - spec.z = origin.z + (roomlights[i].bbox[j].z - origin.z) * 0.6f; - - for (k = 3; k >= 0; k--) { - spdc[k] = spec.f[0] * spf8.m[0][k] + spec.f[1] * spf8.m[1][k] + spec.f[2] * spf8.m[2][k] + spf8.m[3][k]; - - if (k == 3 && spdc[k] <= 0.0f) { - break; - } - } - - if (spdc[3] > 0.0f) { - f20 = 1.0f / spdc[3]; - - if (f20 > 9999.0f) { - f20 = 9999.0f; - } - - if (f20 < -9999.0f) { - f20 = -9999.0f; - } - - xi = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); - yi = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); - f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; - - if (g_ZbufPtr1 - && xi >= (s32)viewleft - && xi < (s32)(viewleft + viewwidth) - && yi >= (s32)viewtop - && yi < (s32)(viewtop + viewheight) - && f0 < 32576.0f) { - index = envGetCurrent()->numsuns; - index *= 8; - artifact = artifacts; - artifact += index; - - while (artifact->type != ARTIFACTTYPE_FREE) { - index++; - artifact++; - } - - if (index < MAX_ARTIFACTS) { -#ifndef PLATFORM_N64 - artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); -#endif - /** - * the game performs artifact depth comparison using - * the original N64 depth values divided by 4, which - * is accomplished by bit shifting the N64 depth value - * to the right using >> 2 - */ - artifact->expecteddepth = floatToN64Depth(f0) >> 2; - artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi]; - artifact->light = &roomlights[i]; - artifact->type = ARTIFACTTYPE_GLARE; - artifact->screenx = xi; - artifact->screeny = yi; - } - } - } - } - } - } - } - } -} - -u8 artifactsClamp(u8 arg0, u8 arg1) -{ - if (arg1 >= arg0 + 7) { - return arg0 + 7; - } - - if (arg1 <= arg0 - 7) { - return arg0 - 7; - } - - return arg1; -} - -Gfx *artifactsConfigureForGlares(Gfx *gdl) -{ - struct stagetableentry *stage = stageGetCurrent(); - - texSelect(&gdl, &g_TexLightGlareConfigs[stage->light_type], 4, 0, 2, 1, NULL); - - gDPSetCycleType(gdl++, G_CYC_1CYCLE); - gDPSetRenderMode(gdl++, G_RM_CLD_SURF, G_RM_CLD_SURF2); - gDPSetTextureFilter(gdl++, G_TF_BILERP); - gDPSetCombineLERP(gdl++, - 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, - 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0); - gDPSetColorDither(gdl++, G_CD_BAYER); - gDPSetAlphaDither(gdl++, G_AD_PATTERN); - gDPSetTexturePersp(gdl++, G_TP_NONE); - - return gdl; -} - -Gfx *artifactsUnconfigureForGlares(Gfx *gdl) -{ - gDPSetTexturePersp(gdl++, G_TP_PERSP); - - return gdl; -} - -Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) -{ - s32 i; - s32 j; - s32 lightindex; - struct artifact *artifacts; - u16 min; - u16 max; - f32 lightop_cur_frac; - s32 numgood; - struct light *light; - u8 *s3; - s32 k; - s32 count; - u16 actualdepth; - f32 add; - s32 l; - f32 brightness; - s32 tolerance; - f32 f0; - s32 difference; - s32 r; - s32 g; - s32 b; - s32 stack; - u8 colour[4]; - s16 lightroompos[3]; - struct coord lightworldpos; - struct coord lightscreenpos; - f32 spdc[2]; - f32 spd4[2]; - f32 f24; - bool extra; - f32 f26; - - artifacts = schedGetFrontArtifacts(); - lightop_cur_frac = roomGetLightOpCurFrac(roomnum); - - if (g_Rooms[roomnum].gfxdata == NULL || g_Rooms[roomnum].loaded240 == 0) { - return gdl; - } - - for (i = envGetCurrent()->numsuns * 8; i < MAX_ARTIFACTS; i++) { - struct light *light2 = artifacts[i].light; - count = 0; - - /** - * light arifacts are created from several, closely spaced - * textures that give the appearance of a dynamic light glare - * as the character moves. Loop to count all the sub-artifacts - * in this light. - */ - for (j = i; j < MAX_ARTIFACTS && artifacts[j].type == ARTIFACTTYPE_GLARE && artifacts[j].light == light2; j++) { - count++; - } - - light = artifacts[i].light; - - if (count > 0) { - if (roomnum == light->roomnum) { - lightindex = ((uintptr_t)light - (uintptr_t)g_BgLightsFileData) / sizeof(struct light); - s3 = &var800a41a0[lightindex * 3]; - numgood = 0; - min = 0xffff; - max = 0; - - /** - * loop to determine the min & max depth of - * the sub-artifacts composing this room light. - */ - for (k = i; k < i + count; k++) { - if (artifacts[k].expecteddepth > max) { - max = artifacts[k].expecteddepth; - } - - if (artifacts[k].expecteddepth < min) { - min = artifacts[k].expecteddepth; - } - } - - /** - * Define a depth tolerance from the min & max - * depths of sub-artifacts for a given light. - * This will be used to determine which light - * artifacts are visible when comparing depth - * values of lights to other items rendered - * on screen. Without this, the lights would - * constantly flicker due to z-fighting caused - * by the low precision of the N64 depth. - */ - tolerance = (max - min) >> 1; - - if (tolerance < 25) { - tolerance = 25; - } - - for (k = i; k < i + count; k++) { - u16 expecteddepth; - actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2; - expecteddepth = artifacts[k].expecteddepth; - - if (expecteddepth < actualdepth) { - difference = actualdepth - expecteddepth; - } else { - difference = expecteddepth - actualdepth; - } - -#ifdef PLATFORM_N64 - if (difference <= tolerance) { - numgood++; - } -#else - numgood += artifacts[k].visiblelos; -#endif - - artifacts[k].type = ARTIFACTTYPE_FREE; - } - - s3[0] = artifactsClamp(s3[0], numgood * 2); - - if (numgood > 0) { - brightness = viGetFovY() * 0.017453292f; - add = cosf(brightness) / sinf(brightness) * 14.6f; - - if (lightIsHealthy(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { - if (!lightIsOn(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { - continue; - } - - brightness = 1.0f; - } else if (lightTickBroken(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) { - brightness = 0.4f; - } else { - continue; - } - - r = ((light->colour >> 12) & 0xf) * 17; - g = ((light->colour >> 8) & 0xf) * 17; - b = ((light->colour >> 4) & 0xf) * 17; - - if ((r == 0xff && g == 0xff && b == 0xff) || (r == 0xff && g + b < 35)) { - extra = false; - } else { - extra = true; - } - - if (USINGDEVICE(DEVICE_NIGHTVISION)) { - s3[2] *= (s32) (lightop_cur_frac * 7.0f); - } - - f0 = s3[2] * (1.0f / 255.0f); - - skySetOverexposure((s32) ((f32)f0 * r), (s32) ((f32)f0 * g), (s32) ((f32)f0 * b)); - - for (l = 0; l < 3; l++) { - lightroompos[l] = (light->bbox[0].s[l] + light->bbox[1].s[l] + light->bbox[2].s[l] + light->bbox[3].s[l]) / 4; - lightworldpos.f[l] = lightroompos[l] + g_BgRooms[roomnum].pos.f[l]; - lightscreenpos.f[l] = lightworldpos.f[l] - g_Vars.currentplayer->cam_pos.f[l]; - } - - mtx4RotateVecInPlace(camGetWorldToScreenMtxf(), &lightscreenpos); - - cam0f0b4d04(&lightscreenpos, spdc); - - brightness *= 27500.0f / (-lightscreenpos.z < 1.0f ? 1.0f : -lightscreenpos.z); - - if (light->brightnessmult != 0) { - brightness *= light->brightnessmult * (1.0f / 32.0f); - } - - brightness *= s3[1] * (1.0f / 255.0f); - - if (USINGDEVICE(DEVICE_NIGHTVISION)) { - brightness *= 14.0f * lightop_cur_frac; - } - - brightness += add; - brightness *= 2.0f * roomGetSettledLocalBrightnessFrac(roomnum); - - if (brightness > 750.0f) { - brightness = 750.0f; - } - - f24 = stageGetCurrent()->light_width * brightness * 0.01f; - f26 = stageGetCurrent()->light_height * brightness * 0.01f; - - f24 *= viGetViewWidth() * (1.0f / 240.0f) / camGetPerspAspect(); - f26 *= viGetViewHeight() * (1.0f / 240.0f); - - if (brightness > 3.0f) { - f32 alpha = (light->colour & 0xf) * 17; - - colour[0] = r; - colour[1] = g; - colour[2] = b; - - alpha *= stageGetCurrent()->light_alpha / 255.0f; - alpha *= (s3[1] / 255.0f); - alpha *= (s3[0] / 8.0f); - - if (USINGDEVICE(DEVICE_NIGHTVISION)) { - alpha *= lightop_cur_frac * 7.0f; - } - - if (alpha > 255.0f) { - alpha = 255.0f; - } - - colour[3] = alpha; - - gDPSetEnvColor(gdl++, colour[0], colour[1], colour[2], colour[3]); - - spd4[0] = f24; - spd4[1] = f26; - - func0f0b2740(&gdl, spdc, spd4, 64, 64, false, false, false, 1); - - if (extra) { - colour[0] = 0xff; - colour[1] = 0xff; - colour[2] = 0xff; - colour[3] = stageGetCurrent()->light_alpha; - colour[3] = s3[0] * colour[3] / 8; - - gDPSetEnvColor(gdl++, colour[0], colour[1], colour[2], colour[3]); - - spd4[0] = f24 * 0.4f; - spd4[1] = f26 * 0.4f; - - func0f0b2740(&gdl, spdc, spd4, 64, 64, false, false, false, 1); - } - } - } - - s3[1] = 0; - s3[2] = 0; - } - - // This is incrementing i past all the artifacts for this particular - // light, then subtracting 1 because the for loop will add 1. - i = i + count - 1; - } - } - - return gdl; -} diff --git a/src/include/game/game_13c510.h b/src/include/game/game_13c510.h deleted file mode 100644 index f84ad1395..000000000 --- a/src/include/game/game_13c510.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _IN_GAME_GAME_13C510_H -#define _IN_GAME_GAME_13C510_H -#include -#include "data.h" -#include "types.h" - -void artifactsClear(void); -void artifactsTick(void); -u16 floatToIntDepth(f32 arg0); -s32 func0f13c710(f32 arg0); -void artifactsCalculateGlaresForRoom(s32 roomnum); -u8 func0f13d3c4(u8 arg0, u8 arg1); -Gfx *artifactsConfigureForGlares(Gfx *gdl); -Gfx *artifactsUnconfigureForGlares(Gfx *gdl); -Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum); -#ifndef PLATFORM_N64 -bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi); -#endif - -#endif From 45f25c704a9f2f545242bf9756901001c9000839 Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sat, 24 May 2025 13:38:43 -0700 Subject: [PATCH 32/32] refactor(menu): rename unk5d5_06 and sp344 for clarity Backport from Friends of Joanna: renamed the bool member `unk5d5_06` in the `menudata` struct to `isdialogopen` to clarify its purpose as a dialog state flag. Also renamed the local bool variable `sp344` in `menutick.c` to `isdialogopen` to reflect its relation to the struct member. Updated all references for consistency and improved readability. --- src/game/menu.c | 2 +- src/game/menutick.c | 18 +++++++++--------- src/include/types.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/game/menu.c b/src/game/menu.c index 705beed9a..c9f4cca3a 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -4011,7 +4011,7 @@ void menuReset(void) g_MenuData.root = 0; g_MenuData.unk010 = 0; g_MenuData.bg = 0; - g_MenuData.unk5d5_06 = false; + g_MenuData.isdialogopen = false; g_MenuData.nextbg = 255; g_MenuData.bannernum = -1; diff --git a/src/game/menutick.c b/src/game/menutick.c index ca7606067..671d33cc0 100644 --- a/src/game/menutick.c +++ b/src/game/menutick.c @@ -59,7 +59,7 @@ void menuTick(void) s32 i; s32 j; s32 k; - s32 sp344; + s32 isdialogopen; s32 sp340 = true; s32 anyopen = false; @@ -485,15 +485,15 @@ void menuTick(void) } g_MpPlayerNum = 0; - sp344 = false; + isdialogopen = false; for (i = 0; i < ARRAYCOUNT(g_Menus); i++) { if (g_Menus[i].curdialog) { - sp344 = true; + isdialogopen = true; } } - if ((g_MenuData.unk5d5_06 || g_MenuData.prevmenuroot != -1) && sp344 == false) { + if ((g_MenuData.isdialogopen || g_MenuData.prevmenuroot != -1) && isdialogopen == false) { if ((g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) && g_MenuData.prevmenuroot == -1) { if (g_Vars.mpsetupmenu == MPSETUPMENU_GENERAL) { @@ -541,17 +541,17 @@ void menuTick(void) setCurrentPlayerNum(playernum); endscreenPushCoop(); setCurrentPlayerNum(prevplayernum); - sp344 = true; + isdialogopen = true; } } else if (g_Vars.antiplayernum >= 0) { s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(playernum); endscreenPushAnti(); setCurrentPlayerNum(prevplayernum); - sp344 = true; + isdialogopen = true; } else { mpPushEndscreenDialog(playernum, i); - sp344 = true; + isdialogopen = true; if (g_PlayerConfigsArray[i].fileguid.fileid && g_PlayerConfigsArray[i].fileguid.deviceserial) { func0f0fd548(i); @@ -571,7 +571,7 @@ void menuTick(void) } else { bool startmusic = false; menuPushRootDialog(g_MenuData.prevmenudialog, g_MenuData.prevmenuroot); - sp344 = true; + isdialogopen = true; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { startmusic = true; @@ -750,5 +750,5 @@ void menuTick(void) } g_ScaleX = 1; - g_MenuData.unk5d5_06 = sp344 ? true : false; + g_MenuData.isdialogopen = isdialogopen ? true : false; } diff --git a/src/include/types.h b/src/include/types.h index 7b005a84d..5f8791717 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -4802,7 +4802,7 @@ struct menudata { /*0x5d5*/ u8 usezbuf : 1; /*0x5d5*/ u8 unk5d5_04 : 1; /*0x5d5*/ u8 unk5d5_05 : 1; - /*0x5d5*/ u8 unk5d5_06 : 1; + /*0x5d5*/ u8 isdialogopen : 1; /*0x5d5*/ u8 unk5d5_07 : 1; /*0x5d5*/ u8 unk5d5_08 : 1; /*0x5d8*/ struct menudata_5d8 unk5d8[12];