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/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/input.h b/port/include/input.h index 6d5d88bfd..6171c2696 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, diff --git a/port/include/mpsetups.h b/port/include/mpsetups.h new file mode 100644 index 000000000..3ef143906 --- /dev/null +++ b/port/include/mpsetups.h @@ -0,0 +1,12 @@ +#ifndef _IN_MPSETUPS_H +#define _IN_MPSETUPS_H + +#include "types.h" + +s32 mpsetupLoadCurrentFile(void); +s32 mpsetupSaveCurrentFile(void); +void mpsetupLoadSetup(s32 slotindex); +s32 mpsetupSaveSetup(s32 slotindex, u8 savefile); +void mpsetupCopyAllFromPak(void); + +#endif diff --git a/port/src/fs.c b/port/src/fs.c index 40335a3fa..459e72392 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,12 @@ void fsFileFree(FILE *f) { fclose(f); } + +s32 fsCreateDir(const char *path) +{ +#ifdef PLATFORM_WIN32 + return _mkdir(fsFullPath(path)); +#else + return mkdir(fsFullPath(path), 0777); +#endif +} diff --git a/port/src/input.c b/port/src/input.c index 2bacbd646..96e37255e 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -88,8 +88,8 @@ static s32 mouseLockMode = MLOCK_AUTO; static u64 mouseCursorTime = 0; static s32 mouseShowCursor = 1; -static f32 mouseSensX = 1.5f; -static f32 mouseSensY = 1.5f; +static f32 mouseSensX = 2.5f; +static f32 mouseSensY = 2.5f; static s32 lastKey = 0; static char lastChar = 0; @@ -1239,32 +1239,28 @@ void inputMouseGetRawDelta(s32 *dx, s32 *dy) if (dy) *dy = mouseDY; } -void inputMouseGetScaledDelta(f32 *dx, f32 *dy) +void inputMouseGetScaledDelta(f32* dx, f32* dy) { - f32 mdx, mdy; - if (mouseLocked) { - mdx = mouseSensX * (f32)mouseDX / 100.0f; - mdy = mouseSensY * (f32)mouseDY / 100.0f; - } else { - mdx = 0.f; - mdy = 0.f; - } - if (dx) *dx = mdx; - if (dy) *dy = mdy; + f32 mdx = 0.f, mdy = 0.f; + + if (mouseLocked) { + mdx = mouseSensX * ((f32)mouseDX / 3.5f) * 0.022f; + mdy = mouseSensY * ((f32)mouseDY / 3.5f) * 0.022f; + } + if (dx) *dx = mdx; + if (dy) *dy = mdy; } -void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy) +void inputMouseGetAbsScaledDelta(f32* dx, f32* dy) { - f32 mdx, mdy; - if (mouseLocked) { - mdx = fabsf(mouseSensX) * (f32)mouseDX / 100.0f; - mdy = fabsf(mouseSensY) * (f32)mouseDY / 100.0f; - } else { - mdx = 0.f; - mdy = 0.f; - } - if (dx) *dx = mdx; - if (dy) *dy = mdy; + f32 mdx = 0.f, mdy = 0.f; + + if (mouseLocked) { + mdx = fabsf(mouseSensX) * ((f32)mouseDX / 3.5f) * 0.022f; + mdy = fabsf(mouseSensY) * ((f32)mouseDY / 3.5f) * 0.022f; + } + if (dx) *dx = mdx; + if (dy) *dy = mdy; } void inputMouseGetSpeed(f32 *x, f32 *y) diff --git a/port/src/mpsetups.c b/port/src/mpsetups.c new file mode 100644 index 000000000..2c308fb9c --- /dev/null +++ b/port/src/mpsetups.c @@ -0,0 +1,896 @@ +#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" +#include +#include "fs.h" +#include "system.h" +#include "mpsetups.h" + +/* +MP Setup File Format + # header + [version{1}] + [defaultsetup{1}] + [numsetups{1}] + # setups + [setup_1{80}] + ... + [setup_n{80}] + */ + +#define MPSETUP_VERSION 1 + +#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; + +s16 g_MpCurrentSetup = -1; +struct mpsetupfile g_MpSetupFile; + +static struct mpsetupfile g_ImportMpSetupFile; +static u64 g_MpImportExportFilter[2]; + +static char g_StatusText[128]; +static char g_TitleImportExportDialog[20]; +static char g_LabelSetDefault[20] = "Set Default\n"; + +/* extended mpsetup menu definitions */ + +static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data); + +static struct menuitem g_StatusOkMenuItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + 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 }, +}; + +static struct menudialogdef g_StatusOkDialog = { + MENUDIALOGTYPE_SUCCESS, + L_OPTIONS_345, // "Cool!" + g_StatusOkMenuItems, + NULL, + MENUDIALOGFLAG_DISABLEBANNER, + NULL, +}; + +static 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 }, +}; + +/* public */ +struct menudialogdef g_StatusErrorDialog = { + MENUDIALOGTYPE_DANGER, + L_OPTIONS_277, // "Failed" + g_StatusErrorMenuItems, + NULL, + MENUDIALOGFLAG_DISABLEBANNER, + NULL, +}; + +static struct menuitem g_RenameSetupItems[] = { +#if VERSION != VERSION_JPN_FINAL + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING, + (uintptr_t)"Enter the setup name:\n", + 0, + NULL, + }, +#endif + { + MENUITEMTYPE_KEYBOARD, + 18, + 0, + 0, + 1, + menuhandlerRenameSetup, + }, + { MENUITEMTYPE_END }, +}; + +static struct menudialogdef g_RenameSetupDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Setup Name:\n", + g_RenameSetupItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static 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 }, +}; + +static struct menudialogdef g_DeleteSetupDialog = { + MENUDIALOGTYPE_DANGER, + (uintptr_t)"Delete Setup\n", + g_DeleteSetupItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static struct menuitem g_ImportExportItems[] = { + { + MENUITEMTYPE_LIST, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LABEL_CUSTOMCOLOUR, + 140, + 0x0000004d, + menuhandlerImportOrExportSettings, + }, + { MENUITEMTYPE_END }, +}; + +static struct menudialogdef g_ImportExportDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) g_TitleImportExportDialog, + g_ImportExportItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static 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 }, +}; + +static struct menudialogdef g_ManageImportExportDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Import/Export\n", + g_ManageImportExportItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static struct menuitem g_MpManageSettingsListItems[] = { + { + MENUITEMTYPE_LIST, + 0, + MENUITEMFLAG_LABEL_CUSTOMCOLOUR, + 160, + 0x00000042, + menuhandlerSelectSetupHandler, + }, + { MENUITEMTYPE_END }, + +}; + +/* public */ +struct menudialogdef g_ManageSettingsDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t) "Manage Settings\n", + g_MpManageSettingsListItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + &g_ManageImportExportDialog, +}; + +static 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_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)g_LabelSetDefault, + 0, + menuhandlerSetupSetDefault, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +static struct menudialogdef g_ManageSetupDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Manage Setup", + g_ManageSetupItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static struct menuitem g_ImportOverrideItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + 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 }, +}; + +static struct menudialogdef g_ImportOverrideDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Name Conflicts\n", + g_ImportOverrideItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +/* common utils */ + +static s32 mpsetupDeserialize(FILE *f, struct mpsetupfile *setupfile) +{ + s32 rx = 0; + + rx += fread(&setupfile->version, sizeof(setupfile->version), 1, f); + rx += fread(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f); + rx += fread(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f); + + for (int i = 0; i < setupfile->numsetups; ++i) { + rx += fread(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f); + } + + return rx; +} + +static s32 mpsetupSerialize(FILE *f, struct mpsetupfile *setupfile) +{ + s32 wx = 0; + + wx += fwrite(&setupfile->version, sizeof(setupfile->version), 1, f); + wx += fwrite(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f); + wx += fwrite(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f); + + for (int i = 0; i < setupfile->numsetups; ++i) { + wx += fwrite(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f); + } + + return wx; +} + +static FILE *mpsetupOpenFile(bool write, 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(filename) < 0) { + // setup file doesn't exist: create one + f = fsFileOpenWrite(filename); + fsFileFree(f); + } + + if (write) { + f = fsFileOpenWrite(filename); + } else { + f = fsFileOpenRead(filename); + } + + if (f == NULL) { + sysLogPrintf(LOG_ERROR, "Unable to open mp setup file"); + return NULL; + } + + return f; +} + +static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile) +{ + FILE *f = mpsetupOpenFile(true, op); + if (f == NULL) { + return -1; + } + + s32 nwritten = mpsetupSerialize(f, setupfile); + if (nwritten < 1) { + fsFileFree(f); + sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file"); + snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write the setup file\n"); + return -1; + } + + fsFileFree(f); + return 0; +} + +static s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op) +{ + FILE *f = mpsetupOpenFile(false, op); + if (f == NULL) { + return -1; + } + + mpsetupDeserialize(f, setupfile); + + if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup > 0) { + mpsetupLoadSetup(setupfile->defaultsetup - 1); + } + + fsFileFree(f); + + return 0; +} + +static s32 mpsetupImportFile(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) { + snprintf(g_StatusText, sizeof(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 mpsetupSaveCurrentFile(); +} + +static s32 mpsetupExportFile(void) +{ + struct mpsetupfile expMpSetupFile; + expMpSetupFile.numsetups = 0; + 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; + u8 bit = i - bank * 64; + if (g_MpImportExportFilter[bank] & (1 << bit)) { + u8 n = expMpSetupFile.numsetups; + expMpSetupFile.numsetups++; + char *name = g_MpSetupFile.setups[i].bytes; + 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) { + snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write\nsetup file\n"); + } + + return err; +} + +static s32 mpsetupDelete(void) +{ + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + for (int i = slotindex; i < g_MpSetupFile.numsetups - 1; ++i) { + u8* dst = g_MpSetupFile.setups[i].bytes; + u8* src = g_MpSetupFile.setups[i+1].bytes; + memcpy(dst, src, MPSETUP_BLOCKSIZE); + } + + if (g_MpCurrentSetup > slotindex) { + g_MpCurrentSetup--; + } + else if (g_MpCurrentSetup == slotindex) { + g_MpCurrentSetup = -1; + } + + if (g_MpSetupFile.defaultsetup > slotindex + 1) { + g_MpSetupFile.defaultsetup--; + } + else if (g_MpSetupFile.defaultsetup == slotindex + 1) { + g_MpSetupFile.defaultsetup = 0; + } + + g_MpSetupFile.numsetups--; + return mpsetupSaveCurrentFile(); +} + +/* menu handlers */ + +// Rename setup dialog +static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data) +{ + char *name = data->keyboard.string; + s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + struct setupblock *setup = &g_MpSetupFile.setups[slotindex]; + s32 err; + + switch (operation) { + case MENUOP_GETTEXT: + strcpy(name, setup->bytes); + break; + case MENUOP_SETTEXT: + strcpy(g_MpSetup.name, name); + break; + case MENUOP_SET: + strcpy(setup->bytes, name); + err = mpsetupSaveSetup(slotindex, true); + if (!err) { + menuPopDialog(); + } else { + // TODO + // menuPushDialog(&g_FilemgrFileSavedMenuDialog); + } + break; + } + + return 0; +} + +// Delete setup dialog +static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 err = mpsetupDelete(); + if (!err) { + menuPopDialog(); + menuPopDialog(); + } else { + // TODO + } + } + + return 0; +} + +// Import (or Export) settings dialog +static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *labels[] = { + "Select All", + "Select None", + "Import", + "Export", + }; + + s32 op = g_Menus[g_MpPlayerNum].mpsetup.unke24; + + struct mpsetupfile *setupfile = op == MPSETUP_OP_IMPORT ? &g_ImportMpSetupFile : &g_MpSetupFile; + + u8 numsetups = setupfile->numsetups; + u8 numitems = numsetups + 3; + u8 bank = data->list.value < 64 ? 0 : 1; + u8 bit = data->list.value - bank * 64; + u8 hasselection = (g_MpImportExportFilter[0] | g_MpImportExportFilter[1]) != 0; + u8 ofs; + s32 err; + u32 colour; + s32 index; + + switch (operation) { + case MENUOP_GETCOLOUR: + colour = data->list.unk04; + if (data->list.value == numitems - 1) { + data->list.unk04 = hasselection ? colour : (colour & 0xffffff00) | 0x66; + } + break; + + case MENUOP_GETOPTIONCOUNT: + data->list.value = numitems; + break; + + case MENUOP_GETOPTIONTEXT: + if (data->list.value < numsetups) { + return (uintptr_t) setupfile->setups[data->list.value].bytes; + } else { + // creates an offset to select the "Export" or "Import" label for the last item + ofs = (data->list.value == numitems - 1) ? op - 1 : 0; + return (intptr_t)labels[data->list.value - numsetups + ofs]; + } + + case MENUOP_SET: + if (data->list.value < setupfile->numsetups) { + g_MpImportExportFilter[bank] ^= (1 << bit); + } else { + index = data->list.value - setupfile->numsetups; + + switch (index) { + // Select All + case 0: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + break; + // Select None + case 1: + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = 0; + break; + // Export (or Import) + case 2: + if (hasselection) { + err = op == MPSETUP_OP_IMPORT ? mpsetupImportFile(0, false) : mpsetupExportFile(); + if (!err) { + menuPopDialog(); + if (op == MPSETUP_OP_IMPORT) { + // back to the 'Manage Settings' screen + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } else { + snprintf(g_StatusText, sizeof(g_StatusText), "File %s.bin\nwritten to the folder 'exported'\n", MPSETUP_FILENAME_EXP); + menuPushDialog(&g_StatusOkDialog); + } + } else { + if (err == MPSETUP_IMPORT_CONFLICT) { + menuPopDialog(); + menuPushDialog(&g_ImportOverrideDialog); + } else { + menuPushDialog(&g_StatusErrorDialog); + } + } + } + break; + } + } + break; + + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0x000fffff; + break; + + case MENUOP_GETLISTITEMCHECKBOX: + if (data->list.value < numsetups) { + data->list.unk04 = (g_MpImportExportFilter[bank] & (1 << bit)); + } + break; + } + + return 0; +} + +// Import: overwrite dialog +static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 err = mpsetupImportFile(item->param, true); + if (!err) { + menuPopDialog(); + menuPushDialog(&g_ManageSettingsDialog); + } else { + menuPushDialog(&g_StatusErrorDialog); + } + } + + return 0; +} + +// Manage Import/Export settings dialog +static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_SET: + if (item->param == MPSETUP_OP_IMPORT) { + strcpy(g_TitleImportExportDialog, "Import Settings\n"); + if (fsFileSize("$S/" MPSETUP_FILENAME_EXP ".bin") < 0) { + snprintf( + g_StatusText, sizeof(g_StatusText), + "No import file found.\n" + "Place the file %s.bin\n" + "Next to your %s.bin file\n", + MPSETUP_FILENAME_EXP, MPSETUP_FILENAME + ); + menuPushDialog(&g_StatusErrorDialog); + return 0; + } + + mpsetupLoadFile(&g_ImportMpSetupFile, MPSETUP_OP_IMPORT); + } else { + strcpy(g_TitleImportExportDialog, "Export Settings\n"); + } + g_Menus[g_MpPlayerNum].mpsetup.unke24 = item->param; + g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1; + menuPushDialog(&g_ImportExportDialog); + break; + } + + return 0; +} + +// Manage settings dialog +static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data) +{ + u32 colour; + switch (operation) { + case MENUOP_GETCOLOUR: + colour = data->list.unk04; + if (data->list.value + 1 == g_MpSetupFile.defaultsetup) { + data->list.unk04 = (colour & 0xffff0000) | 0xff; + } + break; + case MENUOP_GETOPTIONCOUNT: + data->list.value = g_MpSetupFile.numsetups; + break; + case MENUOP_GETOPTIONTEXT: + return (uintptr_t) g_MpSetupFile.setups[data->list.value].bytes; + case MENUOP_SET: + g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value; + if (data->list.value == g_MpSetupFile.defaultsetup - 1) { + strcpy(g_LabelSetDefault, "Clear Default\n"); + } else { + strcpy(g_LabelSetDefault, "Set Default\n"); + } + menuPushDialog(&g_ManageSetupDialog); + break; + case MENUOP_GETSELECTEDINDEX: + data->list.value = 0xfffff; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_RenameSetupDialog); + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + menuPushDialog(&g_DeleteSetupDialog); + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex; + // clicked on "clear default" + if (selected == g_MpSetupFile.defaultsetup - 1) { + g_MpSetupFile.defaultsetup = -1; + strcpy(g_LabelSetDefault, "Set Default\n"); + } else { + g_MpSetupFile.defaultsetup = selected + 1; + strcpy(g_LabelSetDefault, "Clear Default\n"); + } + + mpsetupSaveCurrentFile(); + } + + return 0; +} + +/* public functions */ + +s32 mpsetupLoadCurrentFile(void) +{ + return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT); +} + +s32 mpsetupSaveCurrentFile(void) +{ + g_MpSetupFile.version = MPSETUP_VERSION; + return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile); +} + +s32 mpsetupSaveSetup(s32 slotindex, u8 savefile) +{ + struct savebuffer setup; + + // 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 savefile ? mpsetupSaveCurrentFile() : 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_MpSetupFile.version); + g_MpCurrentSetup = index; +} + +void mpsetupCopyAllFromPak(void) +{ + 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(false); +} diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 4771fc28b..e077acb31 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -10,6 +10,7 @@ #include "game/menu.h" #include "game/gamefile.h" #include "game/player.h" +#include "lib/joy.h" #include "video.h" #include "input.h" #include "config.h" @@ -597,8 +598,10 @@ static MenuItemHandlerResult menuhandlerController(s32 operation, struct menuite if (data->dropdown.value == 0) { // unassign controller inputAssignController(g_ExtMenuPlayer, -1); + joyReset(); } else if (data->dropdown.value <= numCtrls) { inputAssignController(g_ExtMenuPlayer, ctrls[data->dropdown.value - 1]); + joyReset(); } break; case MENUOP_GETSELECTEDINDEX: 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/game_13c510.c b/src/game/artifact.c similarity index 67% rename from src/game/game_13c510.c rename to src/game/artifact.c index c2b4b81e9..390c161a6 100644 --- a/src/game/game_13c510.c +++ b/src/game/artifact.c @@ -7,7 +7,7 @@ #include "game/game_0b2150.h" #include "game/tex.h" #include "game/sky.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/bg.h" #include "game/stagetable.h" #include "game/room.h" @@ -23,6 +23,70 @@ #include "game/prop.h" #endif +/** + * Artifacts are points of interest in the z-buffer. + * + * They correspond to: + * - Individual circles in the lens flare effect from the sun. + * - Individual corners of each light fixture's box. + * + * The game needs to do line of sight checks to these points. The collision + * system would normally be used for line of sight checks, but it only works + * with basic geometric volumes and also doesn't take into account the player's + * gun. Instead, the checks are done by reading from the previous frame's + * z-buffer. + * + * Typically, the z-buffer would be read by the scheduler after a graphics frame + * is rendered, but this only works if the z-buffer is a complete image. + * PD draws the scene, then clears the z-buffer before drawing the player's gun, + * so the z-buffer at the end only contains the player's gun. To work around + * this, the GPU is given commands to read the z-buffer as a texture and copy + * depth values to a safe place before clearing it. Then once the frame is + * rendered, the scheduler compares the saved depths with the current z-buffer + * and updates the artifact, applying the minimum of the two depths. + * + * The scheduler maintains 3 arrays of artifacts, each referenced by indexes + * which rotate between them. + * - Artifacts are written by the main thread using the write index. + * The write and front indexes are then incremented. + * - The same artifacts are updated by the scheduler using the pending index. + * The pending index is then updated. + * - The artifacts are then rendered on a later frame using the front index. + * + * In each artifacts array, the first several slots are reserved for the sun + * flare artifacts. The quantity depends on the number of suns in the stage + * (Skedar Ruins has 3) and there are 8 artifacts per sun. The remaining slots + * are used for light fixture corners. The array is big enough to handle at + * least 90 lights on screen at a time. + * + * The initial state of the indexes are write=0, front=1, pending=0. + * These are set in sched_reset_artifacts. + * + * The detailed workflow is: + * - CPU: Light artifacts are determined and added to write artifacts (0) array + * - CPU: Constructs the gdl in this order: + * - Render scene + * - Copy relevant parts of z-buffer to write depths (0) array + * - Clear z-buffer and render gun + * - Render artifacts by reading from front artifacts (1) array + * - CPU: increments write (0 -> 1) and front (1 -> 2) indexes + * - GPU: Executes above task. g_ArtifactDepths0 now contains depths, and is + * pointed to by pending. + * - Scheduler: Reads the z-buffer, which at this point only contains the gun + * depth information, and updates the artifact with the minimum of the two + * - Scheduler: Increments pending (0 -> 1) + * + * It appears that the front index should be initialised to 2 instead, so that + * it's one frame behind the others rather than two frames behind. + * + * There's no doubt this went through several iterations before landing on this + * implementation. It's likely that this was implemented using a single and full + * z-buffer and it worked well until they decided to clear the z-buffer before + * rendering the gun. There is evidence in zbuf.c that they allocated a second + * z-buffer and swapped it. But when memory got too tight they had to change it + * to this texture read method. + */ + u8 *var800a41a0; u32 var800a41a4; u32 var800a41a8; @@ -44,11 +108,25 @@ void artifactsTick(void) schedIncrementFrontArtifacts(); } -u16 func0f13c574(f32 arg0) +u16 floatToN64Depth(f32 arg0) { + /** + * Method to convert a 32 bit floating point depth value to the + * unsigned 16 bit integer format used by the zbuffer on the N64. + * The argument arg0 represents z normalized to [0, 1] * 32704.0f. + * + * It works by converting the depth value to a large, unsigned 32 bit + * integer before scaling back down to an unsigned 16 bit integer. + * The scaling is done using bit shift operations on the most & least + * significant bytes of the resulting 16 bit integer, likely because + * bit shift operations are faster than division. The segmentation of + * this calculation at values 0x3f800, 0x3f000, etc. is probably + * intended to reduce z-fighting by adding more differentiation for + * distant z values. + */ u32 value = arg0 * 8.0f; - u32 left; - u32 right = value; + u32 left; // forms the most significant byte of the final u16 + u32 right = value; // forms the least significant byte of the final u16 if (value > 0x3f800) { right = value & 0x7ff; @@ -87,7 +165,7 @@ u16 func0f13c574(f32 arg0) return left << 13 | (right << 2); } -s32 func0f13c710(f32 arg0) +s32 artifactsFloatToInt(f32 arg0) { if (arg0 > 0.0f) { if (arg0 > 2147483520.0f) { @@ -232,8 +310,8 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) if (spdc[3] > 0.0001f) { f20 = 1.0f / spdc[3]; - x = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); - y = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); + x = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); + y = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; if (f0 < 32576.0f) { @@ -335,8 +413,8 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) f20 = -9999.0f; } - xi = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); - yi = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); + xi = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f)); + yi = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f)); f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f; if (g_ZbufPtr1 @@ -357,14 +435,20 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) if (index < MAX_ARTIFACTS) { #ifndef PLATFORM_N64 - artifact->unk02 = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); + artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); #endif - artifact->unk04 = func0f13c574(f0) >> 2; - artifact->unk08 = &g_ZbufPtr1[viGetWidth() * yi + xi]; + /** + * the original game performs artifact depth comparison + * using the N64 depth values divided by 4. This is + * accomplished by bit shifting the N64 depth value + * to the right using >> 2 + */ + artifact->expecteddepth = floatToN64Depth(f0) >> 2; + artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi]; artifact->light = &roomlights[i]; artifact->type = ARTIFACTTYPE_GLARE; - artifact->unk0c.u16_2 = xi; - artifact->unk0c.u16_1 = yi; + artifact->screenx = xi; + artifact->screeny = yi; } } } @@ -375,7 +459,7 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) } } -u8 func0f13d3c4(u8 arg0, u8 arg1) +u8 artifactsClamp(u8 arg0, u8 arg1) { if (arg1 >= arg0 + 7) { return arg0 + 7; @@ -423,18 +507,18 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) u16 min; u16 max; f32 lightop_cur_frac; - s32 t2; + s32 numgood; struct light *light; u8 *s3; s32 k; s32 count; - u16 t4; + u16 actualdepth; f32 add; s32 l; f32 brightness; - s32 avg; + s32 tolerance; f32 f0; - s32 v1; + s32 difference; s32 r; s32 g; s32 b; @@ -460,6 +544,12 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) struct light *light2 = artifacts[i].light; count = 0; + /** + * light arifacts are created from several, closely spaced + * textures that give the appearance of a dynamic light glare + * as the character moves. Loop to count all the sub-artifacts + * in this light. + */ for (j = i; j < MAX_ARTIFACTS && artifacts[j].type == ARTIFACTTYPE_GLARE && artifacts[j].light == light2; j++) { count++; } @@ -470,51 +560,65 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum) if (roomnum == light->roomnum) { lightindex = ((uintptr_t)light - (uintptr_t)g_BgLightsFileData) / sizeof(struct light); s3 = &var800a41a0[lightindex * 3]; - t2 = 0; + numgood = 0; min = 0xffff; max = 0; + /** + * loop to determine the min & max depth of + * the sub-artifacts composing this room light. + */ for (k = i; k < i + count; k++) { - if (artifacts[k].unk04 > max) { - max = artifacts[k].unk04; + if (artifacts[k].expecteddepth > max) { + max = artifacts[k].expecteddepth; } - if (artifacts[k].unk04 < min) { - min = artifacts[k].unk04; + if (artifacts[k].expecteddepth < min) { + min = artifacts[k].expecteddepth; } } - avg = (max - min) >> 1; + /** + * Define a depth tolerance from the min & max + * depths of sub-artifacts for a given light. + * This will be used to determine which light + * artifacts are visible when comparing depth + * values of lights to other items rendered + * on screen. Without this, the lights would + * constantly flicker due to z-fighting caused + * by the low precision of the N64 depth. + */ + tolerance = (max - min) >> 1; - if (avg < 25) { - avg = 25; + if (tolerance < 25) { + tolerance = 25; } for (k = i; k < i + count; k++) { #ifdef PLATFORM_N64 - u16 tmp; - t4 = (artifacts[k].unk02 & 0xfffc) >> 2; - tmp = artifacts[k].unk04; + u16 expecteddepth; + actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2; + expecteddepth = artifacts[k].expecteddepth; - if (tmp < t4) { - v1 = t4 - tmp; + if (expecteddepth < actualdepth) { + difference = actualdepth - expecteddepth; } else { - v1 = tmp - t4; + difference = expecteddepth - actualdepth; } - if (avg >= v1) { - t2++; + if (difference <= tolerance) { + numgood++; } #else - t2 += artifacts[k].unk02; + numgood += artifacts[k].visiblelos; #endif artifacts[k].type = ARTIFACTTYPE_FREE; } - s3[0] = func0f13d3c4(s3[0], t2 * 2); + s3[0] = artifactsClamp(s3[0], numgood * 2); - if (t2 > 0) { + if (numgood > 0) { brightness = viGetFovY() * 0.017453292f; add = cosf(brightness) / sinf(brightness) * 14.6f; diff --git a/src/game/bg.c b/src/game/bg.c index 2a3bab3dc..d1569bdc4 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -15,7 +15,7 @@ #include "game/sky.h" #include "game/stars.h" #include "game/dyntex.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/game_1531a0.h" #include "game/gfxmemory.h" #include "game/gfxreplace.h" diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 2f929cb35..7415bf851 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -8477,7 +8477,7 @@ glabel var7f1aca90 /* f0a71e4: 100002d5 */ b .L0f0a7d3c /* f0a71e8: 8fbf0034 */ lw $ra,0x34($sp) .L0f0a71ec: -/* f0a71ec: 0fc5d9ad */ jal zbufDrawArtifactsOffscreen +/* f0a71ec: 0fc5d9ad */ jal zbufSaveArtifactDepths /* f0a71f0: 8fa4014c */ lw $a0,0x14c($sp) /* f0a71f4: afa2014c */ sw $v0,0x14c($sp) /* f0a71f8: 0c002ca0 */ jal viPrepareZbuf @@ -9318,7 +9318,7 @@ glabel var7f1aca90 /* f0a71e4: 100002d5 */ b .L0f0a7d3c /* f0a71e8: 8fbf0034 */ lw $ra,0x34($sp) .L0f0a71ec: -/* f0a71ec: 0fc5d9ad */ jal zbufDrawArtifactsOffscreen +/* f0a71ec: 0fc5d9ad */ jal zbufSaveArtifactDepths /* f0a71f0: 8fa4014c */ lw $a0,0x14c($sp) /* f0a71f4: afa2014c */ sw $v0,0x14c($sp) /* f0a71f8: 0c002ca0 */ jal viPrepareZbuf @@ -10159,7 +10159,7 @@ glabel var7f1aca90 /* f0a4f30: 100002cd */ beqz $zero,.NB0f0a5a68 /* f0a4f34: 8fbf0034 */ lw $ra,0x34($sp) .NB0f0a4f38: -/* f0a4f38: 0fc5c4d5 */ jal zbufDrawArtifactsOffscreen +/* f0a4f38: 0fc5c4d5 */ jal zbufSaveArtifactDepths /* f0a4f3c: 8fa40144 */ lw $a0,0x144($sp) /* f0a4f40: afa20144 */ sw $v0,0x144($sp) /* f0a4f44: 0c002d00 */ jal viPrepareZbuf @@ -10957,7 +10957,7 @@ void bgunRender(Gfx **gdlptr) } #ifdef PLATFORM_N64 // TODO - gdl = zbufDrawArtifactsOffscreen(gdl); + gdl = zbufSaveArtifactDepths(gdl); #endif gdl = viPrepareZbuf(gdl); gdl = vi0000b1d0(gdl); 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/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/filemgr.c b/src/game/filemgr.c index e82d7af15..45b85efae 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: @@ -816,12 +811,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 +834,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: @@ -2780,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++) { @@ -3307,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, @@ -3347,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/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/lv.c b/src/game/lv.c index 56d9e9ab3..8db23f155 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -28,7 +28,7 @@ #include "game/portal.h" #include "game/fmb.h" #include "game/sky.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/game_1531a0.h" #include "game/zbuf.h" #include "game/challenge.h" diff --git a/src/game/menu.c b/src/game/menu.c index 1e9159cae..c9f4cca3a 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -147,6 +147,33 @@ s32 g_MouseDimmedMode = false; s32 g_MouseEndDeferredSlider = false; #endif +s32 menuAlt1Pressed(s32 playerNum) +{ + const s32 rshoulderKey = VK_JOY1_RSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LCTRL) || inputKeyJustPressed(rshoulderKey); + } + + return inputKeyJustPressed(rshoulderKey); +} + +s32 menuAlt2Pressed(s32 playerNum) +{ + const s32 lshoulderKey = VK_JOY1_LSHOULDER + playerNum * INPUT_MAX_CONTROLLER_BUTTONS; + + if (playerNum == 0) { + return inputKeyJustPressed(VK_LALT) || inputKeyJustPressed(lshoulderKey); + } + + return inputKeyJustPressed(lshoulderKey); +} + +s32 menuAltAnyPressed(s32 playerNum) +{ + return menuAlt1Pressed(playerNum) || menuAlt2Pressed(playerNum); +} + void menuPlaySound(s32 menusound) { s32 sound = -1; @@ -3581,8 +3608,8 @@ void func0f0f820c(struct menudialogdef *dialogdef, s32 root) g_MpPlayerNum = prevplayernum; - g_MenuData.unk008 = root; - g_MenuData.unk00c = dialogdef; + g_MenuData.prevmenuroot = root; + g_MenuData.prevmenudialog = dialogdef; } void menuSetBackground(s32 bg) @@ -3656,7 +3683,7 @@ void menuPushRootDialog(struct menudialogdef *dialogdef, s32 root) g_Menus[g_MpPlayerNum].unk820 = 1; g_MenuData.root = root; - g_MenuData.unk008 = -1; + g_MenuData.prevmenuroot = -1; g_MenuData.unk5d5_02 = false; if (root == MENUROOT_MAINMENU @@ -3978,13 +4005,13 @@ void menuReset(void) } g_MenuData.unk668 = -1; - g_MenuData.unk00c = 0; - g_MenuData.unk008 = -1; + g_MenuData.prevmenudialog = 0; + g_MenuData.prevmenuroot = -1; g_MenuData.count = 0; g_MenuData.root = 0; g_MenuData.unk010 = 0; g_MenuData.bg = 0; - g_MenuData.unk5d5_06 = false; + g_MenuData.isdialogopen = false; g_MenuData.nextbg = 255; g_MenuData.bannernum = -1; diff --git a/src/game/menuitem.c b/src/game/menuitem.c index 72c24573a..85fa18105 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 @@ -1520,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 @@ -1531,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; @@ -1608,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; @@ -1644,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/game/menutick.c b/src/game/menutick.c index 5a795da9d..671d33cc0 100644 --- a/src/game/menutick.c +++ b/src/game/menutick.c @@ -59,7 +59,7 @@ void menuTick(void) s32 i; s32 j; s32 k; - s32 sp344; + s32 isdialogopen; s32 sp340 = true; s32 anyopen = false; @@ -285,7 +285,7 @@ void menuTick(void) var8006294c = 1; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { - if (g_MenuData.unk008 == -1) { + if (g_MenuData.prevmenuroot == -1) { g_MpSetup.chrslots &= 0xfff0; } @@ -295,7 +295,7 @@ void menuTick(void) if (g_Menus[i].curdialog) { g_Menus[i].playernum = g_MpNumJoined++; - if (g_MenuData.unk008 == -1) { + if (g_MenuData.prevmenuroot == -1) { g_MpSetup.chrslots |= (1 << i); } } @@ -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) { @@ -486,31 +485,31 @@ void menuTick(void) } g_MpPlayerNum = 0; - sp344 = false; + isdialogopen = false; for (i = 0; i < ARRAYCOUNT(g_Menus); i++) { if (g_Menus[i].curdialog) { - sp344 = true; + isdialogopen = true; } } - if ((g_MenuData.unk5d5_06 || g_MenuData.unk008 != -1) && sp344 == false) { + if ((g_MenuData.isdialogopen || g_MenuData.prevmenuroot != -1) && isdialogopen == false) { if ((g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) - && g_MenuData.unk008 == -1) { + && g_MenuData.prevmenuroot == -1) { if (g_Vars.mpsetupmenu == MPSETUPMENU_GENERAL) { - g_MenuData.unk008 = MENUROOT_MAINMENU; - g_MenuData.unk00c = IS4MB() ? &g_CiMenuViaPauseMenuDialog : &g_CiMenuViaPcMenuDialog; + g_MenuData.prevmenuroot = MENUROOT_MAINMENU; + g_MenuData.prevmenudialog = IS4MB() ? &g_CiMenuViaPauseMenuDialog : &g_CiMenuViaPcMenuDialog; } else if (IS4MB()) { - g_MenuData.unk008 = MENUROOT_4MBMAINMENU; - g_MenuData.unk00c = &g_MainMenu4MbMenuDialog; + g_MenuData.prevmenuroot = MENUROOT_4MBMAINMENU; + g_MenuData.prevmenudialog = &g_MainMenu4MbMenuDialog; } else { - g_MenuData.unk008 = MENUROOT_MPSETUP; - g_MenuData.unk00c = &g_CombatSimulatorMenuDialog; + g_MenuData.prevmenuroot = MENUROOT_MPSETUP; + g_MenuData.prevmenudialog = &g_CombatSimulatorMenuDialog; } } - if (g_MenuData.unk008 != -1) { - if (g_MenuData.unk008 == -5) { + if (g_MenuData.prevmenuroot != -1) { + if (g_MenuData.prevmenuroot == -5) { // Match is beginning mpStartMatch(); menuStop(); @@ -519,7 +518,7 @@ void menuTick(void) bossfileSave(); g_Vars.modifiedfiles &= ~MODFILE_MPSETUP; } - } else if (g_MenuData.unk008 == -6) { + } else if (g_MenuData.prevmenuroot == -6) { // Match is ending s32 playernum = 0; @@ -542,17 +541,17 @@ void menuTick(void) setCurrentPlayerNum(playernum); endscreenPushCoop(); setCurrentPlayerNum(prevplayernum); - sp344 = true; + isdialogopen = true; } } else if (g_Vars.antiplayernum >= 0) { s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(playernum); endscreenPushAnti(); setCurrentPlayerNum(prevplayernum); - sp344 = true; + isdialogopen = true; } else { mpPushEndscreenDialog(playernum, i); - sp344 = true; + isdialogopen = true; if (g_PlayerConfigsArray[i].fileguid.fileid && g_PlayerConfigsArray[i].fileguid.deviceserial) { func0f0fd548(i); @@ -562,7 +561,7 @@ void menuTick(void) playernum++; } } - } else if (g_MenuData.unk008 == -7) { + } else if (g_MenuData.prevmenuroot == -7) { menuStop(); g_FileState = FILESTATE_CHANGINGAGENT; gamefileLoadDefaults(&g_GameFile); @@ -571,8 +570,8 @@ void menuTick(void) musicQueueStopAllEvent(); } else { bool startmusic = false; - menuPushRootDialog(g_MenuData.unk00c, g_MenuData.unk008); - sp344 = true; + menuPushRootDialog(g_MenuData.prevmenudialog, g_MenuData.prevmenuroot); + isdialogopen = true; if (g_MenuData.root == MENUROOT_MPSETUP || g_MenuData.root == MENUROOT_4MBMAINMENU) { startmusic = true; @@ -598,8 +597,8 @@ void menuTick(void) } } - g_MenuData.unk00c = NULL; - g_MenuData.unk008 = -1; + g_MenuData.prevmenudialog = NULL; + g_MenuData.prevmenuroot = -1; } else { switch (g_MenuData.root) { case MENUROOT_ENDSCREEN: @@ -751,5 +750,5 @@ void menuTick(void) } g_ScaleX = 1; - g_MenuData.unk5d5_06 = sp344 ? true : false; + g_MenuData.isdialogopen = isdialogopen ? true : false; } diff --git a/src/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 9d9c73b53..f3a8b20cd 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]; @@ -515,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) @@ -532,7 +529,7 @@ void func0f1881d4(s32 index) g_BotConfigsArray[index].difficulty = BOTDIFF_DISABLED; } -void mpInit(void) +void mpInit(bool resetplayers) { s32 i; s32 j; @@ -565,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++) { @@ -599,6 +598,16 @@ void mpInit(void) } g_MpSetup.chrslots = 0; + + 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 @@ -3532,7 +3541,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; @@ -3683,8 +3692,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); @@ -3717,8 +3724,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; } @@ -3912,21 +3917,54 @@ void mp0f18dec4(s32 slot) #endif } -void mpsetupfileLoadWad(struct savebuffer *buffer) +static u64 packWeaponSetRandomFilters() +{ + u64 packed = 0; + for (int i = 0; i < NUM_MPWEAPONS; ++i) { + packed |= g_MpWeaponSetRandomFilters[i] != 0 ? (1LL << i) : 0; + } + + return packed; +} + +static void unpackWeaponSetRandomFilters(u64 packed) +{ + for (int i = 0; i < NUM_MPWEAPONS; ++i) { + g_MpWeaponSetRandomFilters[i] = (packed & (1LL << i)) != 0; + } +} + +void mpsetupfileLoadWad(struct savebuffer *buffer, u8 version) { s32 i; s32 j; - savebufferReadString(buffer, g_MpSetup.name, false); + 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, 21); g_MpSetup.chrslots &= 0x000f; for (i = 0; i < MAX_BOTS; i++) { @@ -3947,13 +3985,20 @@ void mpsetupfileLoadWad(struct savebuffer *buffer) 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); } - func0f18913c(); + if (version > 0) { + u64 wpnRndPacked = savebufferReadBits(buffer, 64); + unpackWeaponSetRandomFilters(wpnRndPacked); + } + + g_MpWeaponSetNum = savebufferReadBits(buffer, 8); g_MpSetup.timelimit = savebufferReadBits(buffer, 6); g_MpSetup.scorelimit = savebufferReadBits(buffer, 7); @@ -3972,7 +4017,7 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) s32 mpbodynum; s32 i; - func0f0d55a4(buffer, g_MpSetup.name); + savebufferWriteString_ext(buffer, g_MpSetup.name, MPSETUP_MAXNAME + 1); for (i = 0; i < MAX_BOTS; i++) { if (g_MpSetup.chrslots & (1 << (i + 4))) { @@ -3986,7 +4031,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); @@ -4019,6 +4064,10 @@ void mpsetupfileSaveWad(struct savebuffer *buffer) savebufferOr(buffer, g_MpSetup.weapons[i], 7); } + u64 wpnRndPacked = packWeaponSetRandomFilters(); + savebufferOr(buffer, wpnRndPacked, 64); + savebufferOr(buffer, g_MpWeaponSetNum, 8); + savebufferOr(buffer, g_MpSetup.timelimit, 6); savebufferOr(buffer, g_MpSetup.scorelimit, 7); savebufferOr(buffer, g_MpSetup.teamscorelimit, 9); @@ -4032,69 +4081,15 @@ 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); *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..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, 8); + u8 sz = version > 0 ? 32 : 8; + savebufferReadBits(buffer, sz); } } @@ -438,7 +439,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 fbf8eb8ee..6c8f41aa6 100644 --- a/src/game/mplayer/scenarios/kingofthehill.inc +++ b/src/game/mplayer/scenarios/kingofthehill.inc @@ -149,14 +149,15 @@ struct menudialogdef g_KohOptionsMenuDialog = { &g_ExtGameOptionsMenuDialog, }; -void kohReadSave(struct savebuffer *buffer) +void kohReadSave(struct savebuffer *buffer, u8 version) { - g_Vars.mphilltime = savebufferReadBits(buffer, 8); + u8 sz = version > 0 ? 32 : 8; + g_Vars.mphilltime = savebufferReadBits(buffer, sz); } 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 1362c9527..0c138365e 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -20,9 +20,13 @@ #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[]; struct menudialogdef g_MpAddSimulantMenuDialog; @@ -31,6 +35,10 @@ struct menudialogdef g_MpChangeTeamNameMenuDialog; struct menudialogdef g_MpEditSimulantMenuDialog; struct menudialogdef g_MpSaveSetupNameMenuDialog; +extern struct menudialogdef g_ManageSettingsDialog; +extern struct menudialogdef g_FilemgrFileSavedMenuDialog; +extern struct menudialogdef g_FilemgrErrorMenuDialog; + #ifndef PLATFORM_N64 extern s32 g_MpWeaponSetNum; #endif @@ -451,9 +459,11 @@ 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; + s32 err; switch (operation) { case MENUOP_GETTEXT: @@ -463,7 +473,14 @@ MenuItemHandlerResult menuhandlerMpSetupName(s32 operation, struct menuitem *ite strcpy(g_MpSetup.name, name); break; case MENUOP_SET: - filemgrPushSelectLocationDialog(7, FILETYPE_MPSETUP); + err = mpsetupSaveSetup(g_MpSetupFile.numsetups, true); + if (!err) { + menuPushDialog(&g_FilemgrFileSavedMenuDialog); + g_MpCurrentSetup = g_MpSetupFile.numsetups - 1; + } + else { + menuPushDialog(&g_StatusErrorDialog); + } break; } @@ -474,7 +491,8 @@ MenuItemHandlerResult menuhandlerMpSaveSetupOverwrite(s32 operation, struct menu { if (operation == MENUOP_SET) { menuPopDialog(); - filemgrSaveOrLoad(&g_MpSetup.fileguid, FILEOP_SAVE_MPSETUP, 0); + mpsetupSaveSetup(g_MpCurrentSetup, true); + menuPushDialog(&g_FilemgrFileSavedMenuDialog); } return 0; @@ -1075,18 +1093,18 @@ 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, }, #endif { MENUITEMTYPE_KEYBOARD, + MPSETUP_MAXNAME, 0, 0, - 0, - 0, + 1, menuhandlerMpSetupName, }, { MENUITEMTYPE_END }, @@ -1112,10 +1130,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 +1141,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,36 +2258,31 @@ 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(); - - if (g_FileLists[1] != NULL) { - data->list.value += g_FileLists[1]->numfiles; - } + 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_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 - numpresets]; + func0f0d564c_ext(block->bytes, g_StringPointer, false, MPSETUP_MAXNAME+1); return (uintptr_t)g_StringPointer; } break; case MENUOP_SET: mpCloseDialogsForNewSetup(); - if (data->list.value < mpGetNumUnlockedPresets()) { + if (presets && 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 - numpresets); } if (item->param == 1) { @@ -2276,36 +2297,21 @@ 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 = 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" } - if (g_FileLists[1] != NULL) { - return (uintptr_t)filemgrGetDeviceNameOrStartIndex(1, operation, data->list.value - 1); - } - break; + return (uintptr_t)"Custom"; case MENUOP_GETGROUPSTARTINDEX: - if (data->list.value == 0) { - data->list.groupstartindex = 0; - } else { - data->list.groupstartindex = mpGetNumUnlockedPresets(); - - if (g_FileLists[1] != NULL) { - data->list.groupstartindex += filemgrGetDeviceNameOrStartIndex(1, operation, data->list.value - 1); - } - } + 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; } @@ -2315,21 +2321,21 @@ 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; 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++) { @@ -2558,6 +2564,16 @@ MenuDialogHandlerResult menudialogMpSimulant(s32 operation, struct menudialogdef return false; } +MenuDialogHandlerResult mpLoadSettingsDialogHandler(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data) +{ + if (operation == MENUOP_TICK) { + if (menuAltAnyPressed(g_MpPlayerNum)) { + u8 presets = g_Menus[g_MpPlayerNum].mpsetup.showpresets; + g_Menus[g_MpPlayerNum].mpsetup.showpresets = 1 - presets; + } + } +} + struct menuitem g_MpCharacterMenuItems[] = { { MENUITEMTYPE_LABEL, @@ -2633,6 +2649,22 @@ 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 }, }; @@ -2640,7 +2672,7 @@ struct menudialogdef g_MpLoadSettingsMenuDialog = { MENUDIALOGTYPE_DEFAULT, L_MPMENU_139, // "Load Game Settings" g_MpLoadSettingsMenuItems, - NULL, + mpLoadSettingsDialogHandler, MENUDIALOGFLAG_CLOSEONSELECT, NULL, }; @@ -5016,13 +5048,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) { + if (g_MpCurrentSetup < 0) { menuPushDialog(&g_MpSaveSetupNameMenuDialog); - } else { -#if VERSION >= VERSION_NTSC_1_0 - filemgrSetDevice1BySerial(g_MpSetup.fileguid.deviceserial); -#endif - + } + else { menuPushDialog(&g_MpSaveSetupExistsMenuDialog); } } @@ -5365,6 +5394,10 @@ 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 + mpsetupCopyAllFromPak(); + mpsetupLoadCurrentFile(); } if (g_Menus[g_MpPlayerNum].curdialog @@ -5757,6 +5790,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_ManageSettingsDialog, + }, { MENUITEMTYPE_SELECTABLE, 0, diff --git a/src/game/player.c b/src/game/player.c index a0983ef36..cd5fe4137 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -3667,15 +3667,15 @@ void playerTick(bool arg0) f32 mdx, mdy; inputMouseGetScaledDelta(&mdx, &mdy); if (mdx || mdy) { - mdx *= 48.f; - mdy *= 48.f; + mdx *= 0.022f; + mdy *= 0.022f; mdx = (mdx < -128.f) ? -128.f : (mdx > 127.f) ? 127.f : mdx; mdy = (mdy < -128.f) ? -128.f : (mdy > 127.f) ? 127.f : mdy; if (g_Vars.currentplayerstats && !optionsGetForwardPitch(g_Vars.currentplayerstats->mpindex)) { mdy = -mdy; } - sp178 += mdy * 0.00025f; - sp174 -= mdx * 0.00025f; + sp178 += mdy; + sp174 -= mdx; } } #endif diff --git a/src/game/savebuffer.c b/src/game/savebuffer.c index f9d4705fd..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,24 +470,17 @@ 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. */ -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 +502,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 savebufferWriteString_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 +530,22 @@ void func0f0d55a4(struct savebuffer *buffer, char *src) } } -void func0f0d564c(u8 *data, char *dst, bool addlinebreak) +void savebufferWriteString(struct savebuffer *buffer, char *src) +{ + savebufferWriteString_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/game/sky.c b/src/game/sky.c index e610cad83..f2b276f10 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -17,7 +17,7 @@ #ifndef PLATFORM_N64 #include "video.h" #include "game/gfxmemory.h" -#include "game/game_13c510.h" +#include "game/artifact.h" #include "game/player.h" #endif @@ -2518,11 +2518,11 @@ void skyCreateSunArtifact(struct artifact *artifact, s32 x, s32 y) sunpos.x = env->suns[i].pos[0]; sunpos.y = env->suns[i].pos[1]; sunpos.z = env->suns[i].pos[2]; - artifact->unk02 = artifactTestLos(&sunpos, &zero, x, y) * 0xfffc; + artifact->visiblelos = artifactTestLos(&sunpos, &zero, x, y) * 0xfffc; #endif - artifact->unk08 = &g_ZbufPtr1[(s32)camGetScreenWidth() * y + x]; - artifact->unk0c.u16_2 = x; - artifact->unk0c.u16_1 = y; + artifact->zbufptr = &g_ZbufPtr1[(s32)camGetScreenWidth() * y + x]; + artifact->screenx = x; + artifact->screeny = y; artifact->type = ARTIFACTTYPE_CIRCLE; } } @@ -2533,7 +2533,7 @@ f32 skyGetArtifactGroupIntensityFrac(struct artifact *artifacts) s32 i; for (i = 0; i < 8; i++) { - if (artifacts[i].type == ARTIFACTTYPE_CIRCLE && artifacts[i].unk02 == 0xfffc) { + if (artifacts[i].type == ARTIFACTTYPE_CIRCLE && artifacts[i].actualdepth == 0xfffc) { sum += 0.125f; } } diff --git a/src/game/zbuf.c b/src/game/zbuf.c index cf1b62140..d64f79f28 100644 --- a/src/game/zbuf.c +++ b/src/game/zbuf.c @@ -177,23 +177,31 @@ u16 *zbufGetArtifactsCfb(s32 index) return addr; } -Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) +/** + * This method is designed to save artifact depths + * prior to drawing the hands and weapon on-screen + * since drawing them requires clearing the zbuffer + * to avoid clipping the gun model on floors and walls. + * + * TODO: determine if this works with the PC port + */ +Gfx *zbufSaveArtifactDepths(Gfx *gdl) { struct artifact *artifacts = schedGetWriteArtifacts(); u32 stack; - u16 *sp4c = g_ZbufPtr1; - u32 s4 = 0; - u16 *sp44; - u16 *s2; - u16 *image; + u16 *zbuf = g_ZbufPtr1; + u32 numsamples = 0; + u16 *samples; + u16 *thissample; + u16 *zbufrow; s32 i; viGetBackBuffer(); - sp44 = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); + samples = zbufGetArtifactsCfb(g_SchedWriteArtifactsIndex); g_SchedSpecialArtifactIndexes[g_SchedWriteArtifactsIndex] = 1; gDPPipeSync(gdl++); - gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, viGetBufWidth(), OS_PHYSICAL_TO_K0(sp44)); + gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, viGetBufWidth(), OS_PHYSICAL_TO_K0(samples)); gDPSetScissor(gdl++, G_SC_NON_INTERLACE, 0, 0, SCREEN_320, SCREEN_240); gDPSetCycleType(gdl++, G_CYC_COPY); gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0x0000, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); @@ -220,24 +228,24 @@ Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) if (1); if (artifacts[i].type != ARTIFACTTYPE_FREE) { - s2 = &sp44[s4]; - image = &sp4c[artifacts[i].unk0c.u16_1 * viGetWidth()]; + thissample = &samples[numsamples]; + zbufrow = &zbuf[artifacts[i].screeny * viGetWidth()]; gDPPipeSync(gdl++); - gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_320, image); + gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_320, zbufrow); gDPLoadSync(gdl++); gDPLoadBlock(gdl++, 5, 0, 0, viGetWidth() - 1, 0); gDPPipeSync(gdl++); gSPTextureRectangle(gdl++, - s4 << 2, 0, - (s4 + 3) << 2, 0, - G_TX_RENDERTILE, (artifacts[i].unk0c.u16_2 * 32) + 16, 0x0010, 0x1000, 0); + numsamples << 2, 0, + (numsamples + 3) << 2, 0, + G_TX_RENDERTILE, (artifacts[i].screenx * 32) + 16, 0x0010, 0x1000, 0); - artifacts[i].unk0c.u16p = s2; - s4++; + artifacts[i].depthptr = thissample; + numsamples++; - if (s2); + if (thissample); } } @@ -251,7 +259,7 @@ Gfx *zbufDrawArtifactsOffscreen(Gfx *gdl) gDPSetTexturePersp(gdl++, G_TP_PERSP); gDPSetColorDither(gdl++, G_CD_BAYER); - if (sp44); + if (samples); return gdl; } diff --git a/src/include/constants.h b/src/include/constants.h index 8cfe58c73..5759a8eeb 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -4757,6 +4757,10 @@ enum weaponnum { #define EXTRA_SLEEP_TIME 1000LL // 100us +#define MPSETUP_MAXSETUPS 128 +#define MPSETUP_MAXNAME 17 +#define MPSETUP_BLOCKSIZE 80 + #endif #endif diff --git a/src/include/data.h b/src/include/data.h index 0bc0f12b3..f576e606b 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -562,6 +562,9 @@ extern s32 g_FileAutoSelect; extern u8 g_MpWeaponSetRandomFilters[NUM_MPWEAPONS]; extern s32 g_MpWeaponRandomFilterNum; +extern struct mpsetupfile g_MpSetupFile; +extern s16 g_MpCurrentSetup; + #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) diff --git a/src/include/game/game_13c510.h b/src/include/game/artifact.h similarity index 82% rename from src/include/game/game_13c510.h rename to src/include/game/artifact.h index d8ee2f6dd..9feae2a18 100644 --- a/src/include/game/game_13c510.h +++ b/src/include/game/artifact.h @@ -6,10 +6,10 @@ void artifactsClear(void); void artifactsTick(void); -u16 func0f13c574(f32 arg0); -s32 func0f13c710(f32 arg0); +u16 floatToN64Depth(f32 arg0); +s32 artifactsFloatToInt(f32 arg0); void artifactsCalculateGlaresForRoom(s32 roomnum); -u8 func0f13d3c4(u8 arg0, u8 arg1); +u8 artifactsClamp(u8 arg0, u8 arg1); Gfx *artifactsConfigureForGlares(Gfx *gdl); Gfx *artifactsUnconfigureForGlares(Gfx *gdl); Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum); diff --git a/src/include/game/menu.h b/src/include/game/menu.h index 419fb659e..37bdbc5e5 100644 --- a/src/include/game/menu.h +++ b/src/include/game/menu.h @@ -113,6 +113,10 @@ MenuItemHandlerResult menuhandlerRepairPak(s32 operation, struct menuitem *item, MenuItemHandlerResult menuhandlerRetrySavePak(s32 operation, struct menuitem *item, union handlerdata *data); MenuItemHandlerResult menuhandlerWarnRepairPak(s32 operation, struct menuitem *item, union handlerdata *data); +s32 menuAlt1Pressed(s32 playerNum); +s32 menuAlt2Pressed(s32 playerNum); +s32 menuAltAnyPressed(s32 playerNum); + extern const char var7f1b2658[]; extern const char var7f1b265c[]; extern const char var7f1b2660[]; diff --git a/src/include/game/mplayer/mplayer.h b/src/include/game/mplayer/mplayer.h index 3d2591ca7..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); @@ -118,11 +118,10 @@ 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); -s32 mpsetupfileLoad(s32 device, s32 filenum, u16 deviceserial); void func0f18e558(void); struct modeldef *func0f18e57c(s32 index, s32 *headnum); 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); diff --git a/src/include/game/savebuffer.h b/src/include/game/savebuffer.h index 9529248ce..28e4b6cb2 100644 --- a/src/include/game/savebuffer.h +++ b/src/include/game/savebuffer.h @@ -11,14 +11,16 @@ 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(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); void savebufferWriteGuid(struct savebuffer *buffer, struct fileguid *guid); diff --git a/src/include/types.h b/src/include/types.h index 1da61867b..5f8791717 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 @@ -3494,17 +3494,19 @@ struct audioconfig { struct artifact { u16 type; - u16 unk02; - u16 unk04; - u16 unk06; - u16 *unk08; + u16 actualdepth; + u16 expecteddepth; + u16 *zbufptr; +#ifndef PLATFORM_N64 + u16 visiblelos; +#endif union { - u16 *u16p; + u16 *depthptr; struct { - u16 u16_1; - u16 u16_2; + u16 screeny; + u16 screenx; }; - } unk0c; + }; struct light *light; }; @@ -3774,6 +3776,7 @@ struct menudata_main { struct menudata_mpsetup { u32 slotindex; u32 slotcount; + u8 showpresets; u32 unke24; }; @@ -3982,13 +3985,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 { @@ -4080,7 +4084,7 @@ struct missionconfig { }; struct mpsetup { - /*0x800acb88*/ char name[12]; + /*0x800acb88*/ char name[MPSETUP_MAXNAME+1]; /*0x800acb94*/ u32 options; /*0x800acb98*/ u8 scenario; /*0x800acb99*/ u8 stagenum; @@ -4783,8 +4787,8 @@ struct menudata_5d8 { struct menudata { /*0x000*/ s32 count; /*0x004*/ s32 root; - /*0x008*/ s32 unk008; // also a menuroot constant - /*0x00c*/ struct menudialogdef *unk00c; + /*0x008*/ s32 prevmenuroot; // also a menuroot constant + /*0x00c*/ struct menudialogdef *prevmenudialog; /*0x010*/ f32 unk010; /*0x014*/ u8 bg; /*0x015*/ u8 nextbg; @@ -4798,7 +4802,7 @@ struct menudata { /*0x5d5*/ u8 usezbuf : 1; /*0x5d5*/ u8 unk5d5_04 : 1; /*0x5d5*/ u8 unk5d5_05 : 1; - /*0x5d5*/ u8 unk5d5_06 : 1; + /*0x5d5*/ u8 isdialogopen : 1; /*0x5d5*/ u8 unk5d5_07 : 1; /*0x5d5*/ u8 unk5d5_08 : 1; /*0x5d8*/ struct menudata_5d8 unk5d8[12]; @@ -6156,6 +6160,17 @@ struct extplayerconfig { s32 usereloads; }; +struct setupblock { + u8 bytes[MPSETUP_BLOCKSIZE]; +}; + +struct mpsetupfile { + u8 version; + u8 defaultsetup; + u8 numsetups; + struct setupblock setups[MPSETUP_MAXSETUPS]; +}; + #endif #endif diff --git a/src/lib/sched.c b/src/lib/sched.c index 266e4cb68..b70906336 100644 --- a/src/lib/sched.c +++ b/src/lib/sched.c @@ -540,20 +540,20 @@ void schedUpdatePendingArtifacts(void) struct artifact *artifact = &artifacts[i]; if (artifact->type != ARTIFACTTYPE_FREE) { - u16 *unk08 = artifact->unk08; - u16 value08 = unk08[0]; + u16 *currdepthptr = artifact->zbufptr; + u16 currdepth = *currdepthptr; if (g_SchedSpecialArtifactIndexes[g_SchedPendingArtifactsIndex] == 1) { - u16 *unk0c = artifact->unk0c.u16p; - u16 value0c = unk0c[0]; + u16 *prevdepthptr = artifact->depthptr; + u16 prevdepth = *prevdepthptr; - if (value0c > value08) { - artifact->unk02 = value08; + if (currdepth < prevdepth) { + artifact->actualdepth = currdepth; } else { - artifact->unk02 = value0c; + artifact->actualdepth = prevdepth; } } else { - artifact->unk02 = value08; + artifact->actualdepth = currdepth; } } }