From f5d851f7c2d6a491b866f75e087a88d93e6f53c8 Mon Sep 17 00:00:00 2001 From: Raf <> Date: Mon, 24 Mar 2025 03:33:34 +0800 Subject: [PATCH 01/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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 }