diff --git a/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp b/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp index 62fa7c00..f3d57cf7 100644 --- a/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp @@ -172,7 +172,7 @@ MxResult LegoWorldPresenter::LoadWorld(char* p_worldName, LegoWorld* p_world) } strcat(wdbPath, "lego\\data\\world.wdb"); - MxString::NormalizePath(wdbPath); + MxString::MapPathToFilesystem(wdbPath); SDL_IOStream* wdbFile; @@ -184,7 +184,7 @@ MxResult LegoWorldPresenter::LoadWorld(char* p_worldName, LegoWorld* p_world) } strcat(wdbPath, "lego\\data\\world.wdb"); - MxString::NormalizePath(wdbPath); + MxString::MapPathToFilesystem(wdbPath); if ((wdbFile = SDL_IOFromFile(wdbPath, "rb")) == NULL) { return FAILURE; diff --git a/LEGO1/lego/sources/misc/legostorage.cpp b/LEGO1/lego/sources/misc/legostorage.cpp index af4ecb8e..24bbee09 100644 --- a/LEGO1/lego/sources/misc/legostorage.cpp +++ b/LEGO1/lego/sources/misc/legostorage.cpp @@ -121,7 +121,7 @@ LegoResult LegoFile::Open(const char* p_name, LegoU32 p_mode) } MxString path(p_name); - path.NormalizePath(); + path.MapPathToFilesystem(); if (!(m_file = SDL_IOFromFile(path.GetData(), mode))) { return FAILURE; diff --git a/LEGO1/omni/include/mxomni.h b/LEGO1/omni/include/mxomni.h index 91e3cd6c..250a4554 100644 --- a/LEGO1/omni/include/mxomni.h +++ b/LEGO1/omni/include/mxomni.h @@ -4,6 +4,7 @@ #include "lego1_export.h" #include "mxcore.h" #include "mxcriticalsection.h" +#include "mxstl/stlcompat.h" #include "mxstring.h" #include @@ -42,7 +43,8 @@ class MxOmni : public MxCore { LEGO1_EXPORT static void SetCD(const char* p_cd); LEGO1_EXPORT static void SetHD(const char* p_hd); LEGO1_EXPORT static void SetSound3D(MxBool p_use3dSound); - static void NormalizePath(char* p_path); + static const vector& GetHDFiles() { return g_hdFiles; } + static const vector& GetCDFiles() { return g_cdFiles; } MxOmni(); ~MxOmni() override; @@ -105,6 +107,10 @@ class MxOmni : public MxCore { protected: static MxOmni* g_instance; + static vector g_hdFiles; + static vector g_cdFiles; + + static vector GlobIsleFiles(const MxString& p_path); MxString m_mediaPath; // 0x08 HWND m_windowHandle; // 0x18 diff --git a/LEGO1/omni/include/mxstring.h b/LEGO1/omni/include/mxstring.h index 7393d534..8e7fba10 100644 --- a/LEGO1/omni/include/mxstring.h +++ b/LEGO1/omni/include/mxstring.h @@ -18,7 +18,7 @@ class MxString : public MxCore { void Reverse(); void ToUpperCase(); void ToLowerCase(); - void NormalizePath() { NormalizePath(m_data); } + void MapPathToFilesystem() { MapPathToFilesystem(m_data); } MxString& operator=(const MxString& p_str); const MxString& operator=(const char* p_str); @@ -27,7 +27,7 @@ class MxString : public MxCore { MxString& operator+=(const char* p_str); static void CharSwap(char* p_a, char* p_b); - static void NormalizePath(char* p_path); + static void MapPathToFilesystem(char* p_path); // FUNCTION: BETA10 0x10017c50 char* GetData() const { return m_data; } diff --git a/LEGO1/omni/src/common/mxstring.cpp b/LEGO1/omni/src/common/mxstring.cpp index fe75c36f..8ddc6a86 100644 --- a/LEGO1/omni/src/common/mxstring.cpp +++ b/LEGO1/omni/src/common/mxstring.cpp @@ -1,7 +1,9 @@ #include "mxstring.h" #include "decomp.h" +#include "mxomni.h" +#include #include #include #include @@ -188,13 +190,11 @@ void MxString::CharSwap(char* p_a, char* p_b) *p_b = t; } -void MxString::NormalizePath(char* p_path) +void MxString::MapPathToFilesystem(char* p_path) { // [library:filesystem] - // This function is used to build a consistent path that will eventually be used to read a file. - // The input may come with Windows path separators, i.e. \lego\scripts\infocntr\infomain - // We have to replace the backslashes with forward slashes to be able to access the files - // on non-Windows systems + // This function is used to map an internal Windows-style path (found in SI files or in the code) + // to an actual file on disk or CD. We have to account for both Windows path separators and case. #if !defined(SDL_PLATFORM_WINDOWS) char* path = p_path; while (*path) { @@ -204,5 +204,30 @@ void MxString::NormalizePath(char* p_path) path++; } + + size_t pathLen = SDL_strlen(p_path); + + auto mapPath = [p_path, pathLen](const vector& p_files) -> bool { + for (const MxString& file : p_files) { + // Test whether file is a suffix of the provided path (case insensitive) + // If yes, copy the original file system path into p_path. + for (size_t i = pathLen, j = file.GetLength(); i != 0 && j != 0; i--, j--) { + if (SDL_tolower(p_path[i - 1]) != SDL_tolower(file.GetData()[j - 1])) { + break; + } + else if (j == 1) { + SDL_strlcpy(&p_path[i - 1], file.GetData(), file.GetLength() + 1); + SDL_Log("Resolved file path to %s", p_path); + return true; + } + } + } + + return false; + }; + + if (!mapPath(MxOmni::GetHDFiles())) { + mapPath(MxOmni::GetCDFiles()); + } #endif } diff --git a/LEGO1/omni/src/main/mxomni.cpp b/LEGO1/omni/src/main/mxomni.cpp index bb0baee1..7c701c49 100644 --- a/LEGO1/omni/src/main/mxomni.cpp +++ b/LEGO1/omni/src/main/mxomni.cpp @@ -17,6 +17,9 @@ #include "mxvariabletable.h" #include "mxvideomanager.h" +#include +#include + // GLOBAL: LEGO1 0x101015b8 MxString g_hdPath = ""; @@ -29,6 +32,9 @@ MxBool g_use3dSound = FALSE; // GLOBAL: LEGO1 0x101015b0 MxOmni* MxOmni::g_instance = NULL; +vector MxOmni::g_hdFiles; +vector MxOmni::g_cdFiles; + // FUNCTION: LEGO1 0x100aef10 MxOmni::MxOmni() { @@ -357,6 +363,7 @@ const char* MxOmni::GetHD() void MxOmni::SetHD(const char* p_hd) { g_hdPath = p_hd; + g_hdFiles = GlobIsleFiles(g_hdPath); } // FUNCTION: LEGO1 0x100b0940 @@ -369,6 +376,7 @@ const char* MxOmni::GetCD() void MxOmni::SetCD(const char* p_cd) { g_cdPath = p_cd; + g_cdFiles = GlobIsleFiles(g_cdPath); } // FUNCTION: LEGO1 0x100b0980 @@ -415,3 +423,26 @@ void MxOmni::Resume() m_paused = FALSE; } } + +vector MxOmni::GlobIsleFiles(const MxString& p_path) +{ + int count; + char** files = SDL_GlobDirectory(p_path.GetData(), NULL, 0, &count); + vector result; + + if (files == NULL) { + SDL_Log("Error enumerating files for path %s (%s)", p_path.GetData(), SDL_GetError()); + return result; + } + + for (int i = 0; i < count; i++) { + if (!SDL_strncasecmp(files[i], "lego", 4)) { + result.emplace_back(files[i]); + } + } + + SDL_Log("Found %d game files in %s", count, p_path.GetData()); + + SDL_free(files); + return result; +} diff --git a/LEGO1/omni/src/stream/mxio.cpp b/LEGO1/omni/src/stream/mxio.cpp index 52c15186..87cdaf8b 100644 --- a/LEGO1/omni/src/stream/mxio.cpp +++ b/LEGO1/omni/src/stream/mxio.cpp @@ -41,7 +41,7 @@ MxU16 MXIOINFO::Open(const char* p_filename, MxULong p_flags) m_info.lDiskOffset = m_info.lBufOffset = 0; MxString path(p_filename); - path.NormalizePath(); + path.MapPathToFilesystem(); ASSIGN_M_FILE(SDL_IOFromFile(path.GetData(), "rb")); if (M_FILE != NULL) {