From 1f1b0d2d88e383ceffa5355048225f085c717762 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Sat, 28 Sep 2024 02:42:37 +0300 Subject: [PATCH 1/5] Convert some functions of Mission.cpp to std::fs::path --- Descent3/Mission.cpp | 301 +++++++++++++---------------- Descent3/Mission.h | 33 +++- Descent3/gamesequence.cpp | 5 +- Descent3/mission_download.cpp | 4 +- Descent3/multi_server.cpp | 2 +- lib/manage.h | 2 +- manage/manage.cpp | 2 +- netgames/dmfc/dmfcfunctions.cpp | 2 +- netgames/includes/gamedll_header.h | 2 +- 9 files changed, 165 insertions(+), 188 deletions(-) diff --git a/Descent3/Mission.cpp b/Descent3/Mission.cpp index 582a0082f..6a1b5293f 100644 --- a/Descent3/Mission.cpp +++ b/Descent3/Mission.cpp @@ -1,5 +1,5 @@ /* -* Descent 3 +* Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify @@ -649,7 +649,6 @@ #include "gamefont.h" #include "grdefs.h" #include "descent.h" -#include "ddio.h" #include "d3movie.h" #include "program.h" #include "ObjScript.h" @@ -672,51 +671,45 @@ #include "terrain.h" #include "multi.h" #include "hud.h" +#include "localization.h" +#include "levelgoal.h" + // --------------------------------------------------------------------------- // Data // --------------------------------------------------------------------------- // Info about the current level level_info Level_info; tMission Current_mission; -tLevelNode *Current_level = NULL; -char D3MissionsDir[_MAX_PATH]; +tLevelNode *Current_level = nullptr; +std::filesystem::path D3MissionsDir; extern int Times_game_restored; extern msn_urls Net_msn_URLs; // --------------------------------------------------------------------------- // Function prototypes // --------------------------------------------------------------------------- -bool InitMissionScript(); -void DoEndMission(); -void DoMissionMovie(const char *movie); + void FreeMission(); // used in load level callback void LoadLevelCB(const char *chunk, int curlen, int filelen); -// MN3 based mission functions. -// loads the msn file from the mn3 file specified, specifies the hog and table file. -bool mn3_Open(const char *mn3file); + +// MN3 based mission functions. + // returns mission information given the mn3 file. -bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn); -// closes the current mn3 file -void mn3_Close(); +bool mn3_GetInfo(const std::filesystem::path &mn3file, tMissionInfo *msn); -static inline bool IS_MN3_FILE(const char *fname) { - char name[PSFILENAME_LEN + 1]; - char ext[PSFILENAME_LEN + 1]; - ddio_SplitPath(fname, NULL, name, ext); - return (stricmp(ext, ".mn3") == 0) ? true : false; +static inline bool IS_MN3_FILE(const std::filesystem::path &fname) { + std::filesystem::path ext = fname.extension(); + return (stricmp(ext.u8string().c_str(), ".mn3") == 0); } -static inline char *MN3_TO_MSN_NAME(const char *mn3name, char *msnname) { - char fname[PSFILENAME_LEN + 1]; - ddio_SplitPath(mn3name, NULL, fname, NULL); - - if (stricmp(fname, "d3_2") == 0) { - strcpy(fname, "d3"); +static inline std::filesystem::path MN3_TO_MSN_NAME(const std::filesystem::path &mn3name) { + std::filesystem::path fname = std::filesystem::path(mn3name).stem(); + if (stricmp(fname.u8string().c_str(), "d3_2") == 0) { + fname = "d3"; } - strcat(fname, ".msn"); - strcpy(msnname, fname); - return msnname; + fname.replace_extension(".msn"); + return fname; } // --------------------------------------------------------------------------- @@ -735,13 +728,13 @@ void InitMission() { memset(Current_mission.author, 0, MSN_NAMELEN); memset(Current_mission.email, 0, MSN_URLLEN); memset(Current_mission.web, 0, MSN_URLLEN); - Current_mission.hog = NULL; - Current_mission.levels = NULL; - Current_mission.filename = NULL; + Current_mission.hog = nullptr; + Current_mission.levels = nullptr; + Current_mission.filename = nullptr; Current_mission.game_state_flags = 0; Current_mission.mn3_handle = 0; // create add on mission directory - ddio_MakePath(D3MissionsDir, LocalD3Dir, "missions", NULL); + D3MissionsDir = std::filesystem::path(LocalD3Dir) / "missions"; std::error_code ec; std::filesystem::create_directories(D3MissionsDir, ec); if (ec) { @@ -760,16 +753,17 @@ void ResetMission() { memset(Current_mission.author, 0, MSN_NAMELEN); memset(Current_mission.email, 0, MSN_URLLEN); memset(Current_mission.web, 0, MSN_URLLEN); - Current_mission.hog = NULL; - Current_mission.levels = NULL; - Current_mission.filename = NULL; + Current_mission.hog = nullptr; + Current_mission.levels = nullptr; + Current_mission.filename = nullptr; Current_mission.game_state_flags = 0; // clear out old URLs from memory - for (int a = 0; a < MAX_MISSION_URL_COUNT; a++) { - Net_msn_URLs.URL[a][0] = '\0'; + for (auto & a : Net_msn_URLs.URL) { + a[0] = '\0'; } } + #if (defined(OEM) || defined(DEMO)) bool DemoMission(int mode = 0) { tMission *msn = &Current_mission; @@ -881,7 +875,6 @@ bool DemoMission(int mode = 0) { bool LoadMission(const char *mssn) { Times_game_restored = 0; LOG_INFO << "In LoadMission()"; -// ShowProgressScreen(TXT_LOADINGLEVEL); #if (defined(OEM) || defined(DEMO)) #ifdef OEM if (stricmp(mssn, "d3oem.mn3") == 0) @@ -902,31 +895,29 @@ bool LoadMission(const char *mssn) { #endif #else - // #endif - tLevelNode *lvls = NULL; // Temporary storage for level data. + tLevelNode *lvls = nullptr; // Temporary storage for level data. tMission *msn; - CFILE *fp = NULL; // Mission file + CFILE *fp = nullptr; // Mission file char errtext[80]; // Stores error if unable to read mission - char msnfname[_MAX_PATH]; - char mission[_MAX_PATH]; + std::filesystem::path msnfname; + std::filesystem::path mission; int srclinenum = 0; // Current line of source. int curlvlnum; // Current level number int numlevels; // Number of levels required to read in. int cur_objective; // current objective reading. bool indesc; // are we in a multi-line block bool res = false; // used to specify if no error has occurred. - char pathname[_MAX_PATH]; + std::filesystem::path pathname; ResetMission(); // Reset everything. // open MN3 if filename passed was an mn3 file. if (IS_MN3_FILE(mssn)) { - strcpy(mission, mssn); - ddio_MakePath(pathname, "missions", mission, NULL); + mission = mssn; + pathname = std::filesystem::path("missions") / mission; } else { - strcpy(mission, mssn); - strcpy(pathname, mssn); - strcpy(msnfname, mssn); - // ddio_MakePath(pathname, D3MissionsDir, mission, NULL); + mission = mssn; + pathname = mssn; + msnfname = mssn; } if (IS_MN3_FILE(mission)) { @@ -934,7 +925,7 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_OPENMN3FAILED); goto msnfile_error; } - MN3_TO_MSN_NAME(mission, msnfname); + msnfname = MN3_TO_MSN_NAME(mission); } // open mission file fp = cfopen(msnfname, "rt"); @@ -948,7 +939,7 @@ bool LoadMission(const char *mssn) { srclinenum = 1; numlevels = -1; curlvlnum = 0; - indesc = 0; + indesc = false; cur_objective = -1; msn->multiplayable = true; msn->singleplayable = true; @@ -968,7 +959,7 @@ bool LoadMission(const char *mssn) { CleanupStr(command, srcline, sizeof(command)); CleanupStr(operand, srcline + strlen(command) + 1, sizeof(operand)); if (strlen(command) && indesc) - indesc = 0; + indesc = false; if (!stricmp(command, "NAME")) { strncpy(msn->name, operand, MSN_NAMELEN - 1); } else if (!stricmp(command, "MULTI")) { @@ -994,16 +985,16 @@ bool LoadMission(const char *mssn) { strcat(msn->desc, operand); if (indesc) strcat(msn->desc, "\n"); - indesc = 1; // this is a multiline command + indesc = true; // this is a multiline command } else if (!stricmp(command, "URL")) { if (curlvlnum != 0) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - for (int a = 0; a < MAX_MISSION_URL_COUNT; a++) { - if (Net_msn_URLs.URL[a][0] == '\0') { - strncpy(Net_msn_URLs.URL[a], operand, MAX_MISSION_URL_LEN - 1); - Net_msn_URLs.URL[a][MAX_MISSION_URL_LEN - 1] = '\0'; + for (auto & url : Net_msn_URLs.URL) { + if (url[0] == '\0') { + strncpy(url, operand, MAX_MISSION_URL_LEN - 1); + url[MAX_MISSION_URL_LEN - 1] = '\0'; LOG_INFO.printf("Found a Mission URL: %s", operand); break; } @@ -1173,11 +1164,12 @@ bool LoadMission(const char *mssn) { msn->cur_level = 1; msn->num_levels = numlevels; msn->levels = lvls; - msn->filename = mem_strdup(mission); + msn->filename = mem_strdup(mission.u8string().c_str()); msn->game_state_flags = 0; - strcpy(Net_msn_URLs.msnname, mission); - res = true; // everthing is ok. -// if error, print it out, else end. + strcpy(Net_msn_URLs.msnname, mission.u8string().c_str()); + res = true; // everything is ok. + + // if error, print it out, else end. msnfile_error: if (!res) { char str_text[128]; @@ -1203,6 +1195,7 @@ bool LoadMission(const char *mssn) { #endif return false; } + void FreeMission() { // Free up mission script int i; //,j; @@ -1229,33 +1222,27 @@ void FreeMission() { mem_free(Current_mission.levels[i].progress); } mem_free(Current_mission.levels); - Current_mission.levels = NULL; + Current_mission.levels = nullptr; } - //@@ if (Current_mission.d3xmod) { - //@@ D3XFreeProgram(Current_mission.d3xmod); - //@@ Current_mission.d3xmod = NULL; - //@@ } + if (Current_mission.hog) mem_free(Current_mission.hog); if (Current_mission.filename) { - // these DON't USE mem_free since we use _strdup, which doesn't use our memory routines. mem_free(Current_mission.filename); - Current_mission.filename = NULL; + Current_mission.filename = nullptr; } - Current_mission.hog = NULL; - Current_level = NULL; + Current_mission.hog = nullptr; + Current_level = nullptr; } -#include "localization.h" -#include "levelgoal.h" + // Load the text (goal strings) for a level -void LoadLevelText(const char *level_filename) { - char pathname[_MAX_FNAME], filename[_MAX_FNAME]; +void LoadLevelText(const std::filesystem::path &level_filename) { int n_strings; - ddio_SplitPath(level_filename, pathname, filename, NULL); - strcat(pathname, filename); - strcat(pathname, ".str"); + std::filesystem::path pathname = level_filename; + pathname.replace_extension(".str"); + char **goal_strings; - if (CreateStringTable(pathname, &goal_strings, &n_strings)) { + if (CreateStringTable(pathname.u8string().c_str(), &goal_strings, &n_strings)) { int n_goals = Level_goals.GetNumGoals(); ASSERT(n_strings == (n_goals * 3)); for (int i = 0; i < n_goals; i++) { @@ -1272,7 +1259,7 @@ void LoadLevelText(const char *level_filename) { bool LoadMissionLevel(int level) { Hud_show_controls = false; LoadLevelProgress(LOAD_PROGRESS_START, 0); - if (!LoadLevel(Current_mission.levels[level - 1].filename, NULL)) { + if (!LoadLevel(Current_mission.levels[level - 1].filename, nullptr)) { char buf[128]; snprintf(buf, sizeof(buf), TXT_MSNERROR, Current_mission.levels[level - 1].filename); SetUICallback(DEFAULT_UICALLBACK); @@ -1307,7 +1294,7 @@ bool LoadMissionLevel(int level) { #define LOADBAR_W (260 * (float)Max_window_w / (float)FIXED_SCREEN_WIDTH) #define LOADBAR_H (22 * (float)Max_window_h / (float)FIXED_SCREEN_HEIGHT) #define N_LOAD_MSGS 12 -bool started_page = 0; + /* $$TABLE_GAMEFILE "tunnelload.ogf" */ @@ -1338,7 +1325,7 @@ void LoadLevelProgress(int step, float percent, const char *chunk) { lvl_percent_loaded = 0.0f; pag_percent_loaded = 0.0f; dedicated_last_string_len = -1; - const char *p = NULL; + const char *p = nullptr; if ((!(Game_mode & GM_MULTI)) && (!Current_mission.levels[Current_mission.cur_level - 1].progress)) { p = "tunnelload.ogf"; } else { @@ -1352,12 +1339,6 @@ void LoadLevelProgress(int step, float percent, const char *chunk) { n_text_msgs = 0; } } - /* - else - { - ShowProgressScreen (TXT_LOADINGLEVEL); - } - */ Progress_screen_loaded = true; return; } break; @@ -1582,17 +1563,20 @@ void LoadLevelProgress(int step, float percent, const char *chunk) { EndFrame(); rend_Flip(); } -/* this functions performs the end mission code + +/** + * This function performs the end mission code */ void DoEndMission() { - if (Game_mode & GM_MULTI) // If multiplayer, just loop - { + // If multiplayer, just loop + if (Game_mode & GM_MULTI) { if (Dedicated_server) PrintDedicatedMessage(TXT_DS_MISSIONDONE); SetCurrentLevel(1); return; } } + // Shows some text on a background, useful for telling the player what is going on // ie "Loading level...", "Receiving data...", etc void ShowProgressScreen(const char *str, const char *str2, bool flip) { @@ -1615,6 +1599,7 @@ void ShowProgressScreen(const char *str, const char *str2, bool flip) { if (flip) rend_Flip(); } + /* does a mission briefing, returns if mission was canceled, a false, or 0 value. first displays mission goals and some warnings or advice. may allow for selection of player ships @@ -1678,49 +1663,41 @@ bool InitMissionScript() { //@@ } return true; } + extern bool IsRestoredGame; + void InitLevelScript() { if (Current_level->filename) { - char filename[_MAX_PATH], ext[_MAX_EXT]; - ddio_SplitPath(Current_level->filename, NULL, filename, ext); -#if defined(WIN32) - strcat(filename, ".dll"); -#elif defined(MACOSX) - strcat(filename, ".dylib"); -#elif defined(__LINUX__) - strcat(filename, ".so"); -#else - #error Unsupported platform! -#endif + std::filesystem::path filename = Current_level->filename; + filename.replace_extension(MODULE_EXT); Osiris_LoadLevelModule(filename); } - //@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELSTART, 0, 0); - tOSIRISEventInfo ei; + tOSIRISEventInfo ei{}; // This is a hack... we don't want the level start script to be called when restoring a save game if (!IsRestoredGame) Osiris_CallLevelEvent(EVT_LEVELSTART, &ei); AssignScriptsForLevel(); // initialize all scripts for level. } + void FreeLevelScript() { Osiris_UnloadLevelModule(); if (Current_level) { // free level's script and thread //@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELEND, 0, 0); - tOSIRISEventInfo ei; + tOSIRISEventInfo ei{}; Osiris_CallLevelEvent(EVT_LEVELEND, &ei); } } -// return information about a mission -bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) { - CFILE *fp; + +bool GetMissionInfo(const std::filesystem::path &msnfile, tMissionInfo *msn) { bool indesc = false; // are we in a multi-line block // open mission file if (IS_MN3_FILE(msnfile)) { return mn3_GetInfo(msnfile, msn); } - fp = cfopen(msnfile, "rt"); + CFILE *fp = cfopen(msnfile, "rt"); if (!fp) { - LOG_WARNING.printf("Failed to open mission file %s in GetMissionInfo.", msnfile); + LOG_WARNING.printf("Failed to open mission file %s in GetMissionInfo.", msnfile.u8string().c_str()); return false; } msn->multi = true; @@ -1736,8 +1713,7 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) { int readcount; // read-in count readcount = cf_ReadString(srcline, sizeof(srcline), fp); if (readcount) { - // we have a line of source. parse out primary keyword - // then parse out remainder. + // We have a line of source. Parse out primary keyword then parse out remainder. keyword = strtok(srcline, " \t"); CleanupStr(command, srcline, sizeof(command)); CleanupStr(operand, srcline + strlen(command) + 1, sizeof(operand)); @@ -1756,14 +1732,13 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) { } else if (!stricmp(command, "AUTHOR")) { strncpy(msn->author, operand, MSN_NAMELEN - 1); } else if (!stricmp(command, "DESCRIPTION") || indesc) { - // multi-line descriptions require the strcat. the initial - // strings should be empty for this to work. + // Multi-line descriptions require the strcat. The initial strings should be empty for this to work. strcat(msn->desc, operand); if (indesc) strcat(msn->desc, "\n"); indesc = true; // this is a multiline command } else if (!stricmp(command, "NUMLEVELS")) { - // get number of levels + // Get number of levels int value = atoi(operand); msn->n_levels = value; } else if (!stricmp(command, "LEVEL")) { @@ -1777,9 +1752,9 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) { cfclose(fp); return true; } -// --------------------------------------------------------------------------- + const char *GetMissionName(const char *mission) { - tMissionInfo msninfo; + tMissionInfo msninfo{}; static char msnname[MSN_NAMELEN]; msnname[0] = 0; if (GetMissionInfo(mission, &msninfo)) { @@ -1789,38 +1764,23 @@ const char *GetMissionName(const char *mission) { } return msnname; } + bool IsMissionMultiPlayable(const char *mission) { - tMissionInfo msninfo; + tMissionInfo msninfo{}; if (GetMissionInfo(mission, &msninfo)) { return msninfo.multi; } return false; } -bool IsMissionSinglePlayable(const char *mission) { - tMissionInfo msninfo; - if (GetMissionInfo(mission, &msninfo)) { - return msninfo.single; - } - return false; -} + int Mission_voice_hog_handle = 0; -// MN3 based mission functions. -// loads the msn file from the mn3 file specified, specifies the hog and table file. -bool mn3_Open(const char *mn3file) { - char pathname[_MAX_PATH]; - char filename[PSFILENAME_LEN + 1]; - char ext[PSFILENAME_LEN]; + +bool mn3_Open(const std::filesystem::path &mn3file) { int mn3_handle; - // concatanate the mn3 extension if it isn't there. - char tempMn3File[_MAX_PATH]; - if (!IS_MN3_FILE(mn3file)) { - strncpy(tempMn3File, mn3file, sizeof(tempMn3File) - 1); - tempMn3File[sizeof(tempMn3File) - 1] = 0; - strcat(tempMn3File, ".mn3"); - mn3file = tempMn3File; - } + // concatenate the mn3 extension if it isn't there. + std::filesystem::path tempMn3File = mn3file; + tempMn3File.replace_extension(".mn3"); - ddio_MakePath(pathname, D3MissionsDir, mn3file, NULL); // open MN3 HOG. mn3_handle = cf_OpenLibrary(mn3file); if (mn3_handle == 0) { @@ -1829,42 +1789,43 @@ bool mn3_Open(const char *mn3file) { Osiris_ExtractScriptsFromHog(mn3_handle, true); } // do table file stuff. - ddio_SplitPath(mn3file, NULL, filename, ext); + std::filesystem::path filename = mn3file.stem(); - char voice_hog[_MAX_PATH*2]; - if ((stricmp(filename, "d3") == 0) || (stricmp(filename, "training") == 0)) { + std::filesystem::path voice_hog; + if ((stricmp(filename.u8string().c_str(), "d3") == 0) || (stricmp(filename.u8string().c_str(), "training") == 0)) { // Open audio hog file - ddio_MakePath(voice_hog, "missions", "d3voice1.hog", nullptr); // Audio for levels 1-4 + voice_hog = std::filesystem::path("missions") / "d3voice1.hog"; // Audio for levels 1-4 Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); - } else if (stricmp(filename, "d3_2") == 0) { + } else if (stricmp(filename.u8string().c_str(), "d3_2") == 0) { // Open audio hog file - ddio_MakePath(voice_hog, "missions", "d3voice2.hog", nullptr); // Audio for levels 5-17 + voice_hog = std::filesystem::path("missions") / "d3voice2.hog"; // Audio for levels 5-17 Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); } - strcat(filename, ".gam"); - mng_SetAddonTable(filename); + filename.replace_extension(".gam"); + mng_SetAddonTable(filename.u8string().c_str()); Current_mission.mn3_handle = mn3_handle; return true; } // returns mission information given the mn3 file. -bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn) { +bool mn3_GetInfo(const std::filesystem::path &mn3file, tMissionInfo *msn) { int handle; bool retval; - char pathname[_MAX_PATH]; - char filename[PSFILENAME_LEN + 1]; + std::filesystem::path pathname; + std::filesystem::path filename; - ddio_MakePath(pathname, "missions", mn3file, nullptr); + pathname = std::filesystem::path(std::filesystem::path("missions")) / mn3file; handle = cf_OpenLibrary(pathname); if (handle == 0) { LOG_ERROR << "MISSION: MN3 failed to open."; return false; } - MN3_TO_MSN_NAME(mn3file, filename); + filename = MN3_TO_MSN_NAME(mn3file); retval = GetMissionInfo(filename, msn); cf_CloseLibrary(handle); return retval; } + // closes the current mn3 file void mn3_Close() { if (Current_mission.mn3_handle) { @@ -1877,12 +1838,14 @@ void mn3_Close() { } Current_mission.mn3_handle = 0; } + #define KEYWORD_LEN 16 #define NUM_KEYWORDS 16 #define GOALSTEXT "GOALS" #define GOALSTEXTLEN strlen(GOALSTEXT) #define MODMINGOALS "MINGOALS" #define MODMINGOALSLEN strlen(MODMINGOALS) + // Returns the max number of teams this mission can support for this mod, or //-1 if this level shouldn't be played with this mission // Return values: @@ -1894,7 +1857,6 @@ int MissionGetKeywords(const char *mission, char *keywords) { char msn_keywords[NUM_KEYWORDS][KEYWORD_LEN]; char mod_keywords[NUM_KEYWORDS][KEYWORD_LEN]; - int i; char *parse_keys = mem_strdup(keywords); char seps[] = ","; int teams = MAX_NET_PLAYERS; @@ -1903,7 +1865,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { int mod_key_count = 0; int msn_key_count = 0; bool goal_per_team = false; - tMissionInfo msn_info; + tMissionInfo msn_info{}; memset(msn_keywords, 0, sizeof(msn_keywords)); memset(mod_keywords, 0, sizeof(mod_keywords)); @@ -1922,7 +1884,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { do { strcpy(mod_keywords[mod_key_count], tokp); mod_key_count++; - tokp = strtok(NULL, seps); + tokp = strtok(nullptr, seps); } while ((tokp) && (mod_key_count < NUM_KEYWORDS)); } // Break up the msn keywords into an array @@ -1931,38 +1893,38 @@ int MissionGetKeywords(const char *mission, char *keywords) { do { strcpy(msn_keywords[msn_key_count], tokp); msn_key_count++; - tokp = strtok(NULL, seps); + tokp = strtok(nullptr, seps); } while ((tokp) && (msn_key_count < NUM_KEYWORDS)); } - for (i = 0; i < NUM_KEYWORDS; i++) { - if (msn_keywords[i][0] == 0) { + for (auto & msn_keyword : msn_keywords) { + if (msn_keyword[0] == 0) { continue; } - if (strnicmp(GOALSTEXT, msn_keywords[i], GOALSTEXTLEN) == 0) { + if (strnicmp(GOALSTEXT, msn_keyword, GOALSTEXTLEN) == 0) { // Get the number of goals this game has - goals = atoi(msn_keywords[i] + GOALSTEXTLEN); + goals = atoi(msn_keyword + GOALSTEXTLEN); } } mem_free(parse_keys); // Loop through looking for matches - for (i = 0; i < NUM_KEYWORDS; i++) { - if (mod_keywords[i][0] == 0) { + for (auto & mod_keyword : mod_keywords) { + if (mod_keyword[0] == 0) { continue; } - if (strnicmp(mod_keywords[i], MODMINGOALS, MODMINGOALSLEN) == 0) { - goalsneeded = atoi(mod_keywords[i] + MODMINGOALSLEN); - } else if (stricmp("GOALPERTEAM", mod_keywords[i]) == 0) { + if (strnicmp(mod_keyword, MODMINGOALS, MODMINGOALSLEN) == 0) { + goalsneeded = atoi(mod_keyword + MODMINGOALSLEN); + } else if (stricmp("GOALPERTEAM", mod_keyword) == 0) { goal_per_team = true; } else { bool found_keyword = false; // Loop through looking for matches - for (int a = 0; a < NUM_KEYWORDS; a++) { - if (msn_keywords[a][0] == 0) { + for (auto & msn_keyword : msn_keywords) { + if (msn_keyword[0] == 0) { continue; } - if (stricmp(msn_keywords[a], mod_keywords[i]) == 0) { + if (stricmp(msn_keyword, mod_keyword) == 0) { // Woohoo! it's found found_keyword = true; break; @@ -1970,7 +1932,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { } // We never found one we needed, so return -1; if (!found_keyword) { - LOG_WARNING.printf("%s keyword needed in %s not found!", mod_keywords[i], mission); + LOG_WARNING.printf("%s keyword needed in %s not found!", mod_keyword, mission); return -1; } } @@ -1988,6 +1950,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { } return teams; } + ////////////////////////////////////////////////////////////////////////////// #ifdef EDITOR // Used by editor->game to load all necessary elements for level playing for systems diff --git a/Descent3/Mission.h b/Descent3/Mission.h index 6701ea9ec..54af70de3 100644 --- a/Descent3/Mission.h +++ b/Descent3/Mission.h @@ -178,10 +178,10 @@ #define LOAD_PROGRESS_DONE 200 // Load level progress worker -void LoadLevelProgress(int step, float percent, const char *chunk = NULL); +void LoadLevelProgress(int step, float percent, const char *chunk = nullptr); -// array constants -const int MSN_FILENAMELEN = _MAX_PATH, MSN_URLLEN = 256; +// Array constants +const int MSN_URLLEN = 256; #define MAX_KEYWORDLEN 300 @@ -254,7 +254,7 @@ struct tMission { tLevelNode *levels; // array of levels }; -// structyre used to get information about a mission +// structure used to get information about a mission struct tMissionInfo { char name[MSN_NAMELEN]; char author[MSN_NAMELEN]; @@ -272,7 +272,19 @@ struct tMissionInfo { extern tMission Current_mission; extern tLevelNode *Current_level; -extern char D3MissionsDir[]; +extern std::filesystem::path D3MissionsDir; + +/** + * Loads the mn3 file, specifies the hog and table file. + * @param mn3file file for loading + * @return true on success + */ +bool mn3_Open(const std::filesystem::path &mn3file); + +/** + * Closes the current mn3 file + */ +void mn3_Close(); // Get the name field out of the mission file const char *GetMissionName(const char *mission); @@ -308,13 +320,18 @@ bool LoadMissionLevel(int level); bool InitMissionScript(); // Shows text on a background -void ShowProgressScreen(const char *str, const char *str2 = NULL, bool flip = true); +void ShowProgressScreen(const char *str, const char *str2 = nullptr, bool flip = true); // See if a mission file is multiplayer playable. bool IsMissionMultiPlayable(const char *mission); -// return information about a mission -bool GetMissionInfo(const char *msnfile, tMissionInfo *msn); +/** + * Fill information about a mission. + * @param msnfile mission file + * @param msn mission info to be filled + * @return true on success + */ +bool GetMissionInfo(const std::filesystem::path &msnfile, tMissionInfo *msn); // Returns the max number of teams this mission can support for this mod, or //-1 if this level shouldn't be played with this mission diff --git a/Descent3/gamesequence.cpp b/Descent3/gamesequence.cpp index fd8a26598..a8925b6d6 100644 --- a/Descent3/gamesequence.cpp +++ b/Descent3/gamesequence.cpp @@ -1138,9 +1138,6 @@ extern matrix editor_player_orient; extern int editor_player_roomnum; #endif -extern bool mn3_Open(const char *mn3file); -extern void mn3_Close(); - extern bool Game_gauge_do_time_test; extern char Game_gauge_usefile[_MAX_PATH]; @@ -1455,7 +1452,7 @@ void DeleteAmbientObjects() { void Localization_SetLanguage(int type); int Localization_GetLanguage(void); -void LoadLevelText(const char *level_filename); +void LoadLevelText(const std::filesystem::path &level_filename); // Starts the level, which has already been loaded void StartLevel() { diff --git a/Descent3/mission_download.cpp b/Descent3/mission_download.cpp index 3b5293932..bdfd594dd 100644 --- a/Descent3/mission_download.cpp +++ b/Descent3/mission_download.cpp @@ -602,8 +602,8 @@ int msn_CheckGetMission(network_address *net_addr, char *filename) { return 1; #else // Don't download local missions - char pathname[_MAX_PATH]; - ddio_MakePath(pathname, D3MissionsDir, filename, NULL); + std::filesystem::path pathname; + pathname = D3MissionsDir / filename; if (cfexist(filename) || cfexist(pathname)) { return 1; } diff --git a/Descent3/multi_server.cpp b/Descent3/multi_server.cpp index ddbabf158..7235e15d7 100644 --- a/Descent3/multi_server.cpp +++ b/Descent3/multi_server.cpp @@ -826,7 +826,7 @@ void MultiStartServer(int playing, char *scriptname, int dedicated_server_num_te //-1 Not compatible! //>=0 Number of teams supported for this mod & level -#define SCRIPTBADFORMISSION -1 +#define SCRIPTBADFORMISSION (-1) int CheckMissionForScript(char *mission, char *script, int dedicated_server_num_teams) { char mod_keys[MAX_KEYWORDLEN]; diff --git a/lib/manage.h b/lib/manage.h index 6bb4beda0..3659e8b3b 100644 --- a/lib/manage.h +++ b/lib/manage.h @@ -89,7 +89,7 @@ void mng_ClearAddonTables(); // Push the given table file as an addon table file // returns true on success -bool mng_SetAddonTable(char *name); +bool mng_SetAddonTable(const char *name); // Pushes an addon pack onto the stack so we can keep track of it void mng_PushAddonPage(int pagetype, char *name, int overlay); diff --git a/manage/manage.cpp b/manage/manage.cpp index b5429d334..97736b03d 100644 --- a/manage/manage.cpp +++ b/manage/manage.cpp @@ -2729,7 +2729,7 @@ void mng_ClearAddonTables() { } // Push the given table file as an addon table file // returns true on success -bool mng_SetAddonTable(char *name) { +bool mng_SetAddonTable(const char *name) { ASSERT(Num_addon_tables < MAX_ADDON_TABLES); if (Num_addon_tables >= MAX_ADDON_TABLES) return false; diff --git a/netgames/dmfc/dmfcfunctions.cpp b/netgames/dmfc/dmfcfunctions.cpp index b9abbb0f1..6489f5f29 100644 --- a/netgames/dmfc/dmfcfunctions.cpp +++ b/netgames/dmfc/dmfcfunctions.cpp @@ -325,7 +325,7 @@ void (*DLLCheckBoxSetCheck)(void *cb, bool state); bool (*DLLCheckBoxIsChecked)(void *cb); uint32_t (*DLLnw_GetHostAddressFromNumbers)(char *str); void (*TableFilesClear)(void); -bool (*TableFileAdd)(char *filename); +bool (*TableFileAdd)(const char *filename); void (*DLLDebugBreak_callback_stop)(void); void (*DLLDebugBreak_callback_resume)(void); void (*DLLInt3MessageBox)(const char *file, const char *line); diff --git a/netgames/includes/gamedll_header.h b/netgames/includes/gamedll_header.h index 29c032f2c..d04a4c7f5 100644 --- a/netgames/includes/gamedll_header.h +++ b/netgames/includes/gamedll_header.h @@ -928,7 +928,7 @@ typedef void (*TableFilesClear_fp)(void); DMFCDLLOUT(TableFilesClear_fp TableFilesClear;) // Adds a table file into the manage system for add-on data -typedef bool (*TableFileAdd_fp)(char *filename); +typedef bool (*TableFileAdd_fp)(const char *filename); DMFCDLLOUT(TableFileAdd_fp TableFileAdd;) // Debugger interaction From 80dc4135f625c66d2114a242dd99572bd47a8c38 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Sat, 28 Sep 2024 11:31:16 +0300 Subject: [PATCH 2/5] Minor cleanups to menu.cpp --- Descent3/menu.cpp | 175 ++++++++++++++++------------------------------ Descent3/menu.h | 3 +- 2 files changed, 64 insertions(+), 114 deletions(-) diff --git a/Descent3/menu.cpp b/Descent3/menu.cpp index 417cd3800..a6d0dbd42 100644 --- a/Descent3/menu.cpp +++ b/Descent3/menu.cpp @@ -654,6 +654,8 @@ #include #include +#include +#include #include "log.h" #include "menu.h" @@ -661,7 +663,6 @@ #include "game.h" #include "Mission.h" #include "multi_ui.h" -#include "ctlconfig.h" #include "config.h" #include "gamesave.h" #include "gamesequence.h" @@ -698,7 +699,8 @@ bool Directplay_lobby_launched_game = false; #define IDV_LOADLEVEL 20 #define IDV_OK 1 #define IDV_CANCEL 2 -bool MenuLoadLevel(void); + +bool MenuLoadLevel(); #endif // for command line joining of games bool Auto_connected = false; @@ -742,7 +744,7 @@ int MainMenu() { main_menu.AddItem(IDV_CREDITS, KEY_C, TXT_MENUCREDITS); main_menu.AddItem(IDV_QUIT, KEY_Q, TXT_MENUQUIT, MM_ENDMENU_TYPE); #ifdef _DEBUG - main_menu.AddItem(0, 0, NULL); + main_menu.AddItem(0, 0, nullptr); main_menu.AddItem(IDV_LOADLEVEL, 0, "Load Level"); #endif // page in ui data. @@ -781,7 +783,7 @@ int MainMenu() { Demo_restart = false; SetGameMode(GM_NORMAL); SetFunctionMode(LOADDEMO_MODE); - exit_menu = 1; + exit_menu = true; continue; } else if (MultiDLLGameStarting) { // Go back into the multiplayer DLL @ the game list @@ -789,32 +791,12 @@ int MainMenu() { LOG_DEBUG << "Returning to Multiplayer!"; if (ReturnMultiplayerGameMenu()) { - exit_menu = 1; + exit_menu = true; SetFunctionMode(GAME_MODE); continue; } } - /* - else - { - if(FirstGame) - { - //MenuScene(); - //ui_ShowCursor(); - //Descent->defer(); - //DoUIFrame(); - //rend_Flip(); - //GetUIFrameResult(); - res = IDV_NEWGAME; - } - else { - main_menu.SetMusicRegion(MM_MUSIC_REGION); - res = main_menu.DoUI(); - } - } - if (FirstGame) - res = IDV_NEWGAME; - */ + res = FirstGame ? IDV_NEWGAME : -1; if (res == -1) { main_menu.SetMusicRegion(MM_MUSIC_REGION); @@ -836,9 +818,9 @@ int MainMenu() { break; case IDV_QUIT: if (DoMessageBox(TXT_MENUQUIT, TXT_QUITMESSAGE, MSGBOX_YESNO)) { - exit_game = 1; - exit_menu = 1; - Mem_quick_exit = 1; // tell the mem library to not free up each chunk individually + exit_game = true; + exit_menu = true; + Mem_quick_exit = true; // tell the mem library to not free up each chunk individually } break; case IDV_LOADGAME: @@ -864,12 +846,12 @@ int MainMenu() { LOG_DEBUG << "Multiplayer!"; // make all ships available LOG_DEBUG << "Making all ships available"; - for (int i = 0; i < MAX_SHIPS; i++) { - if (Ships[i].used) - PlayerSetShipPermission(-1, Ships[i].name, true); + for (auto & Ship : Ships) { + if (Ship.used) + PlayerSetShipPermission(-1, Ship.name, true); } if (MainMultiplayerMenu()) { - exit_menu = 1; + exit_menu = true; SetFunctionMode(GAME_MODE); } } break; @@ -877,12 +859,12 @@ int MainMenu() { if (LoadDemoDialog()) { SetGameMode(GM_NORMAL); SetFunctionMode(LOADDEMO_MODE); - exit_menu = 1; + exit_menu = true; } break; case IDV_CREDITS: SetFunctionMode(CREDITS_MODE); - exit_menu = 1; + exit_menu = true; /* #if defined(DEMO) //extern void ShowStaticScreen(char *bitmap_filename); @@ -901,7 +883,7 @@ int MainMenu() { case IDV_LOADLEVEL: { if (MenuLoadLevel()) { ShowProgressScreen(TXT_LOADINGLEVEL); - exit_menu = 1; + exit_menu = true; } } break; #endif @@ -940,11 +922,10 @@ bool ProcessCommandLine() { #endif tokp = strtok(p, "/"); if (stricmp(tokp, "ip") == 0) { - tokp = strtok(NULL, "/"); + tokp = strtok(nullptr, "/"); Auto_login_port[0] = '\0'; strcpy(Auto_login_addr, tokp); - // char seldll[_MAX_PATH*2]; - // ddio_MakePath(seldll,Base_directory,"online","Direct TCP~IP Game.d3c",NULL); + if (LoadMultiDLL("Direct TCP~IP")) { CallMultiDLL(MT_AUTO_LOGIN); if (MultiDLLGameStarting) { @@ -957,11 +938,10 @@ bool ProcessCommandLine() { LOG_WARNING << "Couldn't load DLL."; } } else if (stricmp(tokp, "pxo") == 0) { - tokp = strtok(NULL, "/"); + tokp = strtok(nullptr, "/"); Auto_login_port[0] = '\0'; strcpy(Auto_login_addr, tokp); - // char seldll[_MAX_PATH*2]; - // ddio_MakePath(seldll,Base_directory,"online","parallax online.d3c",NULL); + if (LoadMultiDLL("parallax online")) { CallMultiDLL(MT_AUTO_LOGIN); if (MultiDLLGameStarting) { @@ -1042,6 +1022,7 @@ bool ProcessCommandLine() { SetUICallback(DEFAULT_UICALLBACK); return exit_menu ? true : false; } + ////////////////////////////////////////////////////////////////////////////// // Start New Game #define MSNDLG_WIDTH 512 @@ -1090,25 +1071,23 @@ static inline int count_missions(const std::vector &missi return c; } -static inline int generate_mission_listbox(newuiListBox *lb, int n_maxfiles, char **filelist, +static inline int generate_mission_listbox(newuiListBox *lb, std::vector &filelist, const std::vector &missions_directories) { int c = 0; for (const auto &missions_directory : missions_directories) { ddio_DoForeachFile( - missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &n_maxfiles, &filelist](const std::filesystem::path &path) { + missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &filelist](const std::filesystem::path &path) { tMissionInfo msninfo{}; - if (c < n_maxfiles) { - if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0) - return; - if (GetMissionInfo(path.filename().u8string().c_str(), &msninfo) && msninfo.name[0] && msninfo.single) { - filelist[c] = mem_strdup(path.filename().u8string().c_str()); - lb->AddItem(msninfo.name); - c++; - if (!(c % 2)) - DoWaitMessage(true); - } - } - }); + if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0) + return; + if (GetMissionInfo(path, &msninfo) && msninfo.name[0] && msninfo.single) { + filelist.push_back(path.filename()); + lb->AddItem(msninfo.name); + c++; + if (!(c % 2)) + DoWaitMessage(true); + } + }); } return c; @@ -1121,8 +1100,8 @@ bool MenuNewGame() { newuiTiledWindow menu; newuiSheet *select_sheet; newuiListBox *msn_lb; - char **filelist = nullptr; - int n_missions, i, res; + std::vector filelist; + int n_missions, res; bool found = false; bool do_menu = true, load_mission = false, retval = true; #ifdef DEMO @@ -1174,59 +1153,41 @@ bool MenuNewGame() { menu.Create(TXT_MENUNEWGAME, 0, 0, 448, 384); select_sheet = menu.GetSheet(); - select_sheet->NewGroup(NULL, 10, 0); + select_sheet->NewGroup(nullptr, 10, 0); msn_lb = select_sheet->AddListBox(352, 256, UID_MSNLB); - select_sheet->NewGroup(NULL, 160, 280, NEWUI_ALIGN_HORIZ); + select_sheet->NewGroup(nullptr, 160, 280, NEWUI_ALIGN_HORIZ); select_sheet->AddButton(TXT_OK, UID_OK); select_sheet->AddButton(TXT_CANCEL, UID_CANCEL); #ifndef OEM select_sheet->AddButton(TXT_MSNINFO, UID_MSNINFO); #endif #ifndef OEM - // add mission names to listbox - // count valid mission files. - // add a please wait dialog here. - auto missions_directories = cf_LocateMultiplePaths("missions"); - n_missions = 0; - n_missions += count_missions(missions_directories); - if (n_missions) { - // allocate extra mission slot because of check below which adds a name to the filelist. - filelist = (char **)mem_malloc(sizeof(char *) * (n_missions + 1)); - for (i = 0; i < (n_missions + 1); i++) - filelist[i] = NULL; - } else { + // generate real listbox now. + n_missions = generate_mission_listbox(msn_lb, filelist, D3MissionsDir); + + if (n_missions == 0) { DoMessageBox(TXT_ERROR, TXT_NOMISSIONS, MSGBOX_OK); - retval = false; DoWaitMessage(false); - goto missions_fail; + return false; } - // generate real listbox now. - generate_mission_listbox(msn_lb, n_missions, filelist, missions_directories); - // #ifdef RELEASE - int k; - for (k = 0; k < n_missions; k++) { - if (!filelist[k]) - continue; - if (stricmp(filelist[k], "d3.mn3") == 0) { + for (auto const &mission : filelist) { + if (stricmp(mission.u8string().c_str(), "d3.mn3") == 0) { found = true; break; } } if (!found) { - filelist[n_missions] = mem_strdup("d3.mn3"); + filelist.emplace_back("d3.mn3"); msn_lb->AddItem(TXT_MAINMISSION); n_missions++; } -// #endif #else #define OEM_MISSION_NAME "Descent 3: Sol Ascent" -#define OEM_TRAINING_NAME "Pilot Training " +#define OEM_TRAINING_NAME "Pilot Training" n_missions = 2; - filelist = mem_rmalloc(2); - filelist[0] = mem_strdup(OEM_MISSION_FILE); - ; + filelist.emplace_back(OEM_MISSION_NAME); msn_lb->AddItem(OEM_MISSION_NAME); - filelist[1] = mem_strdup(OEM_TRAINING_FILE); + filelist.emplace_back(OEM_TRAINING_NAME); msn_lb->AddItem(OEM_TRAINING_NAME); #endif DoWaitMessage(false); @@ -1238,14 +1199,14 @@ bool MenuNewGame() { res = menu.DoUI(); #ifndef OEM if (res == UID_MSNINFO) { - tMissionInfo msninfo; + tMissionInfo msninfo{}; int index = msn_lb->GetCurrentIndex(); if (index >= 0 && index < n_missions) { if (GetMissionInfo(filelist[index], &msninfo)) { if (msninfo.name[0]) { newuiTiledWindow infownd; newuiSheet *sheet; - infownd.Create(NULL, 0, 0, 384, 192); + infownd.Create(nullptr, 0, 0, 384, 192); infownd.Open(); sheet = infownd.GetSheet(); sheet->NewGroup(TXT_MSNNAME, 0, 0); @@ -1260,7 +1221,7 @@ bool MenuNewGame() { sheet->AddText(msninfo.desc); else sheet->AddText(TXT_NONE); - sheet->NewGroup(NULL, 240, 118); + sheet->NewGroup(nullptr, 240, 118); sheet->AddButton(TXT_OK, UID_OK); infownd.DoUI(); infownd.Close(); @@ -1277,15 +1238,11 @@ bool MenuNewGame() { retval = false; } else if (res == UID_OK || res == UID_MSNLB) { int index = msn_lb->GetCurrentIndex(); - char *nameptr = NULL; - if (index >= 0 && index < n_missions) { + std::filesystem::path nameptr; + if (index >= 0 && index < filelist.size()) { nameptr = filelist[index]; } -#ifndef OEM - if (!nameptr || !LoadMission(nameptr)) { -#else - if (!LoadMission(nameptr)) { -#endif + if (nameptr.empty() || !LoadMission(nameptr.u8string().c_str())) { DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK); retval = false; } else { @@ -1300,8 +1257,7 @@ bool MenuNewGame() { highest = std::min(highest + 1, Current_mission.num_levels); #endif if (highest > 1) { - int start_level; - start_level = DisplayLevelWarpDlg(highest); + int start_level = DisplayLevelWarpDlg(highest); if (start_level == -1) { goto redo_newgame_menu; } else { @@ -1317,19 +1273,11 @@ bool MenuNewGame() { } } menu.Destroy(); -missions_fail: - // free all memory - for (i = 0; i < n_missions; i++) { - if (filelist[i]) { - mem_free(filelist[i]); - } - } - if (filelist) { - mem_free(filelist); - } + return retval; #endif } + // DisplayLevelWarpDlg // pass in the max level allowed to be chosen, if -1, than all levels are allowed (a.k.a level warp cheat) int DisplayLevelWarpDlg(int max_level) { @@ -1352,7 +1300,7 @@ int DisplayLevelWarpDlg(int max_level) { snprintf(buffer, sizeof(buffer), TXT_LEVELWARPB, Current_mission.num_levels); } sheet->NewGroup(buffer, 0, 0); - input_text = sheet->AddEditBox(NULL, 4, 64, IDV_QUIT, UIED_NUMBERS); + input_text = sheet->AddEditBox(nullptr, 4, 64, IDV_QUIT, UIED_NUMBERS); sprintf(input_text, "%d", chosen_level); redo_level_choose: hwnd.Open(); @@ -1371,9 +1319,10 @@ int DisplayLevelWarpDlg(int max_level) { hwnd.Destroy(); return chosen_level; } + #ifdef _DEBUG // Loads a level and starts the game -bool MenuLoadLevel(void) { +bool MenuLoadLevel() { std::filesystem::path buffer; if (DoPathFileDialog(false, buffer, "Load Level", {"*.d3l"}, PFDF_FILEMUSTEXIST)) { SimpleStartLevel(buffer); diff --git a/Descent3/menu.h b/Descent3/menu.h index 29e378753..0692f9ca4 100644 --- a/Descent3/menu.h +++ b/Descent3/menu.h @@ -55,7 +55,8 @@ #ifndef MENU_H #define MENU_H -#include "pstypes.h" +#include + #include "grdefs.h" #define MAX_MENUS 5 From 52b340237400e5630f33008959bac629acd9de21 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Sun, 29 Sep 2024 01:55:37 +0300 Subject: [PATCH 3/5] Implementing LoadLevelInfo() This function may be used for fast information accessing such as level name or description. --- Descent3/LoadLevel.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++ Descent3/LoadLevel.h | 10 ++++++ 2 files changed, 90 insertions(+) diff --git a/Descent3/LoadLevel.cpp b/Descent3/LoadLevel.cpp index 411e8ed2d..939ffd145 100644 --- a/Descent3/LoadLevel.cpp +++ b/Descent3/LoadLevel.cpp @@ -3588,6 +3588,86 @@ static inline char* GetCurrentSumString() { return output_buf; } +bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info) { + CFILE *ifile = cfopen(filename, "rb"); + bool found = false; + + if (!ifile) { + LOG_ERROR.printf("Failed to open mission file %s", filename.u8string().c_str()); + return false; + } + + try { + // catch cfile errors + + // Read & check tag + char tag[4]; + cf_ReadBytes((uint8_t *)tag, 4, ifile); + if (strncmp(tag, LEVEL_FILE_TAG, 4)) { + LOG_ERROR.printf("%s is not a level file (tag %c%c%c%c)", + filename.u8string().c_str(), tag[0], tag[1], tag[2], tag[3]); + cfclose(ifile); + return false; + } + + // Read & check version number + int version = cf_ReadInt(ifile); + + // Check for too-new version + if (version > LEVEL_FILE_VERSION) { + LOG_ERROR.printf("Mission file %s too new (version %d)", filename.u8string().c_str(), version); + cfclose(ifile); + return false; + } + if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) { + LOG_ERROR.printf("Mission file %s too old (version %d)", filename.u8string().c_str(), version); + cfclose(ifile); + return false; + } + + + // Init level info + strcpy(info.name, "Unnamed"); + strcpy(info.designer, "Anonymous"); + strcpy(info.copyright, ""); + strcpy(info.notes, ""); + + + while (!cfeof(ifile)) { + char chunk_name[4]; + + cf_ReadBytes((uint8_t *)chunk_name, 4, ifile); + chunk_start = cftell(ifile); + chunk_size = cf_ReadInt(ifile); + + if (ISCHUNK(CHUNK_LEVEL_INFO)) { + cf_ReadString(info.name, sizeof(info.name), ifile); + + // Localize level name here... + strcpy(info.name, LocalizeLevelName(info.name)); + + cf_ReadString(info.designer, sizeof(info.designer), ifile); + cf_ReadString(info.copyright, sizeof(info.copyright), ifile); + cf_ReadString(info.notes, sizeof(info.notes), ifile); + found = true; + break; + } else { + cfseek(ifile, chunk_start + chunk_size, SEEK_SET); + } + } + + } catch (cfile_error *cfe) { + LOG_FATAL.printf("Error reading: file = <%s>, error = \"%s\"", cfe->file->name, cfe->msg); + ASSERT(cfe->read_write == CFE_READING); + cfclose(ifile); + return false; + } + + cfclose(ifile); + return found; +} + + extern bool Disable_editor_rendering; #define LEVEL_LOADED_PCT_CALC (filelen) ? (float)(chunk_size + chunk_start) / (float)filelen : 0.0f diff --git a/Descent3/LoadLevel.h b/Descent3/LoadLevel.h index 054b5af44..c4f67728f 100644 --- a/Descent3/LoadLevel.h +++ b/Descent3/LoadLevel.h @@ -444,6 +444,7 @@ */ #include "cfile.h" +#include "Mission.h" #include "room.h" // Chunk types @@ -634,6 +635,15 @@ // 130->131 Added the powerups ignore gravity checkbox // 131->132 Added another ff bounce texture for Dan (his last day wish) +/** + * Get information from level such as level name, author copyrights and notes. + * This function much lighter that LoadLevel() and gets only INFO chunk from mission. + * @param filename name of mission file + * @param info resulted info + * @return true on success + */ +bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info); + // Load a level file // Returns 1 if file read ok, else 0 // cb_fn returns current chunk, parm1 = # bytes in chunk, parm2 = number of bytes in file From b8ff1b4a2d5221a57e32960263e81b7d6b654adb Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Sun, 29 Sep 2024 01:58:06 +0300 Subject: [PATCH 4/5] Reworking of DisplayLevelWarpDlg() Now level loading screen shows level name, and user can choose level from list instead of manually entering level number. --- Descent3/Mission.cpp | 257 +++++++++++++++++++------------------- Descent3/Mission.h | 16 +-- Descent3/gamesequence.cpp | 2 - Descent3/menu.cpp | 91 +++++++++----- 4 files changed, 197 insertions(+), 169 deletions(-) diff --git a/Descent3/Mission.cpp b/Descent3/Mission.cpp index 6a1b5293f..f10a5b051 100644 --- a/Descent3/Mission.cpp +++ b/Descent3/Mission.cpp @@ -729,7 +729,6 @@ void InitMission() { memset(Current_mission.email, 0, MSN_URLLEN); memset(Current_mission.web, 0, MSN_URLLEN); Current_mission.hog = nullptr; - Current_mission.levels = nullptr; Current_mission.filename = nullptr; Current_mission.game_state_flags = 0; Current_mission.mn3_handle = 0; @@ -754,12 +753,12 @@ void ResetMission() { memset(Current_mission.email, 0, MSN_URLLEN); memset(Current_mission.web, 0, MSN_URLLEN); Current_mission.hog = nullptr; - Current_mission.levels = nullptr; + Current_mission.levels.clear(); Current_mission.filename = nullptr; Current_mission.game_state_flags = 0; // clear out old URLs from memory - for (auto & a : Net_msn_URLs.URL) { + for (auto &a : Net_msn_URLs.URL) { a[0] = '\0'; } } @@ -767,20 +766,18 @@ void ResetMission() { #if (defined(OEM) || defined(DEMO)) bool DemoMission(int mode = 0) { tMission *msn = &Current_mission; - tLevelNode *lvls = mem_rmalloc(5); msn->cur_level = 1; msn->num_levels = 1; - msn->levels = lvls; + msn->levels.reserve(5); msn->multiplayable = true; msn->singleplayable = true; - memset(lvls, 0, sizeof(tLevelNode) * msn->num_levels); strncpy(msn->author, "Outrage", MSN_NAMELEN - 1); - if (!mode) { + switch (mode) { + case 0: { strcpy(msn->name, "Descent 3: Sol Ascent"); msn->num_levels = 5; - memset(lvls, 0, sizeof(tLevelNode) * msn->num_levels); #ifdef DEMO msn->multiplayable = false; strcpy(msn->name, "Descent 3: Demo2"); @@ -791,82 +788,91 @@ bool DemoMission(int mode = 0) { msn->num_levels = 5; msn->filename = mem_strdup("d3oem.mn3"); #endif - // strncpy(lvls[0].name, "Search for Sweitzer", MSN_NAMELEN-1); - lvls[0].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; - lvls[0].filename = mem_strdup("level1.d3l"); - lvls[0].briefname = mem_strdup("level1.brf"); - lvls[0].score = mem_strdup("level1.omf"); - lvls[0].progress = mem_strdup("l1load.ogf"); - lvls[0].moviename = mem_strdup("level1.mve"); + // strncpy(msn->levels[0].name, "Search for Sweitzer", MSN_NAMELEN-1); + msn->levels[0].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; + msn->levels[0].filename = mem_strdup("level1.d3l"); + msn->levels[0].briefname = mem_strdup("level1.brf"); + msn->levels[0].score = mem_strdup("level1.omf"); + msn->levels[0].progress = mem_strdup("l1load.ogf"); + msn->levels[0].moviename = mem_strdup("level1.mve"); #ifndef DEMO - // strncpy(lvls[0].name, "Into the Heart of the Ship", MSN_NAMELEN-1); - lvls[1].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; - lvls[1].filename = mem_strdup("level3.d3l"); - lvls[1].briefname = mem_strdup("level2o.brf"); - lvls[1].score = mem_strdup("level3.omf"); - lvls[1].progress = mem_strdup("l3load.ogf"); - // strncpy(lvls[0].name, "The Nomad Caves", MSN_NAMELEN-1); - lvls[2].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; - lvls[2].filename = mem_strdup("level6.d3l"); - lvls[2].briefname = mem_strdup("level3o.brf"); - lvls[2].score = mem_strdup("level6.omf"); - lvls[2].progress = mem_strdup("l6load.ogf"); - // strncpy(lvls[0].name, "The Transmode Virus", MSN_NAMELEN-1); - lvls[3].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; - lvls[3].filename = mem_strdup("level7.d3l"); - lvls[3].briefname = mem_strdup("level4o.brf"); - lvls[3].score = mem_strdup("level7.omf"); - lvls[3].progress = mem_strdup("l7load.ogf"); - // strncpy(lvls[0].name, "The Rescue", MSN_NAMELEN-1); - lvls[4].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; - lvls[4].filename = mem_strdup("level11.d3l"); - lvls[4].briefname = mem_strdup("level5o.brf"); - lvls[4].score = mem_strdup("level11.omf"); - lvls[4].progress = mem_strdup("l11load.ogf"); + // strncpy(msn->levels[0].name, "Into the Heart of the Ship", MSN_NAMELEN-1); + msn->levels[1].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; + msn->levels[1].filename = mem_strdup("level3.d3l"); + msn->levels[1].briefname = mem_strdup("level2o.brf"); + msn->levels[1].score = mem_strdup("level3.omf"); + msn->levels[1].progress = mem_strdup("l3load.ogf"); + // strncpy(msn->levels[0].name, "The Nomad Caves", MSN_NAMELEN-1); + msn->levels[2].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; + msn->levels[2].filename = mem_strdup("level6.d3l"); + msn->levels[2].briefname = mem_strdup("level3o.brf"); + msn->levels[2].score = mem_strdup("level6.omf"); + msn->levels[2].progress = mem_strdup("l6load.ogf"); + // strncpy(msn->levels[0].name, "The Transmode Virus", MSN_NAMELEN-1); + msn->levels[3].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; + msn->levels[3].filename = mem_strdup("level7.d3l"); + msn->levels[3].briefname = mem_strdup("level4o.brf"); + msn->levels[3].score = mem_strdup("level7.omf"); + msn->levels[3].progress = mem_strdup("l7load.ogf"); + // strncpy(msn->levels[0].name, "The Rescue", MSN_NAMELEN-1); + msn->levels[4].flags = LVLFLAG_BRIEFING | LVLFLAG_SCORE; + msn->levels[4].filename = mem_strdup("level11.d3l"); + msn->levels[4].briefname = mem_strdup("level5o.brf"); + msn->levels[4].score = mem_strdup("level11.omf"); + msn->levels[4].progress = mem_strdup("l11load.ogf"); mn3_Open("d3oem.mn3"); #endif - + break; } - - else if (mode == 1) { + case 1: { strcpy(msn->name, "Polaris"); msn->filename = mem_strdup("Polaris.d3l"); - // strncpy(lvls[0].name, "Polaris", MSN_NAMELEN-1); - lvls[0].filename = mem_strdup("polaris.d3l"); - lvls[0].flags |= LVLFLAG_BRANCH; - lvls[0].lvlbranch0 = 1; - lvls[0].progress = mem_strdup("polaris.ogf"); - } else if (mode == 2) { + // strncpy(msn->levels[0].name, "Polaris", MSN_NAMELEN-1); + msn->levels[0].filename = mem_strdup("polaris.d3l"); + msn->levels[0].flags |= LVLFLAG_BRANCH; + msn->levels[0].lvlbranch0 = 1; + msn->levels[0].progress = mem_strdup("polaris.ogf"); + break; + } + case 2: { strcpy(msn->name, "The Core"); msn->filename = mem_strdup("TheCore.d3l"); - // strncpy(lvls[0].name, "The Core", MSN_NAMELEN-1); - lvls[0].filename = mem_strdup("thecore.d3l"); - lvls[0].flags |= LVLFLAG_BRANCH; - lvls[0].lvlbranch0 = 1; - lvls[0].progress = mem_strdup("thecore.ogf"); - } else if (mode == 3) { + // strncpy(msn->levels[0].name, "The Core", MSN_NAMELEN-1); + msn->levels[0].filename = mem_strdup("thecore.d3l"); + msn->levels[0].flags |= LVLFLAG_BRANCH; + msn->levels[0].lvlbranch0 = 1; + msn->levels[0].progress = mem_strdup("thecore.ogf"); + break; + } + case 3: { strcpy(msn->name, "Taurus"); msn->filename = mem_strdup("Taurus.d3l"); - // strncpy(lvls[0].name, "Taurus", MSN_NAMELEN-1); - lvls[0].filename = mem_strdup("taurus.d3l"); - lvls[0].flags |= LVLFLAG_BRANCH; - lvls[0].lvlbranch0 = 1; - lvls[0].progress = mem_strdup("taurus.ogf"); + // strncpy(msn->levels[0].name, "Taurus", MSN_NAMELEN-1); + msn->levels[0].filename = mem_strdup("taurus.d3l"); + msn->levels[0].flags |= LVLFLAG_BRANCH; + msn->levels[0].lvlbranch0 = 1; + msn->levels[0].progress = mem_strdup("taurus.ogf"); + break; } #ifndef DEMO - else if (mode == 4) { + case 4: { strcpy(msn->name, "Pilot Training"); msn->filename = mem_strdup("training.mn3"); - // strncpy(lvls[0].name, "Training", MSN_NAMELEN-1); - lvls[0].filename = mem_strdup("trainingmission.d3l"); - lvls[0].briefname = NULL; // mem_strdup("training.brf"); - lvls[0].flags = 0; - lvls[0].lvlbranch0 = 0; - lvls[0].progress = mem_strdup("trainingload.ogf"); + // strncpy(msn->levels[0].name, "Training", MSN_NAMELEN-1); + msn->levels[0].filename = mem_strdup("trainingmission.d3l"); + msn->levels[0].briefname = nullptr; // mem_strdup("training.brf"); + msn->levels[0].flags = 0; + msn->levels[0].lvlbranch0 = 0; + msn->levels[0].progress = mem_strdup("trainingload.ogf"); mn3_Open("training.mn3"); + break; } #endif - // load default script here. + default: + return false; + } + msn->levels.resize(msn->num_levels); + // load default script here. InitMissionScript(); return true; } @@ -895,10 +901,9 @@ bool LoadMission(const char *mssn) { #endif #else - tLevelNode *lvls = nullptr; // Temporary storage for level data. tMission *msn; CFILE *fp = nullptr; // Mission file - char errtext[80]; // Stores error if unable to read mission + char errtext[80]; // Stores error if unable to read mission std::filesystem::path msnfname; std::filesystem::path mission; int srclinenum = 0; // Current line of source. @@ -909,13 +914,13 @@ bool LoadMission(const char *mssn) { bool res = false; // used to specify if no error has occurred. std::filesystem::path pathname; ResetMission(); // Reset everything. - // open MN3 if filename passed was an mn3 file. + // Open MN3 if filename passed was a mn3 file. if (IS_MN3_FILE(mssn)) { mission = mssn; pathname = std::filesystem::path("missions") / mission; } else { - mission = mssn; + mission = mssn; pathname = mssn; msnfname = mssn; } @@ -944,6 +949,7 @@ bool LoadMission(const char *mssn) { msn->multiplayable = true; msn->singleplayable = true; msn->training_mission = false; + msn->levels.reserve(50); // Reserve some place before we know actual number of levels while (!cfeof(fp)) { char srcline[128]; // One line of mission source @@ -991,7 +997,7 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - for (auto & url : Net_msn_URLs.URL) { + for (auto &url : Net_msn_URLs.URL) { if (url[0] == '\0') { strncpy(url, operand, MAX_MISSION_URL_LEN - 1); url[MAX_MISSION_URL_LEN - 1] = '\0'; @@ -1027,9 +1033,9 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_SCORE; - lvls[curlvlnum - 1].score = mem_strdup(operand); - if (!lvls[curlvlnum - 1].score) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_SCORE; + msn->levels[curlvlnum - 1].score = mem_strdup(operand); + if (!msn->levels[curlvlnum - 1].score) goto fatal_error; } } else if (!stricmp(command, "PROGRESS")) { @@ -1037,8 +1043,8 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].progress = mem_strdup(operand); - if (!lvls[curlvlnum - 1].progress) + msn->levels[curlvlnum - 1].progress = mem_strdup(operand); + if (!msn->levels[curlvlnum - 1].progress) goto fatal_error; } } else if (!stricmp(command, "HOG")) { @@ -1047,8 +1053,8 @@ bool LoadMission(const char *mssn) { if (!msn->hog) goto fatal_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_SPECIALHOG; - if (!(lvls[curlvlnum - 1].hog = mem_strdup(operand))) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_SPECIALHOG; + if (!(msn->levels[curlvlnum - 1].hog = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "NUMLEVELS")) { @@ -1062,9 +1068,8 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLNUMINVALID); goto msnfile_error; } - lvls = mem_rmalloc(value); - memset(lvls, 0, sizeof(tLevelNode) * value); numlevels = value; + msn->levels.resize(numlevels); // Now we know number of levels, resize vector } } else if (!stricmp(command, "LEVEL")) { // first check if number of level is greater than num_levels @@ -1073,10 +1078,7 @@ bool LoadMission(const char *mssn) { goto msnfile_error; } curlvlnum = atoi(operand); - if (curlvlnum == 0) { - strcpy(errtext, TXT_MSN_LVLNUMINVALID); - goto msnfile_error; - } else if (curlvlnum > numlevels || curlvlnum < 0) { + if (curlvlnum > numlevels || curlvlnum <= 0) { strcpy(errtext, TXT_MSN_LVLNUMINVALID); goto msnfile_error; } @@ -1085,8 +1087,8 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_STARTMOVIE; - if (!(lvls[curlvlnum - 1].moviename = mem_strdup(operand))) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_STARTMOVIE; + if (!(msn->levels[curlvlnum - 1].moviename = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "INTRODEFAULT")) { @@ -1094,8 +1096,8 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else if (!Descent_overrided_intro) { - lvls[curlvlnum - 1].flags |= LVLFLAG_STARTMOVIE; - if (!(lvls[curlvlnum - 1].moviename = mem_strdup(operand))) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_STARTMOVIE; + if (!(msn->levels[curlvlnum - 1].moviename = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "ENDMOVIE")) { @@ -1103,8 +1105,8 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_ENDMOVIE; - if (!(lvls[curlvlnum - 1].endmovie = mem_strdup(operand))) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_ENDMOVIE; + if (!(msn->levels[curlvlnum - 1].endmovie = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "MINE")) { @@ -1112,7 +1114,7 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - if (!(lvls[curlvlnum - 1].filename = mem_strdup(operand))) + if (!(msn->levels[curlvlnum - 1].filename = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "SECRET")) { @@ -1120,16 +1122,16 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_SPAWNSECRET; - lvls[curlvlnum - 1].secretlvl = atoi(operand); + msn->levels[curlvlnum - 1].flags |= LVLFLAG_SPAWNSECRET; + msn->levels[curlvlnum - 1].secretlvl = atoi(operand); } } else if (!stricmp(command, "BRIEFING")) { if (curlvlnum == 0) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_BRIEFING; - if (!(lvls[curlvlnum - 1].briefname = mem_strdup(operand))) + msn->levels[curlvlnum - 1].flags |= LVLFLAG_BRIEFING; + if (!(msn->levels[curlvlnum - 1].briefname = mem_strdup(operand))) goto fatal_error; } } else if (!stricmp(command, "BRANCH")) { @@ -1144,14 +1146,14 @@ bool LoadMission(const char *mssn) { strcpy(errtext, TXT_MSN_LVLNUMINVALID); goto msnfile_error; } - lvls[curlvlnum - 1].flags |= LVLFLAG_BRANCH; - lvls[curlvlnum - 1].lvlbranch0 = lvlnum; + msn->levels[curlvlnum - 1].flags |= LVLFLAG_BRANCH; + msn->levels[curlvlnum - 1].lvlbranch0 = lvlnum; } else if (!stricmp(command, "ENDMISSION")) { if (curlvlnum == 0) { strcpy(errtext, TXT_MSN_LVLCOMMAND); goto msnfile_error; } else { - lvls[curlvlnum - 1].flags |= LVLFLAG_FINAL; + msn->levels[curlvlnum - 1].flags |= LVLFLAG_FINAL; } } else { snprintf(errtext, sizeof(errtext), TXT_MSN_ILLEGALCMD, command); @@ -1163,7 +1165,6 @@ bool LoadMission(const char *mssn) { // set up current mission (movies are already set above) msn->cur_level = 1; msn->num_levels = numlevels; - msn->levels = lvls; msn->filename = mem_strdup(mission.u8string().c_str()); msn->game_state_flags = 0; strcpy(Net_msn_URLs.msnname, mission.u8string().c_str()); @@ -1179,8 +1180,6 @@ bool LoadMission(const char *mssn) { } else { PrintDedicatedMessage("%s: %s\n", TXT_ERROR, str_text); } - if (lvls) - mem_free(lvls); } if (fp) cfclose(fp); @@ -1203,35 +1202,32 @@ void FreeMission() { mn3_Close(); // Tell Osiris to shutdown the Osiris Mission Memory System, freeing all memory Osiris_CloseOMMS(); - if (Current_mission.levels) { - // free up any data allocated per level node. - for (i = 0; i < Current_mission.num_levels; i++) { - if (Current_mission.levels[i].filename) - mem_free(Current_mission.levels[i].filename); - if (Current_mission.levels[i].briefname) - mem_free(Current_mission.levels[i].briefname); - if (Current_mission.levels[i].hog) - mem_free(Current_mission.levels[i].hog); - if (Current_mission.levels[i].moviename) - mem_free(Current_mission.levels[i].moviename); - if (Current_mission.levels[i].endmovie) - mem_free(Current_mission.levels[i].endmovie); - if (Current_mission.levels[i].score) - mem_free(Current_mission.levels[i].score); - if (Current_mission.levels[i].progress) - mem_free(Current_mission.levels[i].progress); - } - mem_free(Current_mission.levels); - Current_mission.levels = nullptr; + // free up any data allocated per level node. + for (auto level : Current_mission.levels) { + if (level.filename) + mem_free(level.filename); + if (level.briefname) + mem_free(level.briefname); + if (level.hog) + mem_free(level.hog); + if (level.moviename) + mem_free(level.moviename); + if (level.endmovie) + mem_free(level.endmovie); + if (level.score) + mem_free(level.score); + if (level.progress) + mem_free(level.progress); } + Current_mission.levels.clear(); if (Current_mission.hog) mem_free(Current_mission.hog); if (Current_mission.filename) { mem_free(Current_mission.filename); - Current_mission.filename = nullptr; } Current_mission.hog = nullptr; + Current_mission.filename = nullptr; Current_level = nullptr; } @@ -1416,7 +1412,7 @@ void LoadLevelProgress(int step, float percent, const char *chunk) { #else DrawLargeBitmap(&level_bmp, 0, 0, 1.0f); #endif - // do relevent text. + // do relevant text. str[0] = 0; if (chunk) { if (strncmp(CHUNK_TERRAIN, chunk, strlen(CHUNK_TERRAIN)) == 0) @@ -1798,7 +1794,7 @@ bool mn3_Open(const std::filesystem::path &mn3file) { Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); } else if (stricmp(filename.u8string().c_str(), "d3_2") == 0) { // Open audio hog file - voice_hog = std::filesystem::path("missions") / "d3voice2.hog"; // Audio for levels 5-17 + voice_hog = std::filesystem::path("missions") / "d3voice2.hog"; // Audio for levels 5-17 Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); } filename.replace_extension(".gam"); @@ -1897,7 +1893,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { } while ((tokp) && (msn_key_count < NUM_KEYWORDS)); } - for (auto & msn_keyword : msn_keywords) { + for (auto &msn_keyword : msn_keywords) { if (msn_keyword[0] == 0) { continue; } @@ -1909,7 +1905,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { mem_free(parse_keys); // Loop through looking for matches - for (auto & mod_keyword : mod_keywords) { + for (auto &mod_keyword : mod_keywords) { if (mod_keyword[0] == 0) { continue; } @@ -1920,7 +1916,7 @@ int MissionGetKeywords(const char *mission, char *keywords) { } else { bool found_keyword = false; // Loop through looking for matches - for (auto & msn_keyword : msn_keywords) { + for (auto &msn_keyword : msn_keywords) { if (msn_keyword[0] == 0) { continue; } @@ -1962,9 +1958,8 @@ void QuickStartMission() { // this initializes a mini one level mission with no frills. Current_mission.cur_level = 1; Current_mission.num_levels = 1; - Current_mission.levels = mem_rmalloc(); - memset(Current_mission.levels, 0, sizeof(tLevelNode)); - Current_level = Current_mission.levels; + Current_mission.levels.emplace_back(tLevelNode{}); + Current_level = &Current_mission.levels[0]; if (Editor_quickplay_levelname[0] != '\0') Current_level->filename = mem_strdup(Editor_quickplay_levelname); else diff --git a/Descent3/Mission.h b/Descent3/Mission.h index 54af70de3..d2114f0bc 100644 --- a/Descent3/Mission.h +++ b/Descent3/Mission.h @@ -167,6 +167,8 @@ #ifndef MISSION_H #define MISSION_H +#include + #include "pstypes.h" #include "descent.h" @@ -209,22 +211,22 @@ extern level_info Level_info; // level information struct tLevelNode { - // level flags - unsigned flags; // level flags - unsigned objective_flags; // level objective flags + // Level flags + uint32_t flags; // level flags + uint32_t objective_flags; // level objective flags - // movies + // Movies char *moviename; char *endmovie; - // level filename + // Level filename char *filename; // mine filename. char *briefname; // briefing filename char *hog; // hog file name for this level char *score; // music score of level char *progress; // File name containing the progress background screen - // level branching + // Level branching uint8_t lvlbranch0, lvlbranch1; // FORK or BRANCH command uint8_t secretlvl; // SECRET command uint8_t pad; @@ -251,7 +253,7 @@ struct tMission { // listing of levels. int num_levels; // number of levels int cur_level; // current level playing. - tLevelNode *levels; // array of levels + std::vector levels; // array of levels }; // structure used to get information about a mission diff --git a/Descent3/gamesequence.cpp b/Descent3/gamesequence.cpp index a8925b6d6..9a62f9478 100644 --- a/Descent3/gamesequence.cpp +++ b/Descent3/gamesequence.cpp @@ -1328,8 +1328,6 @@ bool SimpleStartLevel(const std::filesystem::path& level_name) { Current_mission.cur_level = 1; Current_mission.num_levels = 1; - Current_mission.levels = mem_rmalloc(); - memset(Current_mission.levels, 0, sizeof(tLevelNode)); Current_level = nullptr; Current_mission.levels[0].filename = mem_strdup(level_name.u8string().c_str()); diff --git a/Descent3/menu.cpp b/Descent3/menu.cpp index a6d0dbd42..0f7e6c5a0 100644 --- a/Descent3/menu.cpp +++ b/Descent3/menu.cpp @@ -655,6 +655,7 @@ #include #include #include +#include #include #include "log.h" @@ -1038,6 +1039,7 @@ bool ProcessCommandLine() { #define MSNBTN_X2 ((3 * MSNDLG_WIDTH / 4) - (MSNBTN_W / 2)) #define MSNBTN_Y (MSNDLG_HEIGHT - 64) #define UID_MSNLB 100 +#define UID_LVLB 101 #define UID_MSNINFO 0x1000 #define TRAINING_MISSION_NAME "Pilot Training" @@ -1080,7 +1082,7 @@ static inline int generate_mission_listbox(newuiListBox *lb, std::vectorAddItem(msninfo.name); c++; @@ -1163,7 +1165,7 @@ bool MenuNewGame() { #endif #ifndef OEM // generate real listbox now. - n_missions = generate_mission_listbox(msn_lb, filelist, D3MissionsDir); + n_missions = generate_mission_listbox(msn_lb, filelist, cf_LocateMultiplePaths("missions")); if (n_missions == 0) { DoMessageBox(TXT_ERROR, TXT_NOMISSIONS, MSGBOX_OK); @@ -1281,42 +1283,73 @@ bool MenuNewGame() { // DisplayLevelWarpDlg // pass in the max level allowed to be chosen, if -1, than all levels are allowed (a.k.a level warp cheat) int DisplayLevelWarpDlg(int max_level) { - newuiMessageBox hwnd; - newuiSheet *sheet; + newuiTiledWindow menu; + newuiSheet *select_sheet; + newuiListBox *level_listbox; + int chosen_level = 1, res; int highest_allowed; - char buffer[128]; - char *input_text; - // creates a sheet - sheet = hwnd.GetSheet(); + + // Create UI + menu.Create(Current_mission.name, 0, 0, 448, 384); + select_sheet = menu.GetSheet(); + select_sheet->NewGroup(nullptr, 10, 0); + level_listbox = select_sheet->AddListBox(352, 256, UID_LVLB, UILB_NOSORT); + select_sheet->NewGroup(nullptr, 160, 280, NEWUI_ALIGN_HORIZ); + select_sheet->AddButton(TXT_OK, UID_OK); + select_sheet->AddButton(TXT_CANCEL, UID_CANCEL); + if (max_level != -1) { - hwnd.Create(TXT_LEVELSELECT, MSGBOX_OKCANCEL); highest_allowed = max_level; - snprintf(buffer, sizeof(buffer), TXT_LEVELSELECTB, highest_allowed); } else { // level warp - hwnd.Create(TXT_LEVELWARP, MSGBOX_OKCANCEL); highest_allowed = Current_mission.num_levels; - snprintf(buffer, sizeof(buffer), TXT_LEVELWARPB, Current_mission.num_levels); } - sheet->NewGroup(buffer, 0, 0); - input_text = sheet->AddEditBox(nullptr, 4, 64, IDV_QUIT, UIED_NUMBERS); - sprintf(input_text, "%d", chosen_level); -redo_level_choose: - hwnd.Open(); - res = hwnd.DoUI(); - hwnd.Close(); - if (res == UID_OK || res == IDV_QUIT) { - chosen_level = atoi(input_text); - if (chosen_level < 1 || chosen_level > highest_allowed) { - snprintf(buffer, sizeof(buffer), TXT_CHOOSELEVEL, highest_allowed); - DoMessageBox(TXT_ERROR, buffer, MSGBOX_OK); - goto redo_level_choose; - } - } else { - chosen_level = -1; + + int mn3_handle = -1; + + // Because of D3: Retribution split mission nature we have to load library early + if (stricmp(Current_mission.filename, "d3.mn3") == 0) { + mn3_handle = cf_OpenLibrary(std::filesystem::path("missions") / "d3_2.mn3"); + } + if (stricmp(Current_mission.filename, "d3_2.mn3") == 0) { + mn3_handle = cf_OpenLibrary(std::filesystem::path("missions") / "d3.mn3"); } - hwnd.Destroy(); + + for (int i = 0; i < highest_allowed; i++) { + std::stringstream ss; + level_info info{}; + + LoadLevelInfo(Current_mission.levels[i].filename, info); + ss << std::setw(2) << std::setfill('0') << i + 1 << ". " << info.name; + level_listbox->AddItem(ss.str().c_str()); + } + + if (mn3_handle != -1) { + cf_CloseLibrary(mn3_handle); + } + + // Main UI loop + menu.Open(); + do { + res = menu.DoUI(); + if (res == UID_OK || res == UID_LVLB) { + int index = level_listbox->GetCurrentIndex(); + chosen_level = index + 1; // Levels are 1-based + if (chosen_level < 1 || chosen_level > highest_allowed) { + DoMessageBox(TXT_ERROR, TXT_CHOOSELEVEL, MSGBOX_OK); + continue; // Allow another selection + } + if (res == UID_LVLB) { + res = UID_OK; + } + } else if (res == UID_CANCEL) { + chosen_level = -1; + } + } while (res != UID_OK && res != UID_CANCEL); + + menu.Close(); + menu.Destroy(); return chosen_level; } From 83886a2ec7c989aad427fa5d25c36af66f546103 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Fri, 18 Oct 2024 03:21:19 +0300 Subject: [PATCH 5/5] Deduplicate code for header and CHUNK_LEVEL_INFO reading --- Descent3/LoadLevel.cpp | 107 +++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/Descent3/LoadLevel.cpp b/Descent3/LoadLevel.cpp index 939ffd145..02ac3bdd3 100644 --- a/Descent3/LoadLevel.cpp +++ b/Descent3/LoadLevel.cpp @@ -1459,7 +1459,7 @@ tConvertObject object_convert[] = { int object_convert_size = sizeof(object_convert) / sizeof(tConvertObject); -int chunk_start, chunk_size, filelen; +uint32_t chunk_start, chunk_size, filelen; int CountDataToPageIn(); @@ -2648,7 +2648,44 @@ void BuildXlateTable(CFILE *ifile, int (*lookup_func)(const char *), int16_t *xl xlate_table[i] = -1; } -#define ISCHUNK(name) (!strncmp(chunk_name, name, 4)) +#define ISCHUNK(name) (strncmp(chunk_name, name, 4) == 0) + +/** + * Read and check level header + * @param fp file handle of level + * @return version of level, -1 on error + */ +int ReadHeader(CFILE *fp) { + // Read & check tag + char chunk_name[4]; + cf_ReadBytes((uint8_t *)chunk_name, 4, fp); + if (!ISCHUNK(LEVEL_FILE_TAG)) { + return -1; + } + + // Read version number + int version = cf_ReadInt(fp); + return version; +} + +void ReadInfoChunk(CFILE *fp, level_info &info) { + // Initialize info + strcpy(info.name, "Unnamed"); + strcpy(info.designer, "Anonymous"); + strcpy(info.copyright, ""); + strcpy(info.notes, ""); + + cf_ReadString(info.name, sizeof(info.name), fp); + + // Localize level name here... + strcpy(info.name, LocalizeLevelName(info.name)); + + cf_ReadString(info.designer, sizeof(info.designer), fp); + cf_ReadString(info.copyright, sizeof(info.copyright), fp); + cf_ReadString(info.notes, sizeof(info.notes), fp); +} + + // Reads info about our lightmaps void ReadNewLightmapChunk(CFILE *fp, int version) { @@ -3600,39 +3637,20 @@ bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info) { try { // catch cfile errors - // Read & check tag - char tag[4]; - cf_ReadBytes((uint8_t *)tag, 4, ifile); - if (strncmp(tag, LEVEL_FILE_TAG, 4)) { - LOG_ERROR.printf("%s is not a level file (tag %c%c%c%c)", - filename.u8string().c_str(), tag[0], tag[1], tag[2], tag[3]); - cfclose(ifile); - return false; - } - - // Read & check version number - int version = cf_ReadInt(ifile); + // Read and check header + int version = ReadHeader(ifile); - // Check for too-new version if (version > LEVEL_FILE_VERSION) { - LOG_ERROR.printf("Mission file %s too new (version %d)", filename.u8string().c_str(), version); + LOG_ERROR.printf("Mission file too new (version %d)", version); cfclose(ifile); return false; } if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) { - LOG_ERROR.printf("Mission file %s too old (version %d)", filename.u8string().c_str(), version); + LOG_ERROR.printf("Mission file too old (version %d)", version); cfclose(ifile); return false; } - - // Init level info - strcpy(info.name, "Unnamed"); - strcpy(info.designer, "Anonymous"); - strcpy(info.copyright, ""); - strcpy(info.notes, ""); - - while (!cfeof(ifile)) { char chunk_name[4]; @@ -3641,14 +3659,7 @@ bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info) { chunk_size = cf_ReadInt(ifile); if (ISCHUNK(CHUNK_LEVEL_INFO)) { - cf_ReadString(info.name, sizeof(info.name), ifile); - - // Localize level name here... - strcpy(info.name, LocalizeLevelName(info.name)); - - cf_ReadString(info.designer, sizeof(info.designer), ifile); - cf_ReadString(info.copyright, sizeof(info.copyright), ifile); - cf_ReadString(info.notes, sizeof(info.notes), ifile); + ReadInfoChunk(ifile, info); found = true; break; } else { @@ -3700,19 +3711,11 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) { filelen = cfilelength(ifile); - // Read & check tag - cf_ReadBytes((uint8_t *)tag, 4, ifile); - if (strncmp(tag, LEVEL_FILE_TAG, 4)) { - cfclose(ifile); - retval = 0; - goto end_loadlevel; - } - - // Read & check version number - version = cf_ReadInt(ifile); + version = ReadHeader(ifile); - // Check for too-new version + // Check for too new version if (version > LEVEL_FILE_VERSION) { + LOG_ERROR.printf("Mission file too new (version %d)", version); cfclose(ifile); #if (defined(EDITOR) || defined(NEWEDITOR)) if (GetFunctionMode() == EDITOR_MODE) @@ -3725,6 +3728,7 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) { // Check for too-old version if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) { + LOG_ERROR.printf("Mission file too old (version %d)", version); cfclose(ifile); #if (defined(EDITOR) || defined(NEWEDITOR)) if (GetFunctionMode() == EDITOR_MODE) @@ -3770,12 +3774,6 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) { // Clear terrain sounds ClearTerrainSound(); - // Init level info - strcpy(Level_info.name, "Unnamed"); - strcpy(Level_info.designer, "Anonymous"); - strcpy(Level_info.copyright, ""); - strcpy(Level_info.notes, ""); - BOA_AABB_checksum = BOA_mine_checksum = 0; for (i = 0; i < MAX_ROOMS; i++) { BOA_AABB_ROOM_checksum[i] = 0; @@ -4009,14 +4007,7 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) { } else if (ISCHUNK(CHUNK_TERRAIN)) { ReadTerrainChunks(ifile, version); } else if (ISCHUNK(CHUNK_LEVEL_INFO)) { - cf_ReadString(Level_info.name, sizeof(Level_info.name), ifile); - - // Localize level name here... - strcpy(Level_info.name, LocalizeLevelName(Level_info.name)); - - cf_ReadString(Level_info.designer, sizeof(Level_info.designer), ifile); - cf_ReadString(Level_info.copyright, sizeof(Level_info.copyright), ifile); - cf_ReadString(Level_info.notes, sizeof(Level_info.notes), ifile); + ReadInfoChunk(ifile, Level_info); if (version >= 83) { Gravity_strength = cf_ReadFloat(ifile);