From 1ecef6f8efc8335d7f479756fe6f82e68c8fd3e4 Mon Sep 17 00:00:00 2001 From: elasota Date: Mon, 12 Oct 2020 18:03:23 -0400 Subject: [PATCH] Add support for unpackaged resources to speed up loads on Android, i.e. so we don't have to decompress entire GPAs to load a single resource. --- Aerofoil/GpFileSystem_Win32.cpp | 72 +++- Aerofoil/GpFileSystem_Win32.h | 8 +- AerofoilAndroid/.gitignore | 1 + AerofoilAndroid/copy_packaged_resources.bat | 49 +++ AerofoilAndroid/make_symlinks.bat | 1 - AerofoilAndroid/remove_symlinks.bat | 1 - GpApp/Environ.cpp | 104 ----- GpApp/Externs.h | 5 +- GpApp/GliderProtos.h | 2 - GpApp/House.cpp | 2 +- GpApp/HouseIO.cpp | 2 +- GpApp/Main.cpp | 1 - GpApp/Music.cpp | 19 - GpApp/RoomInfo.cpp | 2 +- GpApp/SelectHouse.cpp | 2 +- GpApp/Sound.cpp | 21 - GpApp/Utilities.cpp | 2 +- PortabilityLayer/GPArchive.cpp | 60 ++- PortabilityLayer/GPArchive.h | 3 + PortabilityLayer/HostFileSystem.h | 19 +- PortabilityLayer/HostSystemServices.h | 4 - PortabilityLayer/PLMovies.h | 4 +- PortabilityLayer/PLResourceManager.cpp | 450 +++++++++++++++++--- PortabilityLayer/ResTypeID.h | 6 + PortabilityLayer/ResourceManager.h | 80 +++- PortabilityLayer/ScanlineMaskBuilder.cpp | 6 +- 26 files changed, 674 insertions(+), 252 deletions(-) create mode 100644 AerofoilAndroid/copy_packaged_resources.bat diff --git a/Aerofoil/GpFileSystem_Win32.cpp b/Aerofoil/GpFileSystem_Win32.cpp index 865cc03..2e33820 100644 --- a/Aerofoil/GpFileSystem_Win32.cpp +++ b/Aerofoil/GpFileSystem_Win32.cpp @@ -183,7 +183,7 @@ bool GpFileSystem_Win32::FileExists(PortabilityLayer::VirtualDirectory_t virtual { wchar_t winPath[MAX_PATH + 1]; - if (!ResolvePath(virtualDirectory, path, winPath)) + if (!ResolvePath(virtualDirectory, &path, 1, winPath)) return false; return PathFileExistsW(winPath) != 0; @@ -193,7 +193,7 @@ bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtual { wchar_t winPath[MAX_PATH + 1]; - if (!ResolvePath(virtualDirectory, path, winPath)) + if (!ResolvePath(virtualDirectory, &path, 1, winPath)) { *exists = false; return false; @@ -210,11 +210,11 @@ bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtual return (attribs & FILE_ATTRIBUTE_READONLY) != 0; } -GpIOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) +GpIOStream *GpFileSystem_Win32::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) { wchar_t winPath[MAX_PATH + 1]; - if (!ResolvePath(virtualDirectory, path, winPath)) + if (!ResolvePath(virtualDirectory, paths, numPaths, winPath)) return false; const DWORD desiredAccess = writeAccess ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ; @@ -252,7 +252,7 @@ bool GpFileSystem_Win32::DeleteFile(PortabilityLayer::VirtualDirectory_t virtual { wchar_t winPath[MAX_PATH + 1]; - if (!ResolvePath(virtualDirectory, path, winPath)) + if (!ResolvePath(virtualDirectory, &path, 1, winPath)) return false; if (DeleteFileW(winPath)) @@ -270,11 +270,22 @@ bool GpFileSystem_Win32::DeleteFile(PortabilityLayer::VirtualDirectory_t virtual return false; } -PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) +PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) { wchar_t winPath[MAX_PATH + 2]; - if (!ResolvePath(virtualDirectory, "*", winPath)) + const char **expandedPaths = static_cast(malloc(sizeof(const char*) * (numPaths + 1))); + if (!expandedPaths) + return nullptr; + + for (size_t i = 0; i < numPaths; i++) + expandedPaths[i] = paths[i]; + expandedPaths[numPaths] = "*"; + + const bool isPathResolved = ResolvePath(virtualDirectory, expandedPaths, numPaths + 1, winPath); + free(expandedPaths); + + if (!isPathResolved) return nullptr; WIN32_FIND_DATAW findData; @@ -306,6 +317,11 @@ bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const return false; } +bool GpFileSystem_Win32::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const +{ + return false; +} + bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const { for (size_t i = 0; i < length; i++) @@ -407,7 +423,7 @@ GpFileSystem_Win32 *GpFileSystem_Win32::GetInstance() return &ms_instance; } -bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, wchar_t *outPath) +bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath) { const wchar_t *baseDir = nullptr; @@ -445,23 +461,39 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua return false; const size_t baseDirLen = wcslen(baseDir); - const size_t pathLen = strlen(path); - - if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < pathLen) - return false; - memcpy(outPath, baseDir, sizeof(wchar_t) * baseDirLen); - for (size_t i = 0; i < pathLen; i++) + outPath[baseDirLen] = static_cast(0); + + for (size_t i = 0; i < numPaths; i++) { - char c = path[i]; - if (c == '/') - c = '\\'; + size_t outDirLen = wcslen(outPath); - outPath[baseDirLen + i] = static_cast(c); + if (i != 0) + { + if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < 1) + return false; + + outPath[outDirLen++] = '\\'; + } + + const char *path = paths[i]; + const size_t pathLen = strlen(path); + + if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < pathLen) + return false; + + for (size_t j = 0; j < pathLen; j++) + { + char c = path[j]; + if (c == '/') + c = '\\'; + + outPath[outDirLen + j] = static_cast(c); + } + + outPath[outDirLen + pathLen] = static_cast(0); } - outPath[baseDirLen + pathLen] = static_cast(0); - return true; } diff --git a/Aerofoil/GpFileSystem_Win32.h b/Aerofoil/GpFileSystem_Win32.h index d19cb53..ebd09eb 100644 --- a/Aerofoil/GpFileSystem_Win32.h +++ b/Aerofoil/GpFileSystem_Win32.h @@ -14,19 +14,21 @@ public: bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override; bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override; - GpIOStream *OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) override; + GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override; bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override; - PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) override; + PortabilityLayer::HostDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override; bool ValidateFilePath(const char *path, size_t sz) const override; bool ValidateFilePathUnicodeChar(uint32_t ch) const override; + bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const override; + const wchar_t *GetBasePath() const; static GpFileSystem_Win32 *GetInstance(); private: - bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, wchar_t *outPath); + bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath); std::wstring m_prefsDir; std::wstring m_scoresDir; diff --git a/AerofoilAndroid/.gitignore b/AerofoilAndroid/.gitignore index ff66339..cc0602e 100644 --- a/AerofoilAndroid/.gitignore +++ b/AerofoilAndroid/.gitignore @@ -2,3 +2,4 @@ .gradle gradle local.properties +build diff --git a/AerofoilAndroid/copy_packaged_resources.bat b/AerofoilAndroid/copy_packaged_resources.bat new file mode 100644 index 0000000..1c0b712 --- /dev/null +++ b/AerofoilAndroid/copy_packaged_resources.bat @@ -0,0 +1,49 @@ +rmdir /S /Q app\src\main\assets\Packaged\ApplicationResources +..\Tools\7z.exe x -oapp\src\main\assets\Packaged\ApplicationResources ..\Packaged\ApplicationResources.gpa +cd app +cd src +cd main +cd assets +cd Packaged +rmdir /S /Q Houses +mkdir Houses +copy ..\..\..\..\..\..\Packaged\Houses\* Houses\ +cd Houses +..\..\..\..\..\..\..\Tools\7z.exe x "-oArt Museum" "Art Museum.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oArt Museum.mov" "Art Museum.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oCalifornia or Bust!" "California or Bust!.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oCastle o' the Air" "Castle o' the Air.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oCastle o' the Air.mov" "Castle o' the Air.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oCD Demo House" "CD Demo House.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oCD Demo House.mov" "CD Demo House.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oDavis Station" "Davis Station.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oDemo House" "Demo House.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oDemo House.mov" "Demo House.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oEmpty House" "Empty House.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oFun House" "Fun House.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oGrand Prix" "Grand Prix.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oGrand Prix.mov" "Grand Prix.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oImagineHouse PRO II" "ImagineHouse PRO II.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oImagineHouse PRO II.mov" "ImagineHouse PRO II.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oIn The Mirror" "In The Mirror.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oLand of Illusion" "Land of Illusion.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oLand of Illusion.mov" "Land of Illusion.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oLeviathan" "Leviathan.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oLeviathan.mov" "Leviathan.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oMetropolis" "Metropolis.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oNemo's Market" "Nemo's Market.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oNemo's Market.mov" "Nemo's Market.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oRainbow's End" "Rainbow's End.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oRainbow's End.mov" "Rainbow's End.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oSampler" "Sampler.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oSlumberland" "Slumberland.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oSlumberland.mov" "Slumberland.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oSpacePods" "SpacePods.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oSpacePods.mov" "SpacePods.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oTeddy World" "Teddy World.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oTeddy World.mov" "Teddy World.mov.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oThe Asylum Pro" "The Asylum Pro.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oTitanic" "Titanic.gpa" +..\..\..\..\..\..\..\Tools\7z.exe x "-oTitanic.mov" "Titanic.mov.gpa" +del /Q *.gpa +cd .. diff --git a/AerofoilAndroid/make_symlinks.bat b/AerofoilAndroid/make_symlinks.bat index ab38b63..fa4b677 100644 --- a/AerofoilAndroid/make_symlinks.bat +++ b/AerofoilAndroid/make_symlinks.bat @@ -17,7 +17,6 @@ mklink /D app\jni\zlib ..\..\..\zlib mklink /D app\jni\rapidjson ..\..\..\rapidjson mklink /D app\jni\MacRomanConversion ..\..\..\MacRomanConversion mklink /D app\jni\stb ..\..\..\stb -mklink /D app\src\main\assets\Packaged ..\..\..\..\..\Packaged mklink /D app\src\main\assets\Resources ..\..\..\..\..\Resources pause diff --git a/AerofoilAndroid/remove_symlinks.bat b/AerofoilAndroid/remove_symlinks.bat index e49eaa2..d5f28a2 100644 --- a/AerofoilAndroid/remove_symlinks.bat +++ b/AerofoilAndroid/remove_symlinks.bat @@ -14,5 +14,4 @@ rmdir app\jni\zlib rmdir app\jni\rapidjson rmdir app\jni\MacRomanConversion rmdir app\jni\stb -rmdir app\src\main\assets\Packaged rmdir app\src\main\assets\Resources diff --git a/GpApp/Environ.cpp b/GpApp/Environ.cpp index 62c13a2..70c01fe 100644 --- a/GpApp/Environ.cpp +++ b/GpApp/Environ.cpp @@ -419,110 +419,6 @@ void HandleDepthSwitching (void) PortabilityLayer::HostDisplayDriver::GetInstance()->SetUseICCProfile(isUseICCProfile != 0); } -//-------------------------------------------------------------- CheckMemorySize - -// Tests for a specific amount of memory available. If the required memory� -// is not available, attempts to turn off various game features (music, etc.)� -// in order to accomodate the constrained memory available. - -void CheckMemorySize (void) -{ - #define kBaseBytesNeeded 614400L // 600K Base memory - #define kPaddingBytes 204800L // 200K Padding - long bytesNeeded; - long soundBytes, musicBytes; - - dontLoadMusic = false; - dontLoadSounds = false; - - bytesNeeded = kBaseBytesNeeded; // base memory - soundBytes = SoundBytesNeeded(); // sound memory - if (soundBytes <= 0L) - RedAlert(kErrNoMemory); - else - bytesNeeded += soundBytes; - musicBytes = MusicBytesNeeded(); // music memory - if (musicBytes <= 0L) - RedAlert(kErrNoMemory); - else - bytesNeeded += musicBytes; - bytesNeeded += 4L * (long)thisMac.constrainedScreen.bottom; // main screen - bytesNeeded += (((long)houseRect.right - (long)houseRect.left) * - ((long)houseRect.bottom + 1 - (long)houseRect.top) * - (long)thisMac.isDepth) / 8L; // work map - bytesNeeded += 4L * (long)houseRect.bottom; - bytesNeeded += (((long)houseRect.right - (long)houseRect.left) * - ((long)houseRect.bottom + 1 - (long)houseRect.top) * - (long)thisMac.isDepth) / 8L; // back map - bytesNeeded += 4L * houseRect.bottom; - bytesNeeded += (((long)houseRect.right - (long)houseRect.left) * 21 * - (long)thisMac.isDepth) / 8L; // scoreboard map - bytesNeeded += (6396L * (long)thisMac.isDepth) / 8L; // more scoreboard - bytesNeeded += (32112L * (long)thisMac.isDepth) / 8L; // glider map - bytesNeeded += (32112L * (long)thisMac.isDepth) / 8L; // glider2 map - bytesNeeded += 32064L / 8L; // glider mask - bytesNeeded += (912L * (long)thisMac.isDepth) / 8L; // glider shadow - bytesNeeded += 864L / 8L; // shadow mask - bytesNeeded += (304L * (long)thisMac.isDepth) / 8L; // rubber bands - bytesNeeded += 288L / 8L; // bands mask - bytesNeeded += (19344L * (long)thisMac.isDepth) / 8L; // blower map - bytesNeeded += 19344L / 8L; // blower mask - bytesNeeded += (17856L * (long)thisMac.isDepth) / 8L; // furniture map - bytesNeeded += 17792L / 8L; // furniture mask - bytesNeeded += (33264L * (long)thisMac.isDepth) / 8L; // prizes map - bytesNeeded += 33176L / 8L; // prizes mask - bytesNeeded += (2904L * (long)thisMac.isDepth) / 8L; // points map - bytesNeeded += 2880L / 8L; // points mask - bytesNeeded += (1848L * (long)thisMac.isDepth) / 8L; // transport map - bytesNeeded += 1792L / 8L; // transport mask - bytesNeeded += (3360L * (long)thisMac.isDepth) / 8L; // switches map - bytesNeeded += (9144L * (long)thisMac.isDepth) / 8L; // lights map - bytesNeeded += 9072L / 8L; // lights mask - bytesNeeded += (21600L * (long)thisMac.isDepth) / 8L; // appliances map - bytesNeeded += 21520L / 8L; // appliances mask - bytesNeeded += (5600L * (long)thisMac.isDepth) / 8L; // toast map - bytesNeeded += 5568L / 8L; // toast mask - bytesNeeded += (1440L * (long)thisMac.isDepth) / 8L; // shredded map - bytesNeeded += 1400L / 8L; // shredded mask - bytesNeeded += (5784L * (long)thisMac.isDepth) / 8L; // balloon map - bytesNeeded += 5760L / 8L; // balloon mask - bytesNeeded += (9632L * (long)thisMac.isDepth) / 8L; // copter map - bytesNeeded += 9600L / 8L; // copter mask - bytesNeeded += (4928L * (long)thisMac.isDepth) / 8L; // dart map - bytesNeeded += 4864L / 8L; // dart mask - bytesNeeded += (2080L * (long)thisMac.isDepth) / 8L; // ball map - bytesNeeded += 2048L / 8L; // ball mask - bytesNeeded += (1168L * (long)thisMac.isDepth) / 8L; // drip map - bytesNeeded += 1152L / 8L; // drip mask - bytesNeeded += (1224L * (long)thisMac.isDepth) / 8L; // enemy map - bytesNeeded += 1188L / 8L; // enemy mask - bytesNeeded += (2064L * (long)thisMac.isDepth) / 8L; // fish map - bytesNeeded += 2048L / 8L; // fish mask - bytesNeeded += (8960L * (long)thisMac.isDepth) / 8L; // clutter map - bytesNeeded += 8832L / 8L; // clutter mask - bytesNeeded += (23040L * (long)thisMac.isDepth) / 8L; // support map - bytesNeeded += (4320L * (long)thisMac.isDepth) / 8L; // angel map - bytesNeeded += 4224L / 8L; // angel mask - bytesNeeded += sizeof(roomType); - bytesNeeded += sizeof(hotObject) * kMaxHotSpots; - bytesNeeded += sizeof(sparkleType) * kMaxSparkles; - bytesNeeded += sizeof(flyingPtType) * kMaxFlyingPts; - bytesNeeded += sizeof(flameType) * kMaxCandles; - bytesNeeded += sizeof(flameType) * kMaxTikis; - bytesNeeded += sizeof(flameType) * kMaxCoals; - bytesNeeded += sizeof(pendulumType) * kMaxPendulums; - bytesNeeded += sizeof(savedType) * kMaxSavedMaps; - bytesNeeded += sizeof(bandType) * kMaxRubberBands; - bytesNeeded += sizeof(greaseType) * kMaxGrease; - bytesNeeded += sizeof(starType) * kMaxStars; - bytesNeeded += sizeof(shredType) * kMaxShredded; - bytesNeeded += sizeof(dynaType) * kMaxDynamicObs; - bytesNeeded += sizeof(objDataType) * kMaxMasterObjects; - bytesNeeded += kDemoLength; SpinCursor(1); - - SpinCursor(1); -} - void GetDeviceRect(Rect *rect) { unsigned int width; diff --git a/GpApp/Externs.h b/GpApp/Externs.h index fe5a51c..87bb84c 100644 --- a/GpApp/Externs.h +++ b/GpApp/Externs.h @@ -14,7 +14,7 @@ namespace PortabilityLayer { class ResolveCachingColor; - class ResourceArchive; + struct IResourceArchive; class ScanlineMask; class ResTypeID; struct RGBAColor; @@ -169,7 +169,6 @@ void CheckOurEnvirons (void); void InstallResolutionHandler (void); //void ReflectSecondMonitorEnvirons (Boolean, Boolean, Boolean); void HandleDepthSwitching (void); -void CheckMemorySize (void); Boolean CheckFileError (short, const PLPasStr &); // --- File Error.c @@ -200,7 +199,7 @@ void LoadGraphic (DrawSurface *surface, short resID); // Only loads from app r void LoadGraphicCustom (DrawSurface *surface, short resID); // Supports custom graphics void LoadScaledGraphic (DrawSurface *, short, Rect *); // Only loads from app resources void LoadScaledGraphicCustom (DrawSurface *, short, Rect *); // Supports custom graphics -bool LargeIconPlot (DrawSurface *, PortabilityLayer::ResourceArchive *, short, const Rect &); +bool LargeIconPlot (DrawSurface *, PortabilityLayer::IResourceArchive *, short, const Rect &); void DrawCIcon (DrawSurface *surface, short, short, short); char KeyMapOffsetFromRawKey (char); long LongSquareRoot (long); diff --git a/GpApp/GliderProtos.h b/GpApp/GliderProtos.h index 634d825..b0d3b77 100644 --- a/GpApp/GliderProtos.h +++ b/GpApp/GliderProtos.h @@ -235,7 +235,6 @@ void ToggleMusicWhilePlaying (void); void SetMusicalMode (SInt16); void InitMusic (void); void KillMusic (void); -long MusicBytesNeeded (void); void TellHerNoMusic (void); Boolean AddNewObject (Point, SInt16, Boolean); // --- ObjectAdd.c @@ -456,7 +455,6 @@ PLError_t LoadTriggerSound (SInt16); void DumpTriggerSound (void); void InitSound (void); void KillSound (void); -long SoundBytesNeeded (void); void TellHerNoSounds (void); void BitchAboutSM3 (void); diff --git a/GpApp/House.cpp b/GpApp/House.cpp index 3076662..9472e70 100644 --- a/GpApp/House.cpp +++ b/GpApp/House.cpp @@ -28,7 +28,7 @@ void UpdateGoToDialog (Dialog *); int16_t GoToFilter (void *context, Dialog *dial, const TimeTaggedVOSEvent *evt); -extern PortabilityLayer::ResourceArchive *houseResFork; +extern PortabilityLayer::IResourceArchive *houseResFork; houseHand thisHouse; diff --git a/GpApp/HouseIO.cpp b/GpApp/HouseIO.cpp index d5950dc..44e3cd3 100644 --- a/GpApp/HouseIO.cpp +++ b/GpApp/HouseIO.cpp @@ -36,7 +36,7 @@ Boolean IsFileReadOnly (const VFileSpec &); AnimationPlayer theMovie; Rect movieRect; -PortabilityLayer::ResourceArchive *houseResFork; +PortabilityLayer::IResourceArchive *houseResFork; short wasHouseVersion; GpIOStream *houseStream; Boolean houseOpen, fileDirty, gameDirty; diff --git a/GpApp/Main.cpp b/GpApp/Main.cpp index cd360e2..3d06b89 100644 --- a/GpApp/Main.cpp +++ b/GpApp/Main.cpp @@ -373,7 +373,6 @@ int gpAppMain() // ReflectSecondMonitorEnvirons(false, true, true); HandleDepthSwitching(); VariableInit(); SpinCursor(2); - CheckMemorySize(); GetExtraCursors(); SpinCursor(2); InitMarquee(); CreatePointers(); SpinCursor(2); diff --git a/GpApp/Music.cpp b/GpApp/Music.cpp index 9c79f4b..88fedc4 100644 --- a/GpApp/Music.cpp +++ b/GpApp/Music.cpp @@ -368,25 +368,6 @@ void KillMusic (void) musicMutex->Destroy(); } -//-------------------------------------------------------------- MusicBytesNeeded - -long MusicBytesNeeded (void) -{ - size_t totalBytes; - short i; - - totalBytes = 0L; - for (i = 0; i < kMaxMusic; i++) - { - size_t resSize = 0; - if (!PortabilityLayer::ResourceManager::GetInstance()->GetAppResourceArchive()->GetResourceSize('snd ', i + kBaseBufferMusicID, resSize)) - return -1; - - totalBytes += static_cast(resSize); - } - return totalBytes; -} - //-------------------------------------------------------------- TellHerNoMusic void TellHerNoMusic (void) diff --git a/GpApp/RoomInfo.cpp b/GpApp/RoomInfo.cpp index 0bc1236..aa5f861 100644 --- a/GpApp/RoomInfo.cpp +++ b/GpApp/RoomInfo.cpp @@ -68,7 +68,7 @@ Boolean originalFloor; extern IGpCursor *handCursor; extern short lastBackground; -extern PortabilityLayer::ResourceArchive *houseResFork; +extern PortabilityLayer::IResourceArchive *houseResFork; //============================================================== Functions diff --git a/GpApp/SelectHouse.cpp b/GpApp/SelectHouse.cpp index a63dc53..0e4af09 100644 --- a/GpApp/SelectHouse.cpp +++ b/GpApp/SelectHouse.cpp @@ -101,7 +101,7 @@ void UpdateLoadDialog (Dialog *theDialog) if (SectRect(&dialogRect, &tempRect, &dummyRect)) { - PortabilityLayer::ResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name); + PortabilityLayer::IResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name); if (resFile != nullptr) { if (!LargeIconPlot(surface, resFile, -16455, tempRect)) diff --git a/GpApp/Sound.cpp b/GpApp/Sound.cpp index f60c35d..9eb5eff 100644 --- a/GpApp/Sound.cpp +++ b/GpApp/Sound.cpp @@ -367,27 +367,6 @@ void KillSound (void) DumpBufferSounds(); } -//-------------------------------------------------------------- SoundBytesNeeded - -long SoundBytesNeeded (void) -{ - long totalBytes; - short i; - - totalBytes = 0L; - for (i = 0; i < kMaxSounds - 1; i++) - { - size_t resSize = 0; - if (!PortabilityLayer::ResourceManager::GetInstance()->GetAppResourceArchive()->GetResourceSize('snd ', i + kBaseBufferSoundID, resSize)) - return -1; - - totalBytes += static_cast(resSize); -// ReleaseResource(theSound); - } - - return totalBytes; -} - //-------------------------------------------------------------- TellHerNoSounds void TellHerNoSounds (void) diff --git a/GpApp/Utilities.cpp b/GpApp/Utilities.cpp index afbd359..6051b16 100644 --- a/GpApp/Utilities.cpp +++ b/GpApp/Utilities.cpp @@ -330,7 +330,7 @@ void LoadScaledGraphicCustom(DrawSurface *surface, short resID, Rect *theRect) //-------------------------------------------------------------- LargeIconPlot // Draws a standard b&w icon (32 x 32) - resource is an 'ICON'. -bool LargeIconPlot (DrawSurface *surface, PortabilityLayer::ResourceArchive *resFile, short resID, const Rect &theRect) +bool LargeIconPlot (DrawSurface *surface, PortabilityLayer::IResourceArchive *resFile, short resID, const Rect &theRect) { Handle hdl = resFile->LoadResource('icl8', resID); if (hdl) diff --git a/PortabilityLayer/GPArchive.cpp b/PortabilityLayer/GPArchive.cpp index 17d9672..765dbb9 100644 --- a/PortabilityLayer/GPArchive.cpp +++ b/PortabilityLayer/GPArchive.cpp @@ -25,6 +25,11 @@ static const char *gs_forbiddenNames[] = "LPT9", }; +static bool IsCharForbidden(char c) +{ + return (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$' || c == '#'); +} + namespace PortabilityLayer { GpArcResourceTypeTag GpArcResourceTypeTag::Encode(const ResTypeID &tag) @@ -42,7 +47,7 @@ namespace PortabilityLayer { char c = chars[i]; - bool isForbidden = (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$'); + bool isForbidden = IsCharForbidden(c); if (i == 3) { @@ -81,4 +86,57 @@ namespace PortabilityLayer return output; } + + bool GpArcResourceTypeTag::Load(const char *str) + { + size_t l = strlen(str); + if (l < sizeof(m_id)) + { + memcpy(m_id, str, l); + m_id[l] = '\0'; + return true; + } + + return false; + } + + bool GpArcResourceTypeTag::Decode(ResTypeID &outTag) + { + char decodedChars[4]; + size_t parseOffset = 0; + for (int i = 0; i < 4; i++) + { + char parseChar = m_id[parseOffset]; + if (parseChar == '\0') + return false; + + if (parseChar == '$') + { + char nibbles[2] = { m_id[parseOffset + 1], m_id[parseOffset + 2] }; + parseOffset += 2; + + int decodedNibbles[2]; + + for (int n = 0; n < 2; n++) + { + char nibble = nibbles[n]; + if (nibble >= '0' && nibble <= '9') + decodedNibbles[n] = nibble - '0'; + else if (nibble >= 'a' && nibble <= 'f') + decodedNibbles[n] = nibble - 'a' + 0xa; + else + return false; + } + + decodedChars[i] = (decodedNibbles[0] << 4) | (decodedNibbles[1]); + } + else + decodedChars[i] = parseChar; + + parseOffset++; + } + + outTag = ResTypeID(decodedChars); + return true; + } } diff --git a/PortabilityLayer/GPArchive.h b/PortabilityLayer/GPArchive.h index 35d9354..16915ee 100644 --- a/PortabilityLayer/GPArchive.h +++ b/PortabilityLayer/GPArchive.h @@ -9,5 +9,8 @@ namespace PortabilityLayer char m_id[13]; static GpArcResourceTypeTag Encode(const ResTypeID &tag); + + bool Load(const char *str); + bool Decode(ResTypeID &outTag); }; } diff --git a/PortabilityLayer/HostFileSystem.h b/PortabilityLayer/HostFileSystem.h index 506e9df..daef277 100644 --- a/PortabilityLayer/HostFileSystem.h +++ b/PortabilityLayer/HostFileSystem.h @@ -16,17 +16,32 @@ namespace PortabilityLayer public: virtual bool FileExists(VirtualDirectory_t virtualDirectory, const char *path) = 0; virtual bool FileLocked(VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0; - virtual GpIOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0; + virtual GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0; virtual bool DeleteFile(VirtualDirectory_t virtualDirectory, const char *path, bool &existed) = 0; - virtual HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory) = 0; + virtual HostDirectoryCursor *ScanDirectoryNested(VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) = 0; + HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory); virtual bool ValidateFilePath(const char *path, size_t pathLen) const = 0; virtual bool ValidateFilePathUnicodeChar(uint32_t ch) const = 0; + virtual bool IsVirtualDirectoryLooseResources(VirtualDirectory_t virtualDir) const = 0; + static HostFileSystem *GetInstance(); static void SetInstance(HostFileSystem *instance); + GpIOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition); + private: static HostFileSystem *ms_instance; }; } + +inline GpIOStream *PortabilityLayer::HostFileSystem::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) +{ + return this->OpenFileNested(virtualDirectory, &path, 1, writeAccess, createDisposition); +} + +inline PortabilityLayer::HostDirectoryCursor *PortabilityLayer::HostFileSystem::ScanDirectory(VirtualDirectory_t virtualDirectory) +{ + return this->ScanDirectoryNested(virtualDirectory, nullptr, 0); +} diff --git a/PortabilityLayer/HostSystemServices.h b/PortabilityLayer/HostSystemServices.h index 4c971c0..4268836 100644 --- a/PortabilityLayer/HostSystemServices.h +++ b/PortabilityLayer/HostSystemServices.h @@ -1,6 +1,4 @@ #pragma once -#ifndef __PL_HOST_SYSTEM_SERVICES_H__ -#define __PL_HOST_SYSTEM_SERVICES_H__ #include @@ -34,5 +32,3 @@ namespace PortabilityLayer static HostSystemServices *ms_instance; }; } - -#endif diff --git a/PortabilityLayer/PLMovies.h b/PortabilityLayer/PLMovies.h index 7e5652e..9e4e5a5 100644 --- a/PortabilityLayer/PLMovies.h +++ b/PortabilityLayer/PLMovies.h @@ -7,7 +7,7 @@ namespace PortabilityLayer { - class ResourceArchive; + struct IResourceArchive; } struct DrawSurface; @@ -52,7 +52,7 @@ private: ~AnimationPackage(); THandle *m_images; - PortabilityLayer::ResourceArchive *m_resArchive; + PortabilityLayer::IResourceArchive *m_resArchive; size_t m_numImages; uint32_t m_frameRateNumerator; diff --git a/PortabilityLayer/PLResourceManager.cpp b/PortabilityLayer/PLResourceManager.cpp index 6e2846a..66aaffb 100644 --- a/PortabilityLayer/PLResourceManager.cpp +++ b/PortabilityLayer/PLResourceManager.cpp @@ -1,8 +1,10 @@ #include "ResourceManager.h" +#include "BinarySearch.h" #include "BMPFormat.h" #include "FileManager.h" #include "GPArchive.h" +#include "HostDirectoryCursor.h" #include "HostFileSystem.h" #include "HostMemoryBuffer.h" #include "GpIOStream.h" @@ -21,6 +23,7 @@ #include "ZipFile.h" #include +#include namespace ResourceValidationRules { @@ -97,7 +100,7 @@ namespace namespace PortabilityLayer { struct MMHandleBlock; - class ResourceArchive; + struct IResourceArchive; class ResourceManagerImpl final : public ResourceManager { @@ -108,9 +111,9 @@ namespace PortabilityLayer void Shutdown() override; THandle GetAppResource(const ResTypeID &resTypeID, int16_t resID) const override; - ResourceArchive *GetAppResourceArchive() const override; + IResourceArchive *GetAppResourceArchive() const override; - ResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const override; + IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const override; PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) override; void DissociateHandle(MMHandleBlock *hdl) const override; @@ -119,9 +122,11 @@ namespace PortabilityLayer static ResourceManagerImpl *GetInstance(); private: - void UnloadAndDestroyResourceFile(ResourceArchive *rf); + void UnloadAndDestroyResourceFile(IResourceArchive *rf); - ResourceArchive *m_appResArchive; + IResourceArchive *LoadResDirectory(VirtualDirectory_t virtualDir, const PLPasStr &filename) const; + + IResourceArchive *m_appResArchive; static ResourceManagerImpl ms_instance; }; @@ -157,18 +162,36 @@ namespace PortabilityLayer return hdl->m_rmSelfRef; } - void ResourceManagerImpl::UnloadAndDestroyResourceFile(ResourceArchive *rf) + void ResourceManagerImpl::UnloadAndDestroyResourceFile(IResourceArchive *rf) { rf->Destroy(); } + IResourceArchive *ResourceManagerImpl::LoadResDirectory(VirtualDirectory_t virtualDir, const PLPasStr &filename) const + { + ResourceArchiveDirectory *archive = ResourceArchiveDirectory::Create(virtualDir, filename); + if (!archive) + return nullptr; + + if (!archive->Init()) + { + archive->Destroy(); + return nullptr; + } + + return archive; + } + ResourceManagerImpl *ResourceManagerImpl::GetInstance() { return &ms_instance; } - ResourceArchive *ResourceManagerImpl::LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const + IResourceArchive *ResourceManagerImpl::LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const { + if (PortabilityLayer::HostFileSystem::GetInstance()->IsVirtualDirectoryLooseResources(virtualDir)) + return LoadResDirectory(virtualDir, filename); + GpIOStream *fStream = nullptr; if (FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, fStream) != PLErrors::kNone) return nullptr; @@ -180,7 +203,7 @@ namespace PortabilityLayer return nullptr; } - ResourceArchive *archive = ResourceArchive::Create(proxy, fStream); + IResourceArchive *archive = ResourceArchiveZipFile::Create(proxy, fStream); if (!archive) { proxy->Destroy(); @@ -227,7 +250,7 @@ namespace PortabilityLayer return m_appResArchive->LoadResource(resType, resID); } - ResourceArchive *ResourceManagerImpl::GetAppResourceArchive() const + IResourceArchive *ResourceManagerImpl::GetAppResourceArchive() const { return m_appResArchive; } @@ -238,6 +261,31 @@ namespace PortabilityLayer // =========================================================================================== + const char *ResourceArchiveBase::GetFileExtensionForResType(const ResTypeID &resTypeID, int &outValidationRule) + { + const char *extension = ".bin"; + outValidationRule = ResourceValidationRules::kNone; + + if (resTypeID == ResTypeID('snd ')) + { + extension = ".wav"; + outValidationRule = ResourceValidationRules::kWAV; + } + else if (resTypeID == ResTypeID('Date') || resTypeID == ResTypeID('PICT')) + { + extension = kPICTExtension; + outValidationRule = ResourceValidationRules::kBMP; + } + else if (resTypeID == ResTypeID('STR#')) + extension = ".txt"; + else if (resTypeID == ResTypeID('DITL') || resTypeID == ResTypeID('muvi')) + extension = ".json"; + + return extension; + } + + // =========================================================================================== + ResourceArchiveRef::ResourceArchiveRef() : m_handle(nullptr) , m_size(0) @@ -245,7 +293,9 @@ namespace PortabilityLayer { } - ResourceArchive *ResourceArchive::Create(ZipFileProxy *zipFileProxy, GpIOStream *stream) + // =========================================================================================== + + ResourceArchiveZipFile *ResourceArchiveZipFile::Create(ZipFileProxy *zipFileProxy, GpIOStream *stream) { PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); @@ -262,39 +312,28 @@ namespace PortabilityLayer new (refs + i) ResourceArchiveRef(); } - void *storage = mm->Alloc(sizeof(ResourceArchive)); + void *storage = mm->Alloc(sizeof(ResourceArchiveZipFile)); if (!storage) { mm->Release(refs); return nullptr; } - return new (storage) ResourceArchive(zipFileProxy, stream, refs); + return new (storage) ResourceArchiveZipFile(zipFileProxy, stream, refs); } - void ResourceArchive::Destroy() + void ResourceArchiveZipFile::Destroy() { - this->~ResourceArchive(); + this->~ResourceArchiveZipFile(); PortabilityLayer::MemoryManager::GetInstance()->Release(this); } - THandle ResourceArchive::LoadResource(const ResTypeID &resTypeID, int id) + THandle ResourceArchiveZipFile::LoadResource(const ResTypeID &resTypeID, int id) { return GetResource(resTypeID, id, true); } - bool ResourceArchive::GetResourceSize(const ResTypeID &resTypeID, int id, size_t &outSize) const - { - size_t index = 0; - int validationRule = 0; - if (!IndexResource(resTypeID, id, index, validationRule)) - return false; - - outSize = m_zipFileProxy->GetFileSize(index); - return true; - } - - bool ResourceArchive::HasAnyResourcesOfType(const ResTypeID &resTypeID) const + bool ResourceArchiveZipFile::HasAnyResourcesOfType(const ResTypeID &resTypeID) const { char resPrefix[6]; resTypeID.ExportAsChars(resPrefix); @@ -304,7 +343,7 @@ namespace PortabilityLayer return m_zipFileProxy->HasPrefix(resPrefix); } - bool ResourceArchive::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const + bool ResourceArchiveZipFile::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const { char resPrefix[6]; resTypeID.ExportAsChars(resPrefix); @@ -409,25 +448,9 @@ namespace PortabilityLayer return haveAny; } - bool ResourceArchive::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const + bool ResourceArchiveZipFile::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const { - const char *extension = ".bin"; - outValidationRule = ResourceValidationRules::kNone; - - if (resTypeID == ResTypeID('snd ')) - { - extension = ".wav"; - outValidationRule = ResourceValidationRules::kWAV; - } - else if (resTypeID == ResTypeID('Date') || resTypeID == ResTypeID('PICT')) - { - extension = kPICTExtension; - outValidationRule = ResourceValidationRules::kBMP; - } - else if (resTypeID == ResTypeID('STR#')) - extension = ".txt"; - else if (resTypeID == ResTypeID('DITL') || resTypeID == ResTypeID('muvi')) - extension = ".json"; + const char *extension = GetFileExtensionForResType(resTypeID, outValidationRule); char resourceFile[64]; @@ -439,7 +462,7 @@ namespace PortabilityLayer return m_zipFileProxy->IndexFile(resourceFile, outIndex); } - THandle ResourceArchive::GetResource(const ResTypeID &resTypeID, int id, bool load) + THandle ResourceArchiveZipFile::GetResource(const ResTypeID &resTypeID, int id, bool load) { int validationRule = 0; size_t index = 0; @@ -485,14 +508,14 @@ namespace PortabilityLayer return THandle(handle); } - ResourceArchive::ResourceArchive(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles) + ResourceArchiveZipFile::ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles) : m_zipFileProxy(zipFileProxy) , m_stream(stream) , m_resourceHandles(resourceHandles) { } - ResourceArchive::~ResourceArchive() + ResourceArchiveZipFile::~ResourceArchiveZipFile() { MemoryManager *mm = MemoryManager::GetInstance(); @@ -511,4 +534,333 @@ namespace PortabilityLayer m_zipFileProxy->Destroy(); m_stream->Close(); } + + // ======================================================================================== + + ResourceArchiveDirectory *ResourceArchiveDirectory::Create(VirtualDirectory_t directory, const PLPasStr &subdirectory) + { + void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(ResourceArchiveDirectory)); + if (!storage) + return nullptr; + + return new (storage) ResourceArchiveDirectory(directory, subdirectory); + } + + void ResourceArchiveDirectory::Destroy() + { + this->~ResourceArchiveDirectory(); + PortabilityLayer::MemoryManager::GetInstance()->Release(this); + } + + THandle ResourceArchiveDirectory::LoadResource(const ResTypeID &resTypeID, int id) + { + return GetResource(resTypeID, id, true); + } + + bool ResourceArchiveDirectory::HasAnyResourcesOfType(const ResTypeID &resTypeID) const + { + int16_t scratch; + return FindFirstResourceOfType(resTypeID, scratch); + } + + bool ResourceArchiveDirectory::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const + { + int32_t resID32 = resTypeID.ExportAsInt32(); + + const ResTypeEntry *firstTypeEntry = *m_resTypes; + const ResTypeEntry *lastTypeEntry = firstTypeEntry + m_numResourceTypes; + + const ResTypeEntry *entry = BinarySearch(firstTypeEntry, lastTypeEntry, resID32, ResourceArchiveDirectory::ResTypeSearchPredicate); + + if (entry == lastTypeEntry) + return false; + + outID = (*m_resIDs)[entry->m_firstRes]; + return true; + } + + bool ResourceArchiveDirectory::Init() + { + PortabilityLayer::HostFileSystem *fs = PortabilityLayer::HostFileSystem::GetInstance(); + + const char *typePaths[1] = { this->m_subdirectory }; + + PortabilityLayer::HostDirectoryCursor *typeDirCursor = fs->ScanDirectoryNested(m_directory, typePaths, 1); + if (!typeDirCursor) + return false; + + PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); + + m_resTypes = THandle(mm->AllocHandle(0)); + if (!m_resTypes) + return false; + + m_resIDs = THandle(mm->AllocHandle(0)); + if (!m_resIDs) + return false; + + size_t resTypeCapacity = 0; + size_t resIDCapacity = 0; + + const char *typeScanFilename = nullptr; + while (typeDirCursor->GetNext(typeScanFilename)) + { + GpArcResourceTypeTag resourceTypeTag; + if (!resourceTypeTag.Load(typeScanFilename)) + continue; + + ResTypeID resTypeID; + if (!resourceTypeTag.Decode(resTypeID)) + continue; + + int32_t dirResType = resTypeID.ExportAsInt32(); + + ResTypeEntry rte; + rte.m_resTypeID = dirResType; + rte.m_firstRes = m_numResources; + rte.m_lastRes = m_numResources; + + const char *idScanFilenames[2] = { this->m_subdirectory, typeScanFilename }; + PortabilityLayer::HostDirectoryCursor *typeIDCursor = fs->ScanDirectoryNested(m_directory, idScanFilenames, 2); + if (!typeIDCursor) + continue; + + const char *idScanFilename = nullptr; + while (typeIDCursor->GetNext(idScanFilename)) + { + int resID = 0; + bool isNegative = false; + + for (size_t chi = 0; idScanFilename[chi] != '.' && idScanFilename[chi] != '\0'; chi++) + { + char ch = idScanFilename[chi]; + if (ch == '-') + isNegative = true; + else if (ch >= '0' && ch <= '9') + { + resID *= 10; + int digit = ch - '0'; + if (isNegative) + resID -= digit; + else + resID += digit; + } + else + break; + } + + if (m_numResources == resIDCapacity) + { + const size_t oldCapacity = resIDCapacity; + + resIDCapacity *= 2; + if (resIDCapacity == 0) + resIDCapacity = 1; + + if (!mm->ResizeHandle(m_resIDs.MMBlock(), sizeof(int16_t) * resIDCapacity)) + { + typeIDCursor->Destroy(); + typeDirCursor->Destroy(); + return false; + } + } + + (*m_resIDs)[m_numResources] = resID; + m_numResources++; + } + + typeIDCursor->Destroy(); + rte.m_lastRes = m_numResources; + + if (m_numResourceTypes == resTypeCapacity) + { + const size_t oldCapacity = resTypeCapacity; + + resTypeCapacity *= 2; + if (resTypeCapacity == 0) + resTypeCapacity = 1; + + if (!mm->ResizeHandle(m_resTypes.MMBlock(), sizeof(ResTypeEntry) * resTypeCapacity)) + { + typeDirCursor->Destroy(); + return false; + } + } + + (*m_resTypes)[m_numResourceTypes] = rte; + m_numResourceTypes++; + } + + mm->ResizeHandle(m_resTypes.MMBlock(), sizeof(ResTypeEntry) * m_numResourceTypes); + mm->ResizeHandle(m_resIDs.MMBlock(), sizeof(int16_t) * m_numResources); + + ResTypeEntry *resTypes = *m_resTypes; + int16_t *resIDs = *m_resIDs; + + std::sort(resTypes, resTypes + m_numResourceTypes, ResourceArchiveDirectory::ResTypeEntrySortPredicate); + + for (size_t i = 0; i < m_numResourceTypes; i++) + { + int16_t *resIDStart = resIDs + resTypes[i].m_firstRes; + int16_t *resIDEnd = resIDs + resTypes[i].m_lastRes; + + std::sort(resIDStart, resIDEnd); + } + + m_resourceHandles = static_cast(mm->Alloc(sizeof(ResourceArchiveRef) * m_numResources)); + if (!m_resourceHandles) + return false; + + for (size_t i = 0; i < m_numResources; i++) + new (m_resourceHandles + i) ResourceArchiveRef(); + + return true; + } + + bool ResourceArchiveDirectory::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex) const + { + int32_t resID32 = resTypeID.ExportAsInt32(); + + const ResTypeEntry *firstTypeEntry = *m_resTypes; + const ResTypeEntry *lastTypeEntry = firstTypeEntry + m_numResourceTypes; + + const ResTypeEntry *entry = BinarySearch(firstTypeEntry, lastTypeEntry, resID32, ResourceArchiveDirectory::ResTypeSearchPredicate); + + if (entry == lastTypeEntry) + return false; + + const int16_t *resIDs = *m_resIDs; + const int16_t *firstRes = resIDs + entry->m_firstRes; + const int16_t *lastRes = resIDs + entry->m_lastRes; + + const int16_t *idLoc = BinarySearch(firstRes, lastRes, static_cast(id), ResourceArchiveDirectory::ResIDSearchPredicate); + if (idLoc == lastRes) + return false; + + outIndex = static_cast(idLoc - resIDs); + + return true; + } + + THandle ResourceArchiveDirectory::GetResource(const ResTypeID &resTypeID, int id, bool load) + { + + int validationRule = 0; + size_t index = 0; + if (!IndexResource(resTypeID, id, index)) + return THandle(); + + ResourceArchiveRef *ref = m_resourceHandles + index; + + MMHandleBlock *handle = nullptr; + if (ref->m_handle != nullptr) + handle = ref->m_handle; + else + { + handle = MemoryManager::GetInstance()->AllocHandle(0); + if (!handle) + return THandle(); + + handle->m_rmSelfRef = ref; + ref->m_handle = handle; + ref->m_resID = static_cast(id); + ref->m_size = 0; + } + + if (handle->m_contents == nullptr && load) + { + int validationRule = 0; + const char *extension = GetFileExtensionForResType(resTypeID, validationRule); + + GpArcResourceTypeTag resTypeTag = GpArcResourceTypeTag::Encode(resTypeID); + char fileName[32]; + + snprintf(fileName, sizeof(fileName) - 1, "%i%s", id, extension); + + const char *paths[3] = { m_subdirectory, resTypeTag.m_id, fileName }; + + GpIOStream *ioStream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFileNested(m_directory, paths, 3, false, GpFileCreationDispositions::kOpenExisting); + if (!ioStream) + return THandle(); + + size_t size = ioStream->Size(); + + void *contents = MemoryManager::GetInstance()->Alloc(size); + handle->m_contents = contents; + handle->m_size = size; + ref->m_size = size; + + bool readOK = (ioStream->Read(contents, size)); + ioStream->Close(); + + if (!readOK || (validationRule != ResourceValidationRules::kNone && !ValidateResource(contents, ref->m_size, static_cast(validationRule)))) + { + MemoryManager::GetInstance()->Release(contents); + handle->m_contents = nullptr; + handle->m_size = 0; + ref->m_size = 0; + + return THandle(); + } + } + + return THandle(handle); + } + + int ResourceArchiveDirectory::ResTypeSearchPredicate(int32_t resTypeID, const ResTypeEntry &entry) + { + if (resTypeID < entry.m_resTypeID) + return -1; + if (resTypeID > entry.m_resTypeID) + return 1; + return 0; + } + + int ResourceArchiveDirectory::ResIDSearchPredicate(int16_t resTypeID, int16_t entry) + { + if (resTypeID < entry) + return -1; + if (resTypeID > entry) + return 1; + return 0; + } + + bool ResourceArchiveDirectory::ResTypeEntrySortPredicate(const ResTypeEntry &a, const ResTypeEntry &b) + { + return a.m_resTypeID < b.m_resTypeID; + } + + ResourceArchiveDirectory::ResourceArchiveDirectory(VirtualDirectory_t directory, const PLPasStr &subdirectory) + : m_directory(directory) + , m_numResourceTypes(0) + , m_resourceHandles(nullptr) + , m_numResources(0) + { + memcpy(m_subdirectory, subdirectory.UChars(), subdirectory.Length()); + m_subdirectory[subdirectory.Length()] = '\0'; + } + + ResourceArchiveDirectory::~ResourceArchiveDirectory() + { + MemoryManager *mm = MemoryManager::GetInstance(); + + const size_t numHandles = m_numResources; + + if (m_resourceHandles) + { + for (size_t i = 0; i < numHandles; i++) + { + ResourceArchiveRef &ref = m_resourceHandles[numHandles - 1 - i]; + if (ref.m_handle) + mm->ReleaseHandle(ref.m_handle); + + ref.~ResourceArchiveRef(); + } + } + + mm->Release(m_resourceHandles); + + m_resIDs.Dispose(); + m_resTypes.Dispose(); + } } diff --git a/PortabilityLayer/ResTypeID.h b/PortabilityLayer/ResTypeID.h index 67c71bf..9cf7104 100644 --- a/PortabilityLayer/ResTypeID.h +++ b/PortabilityLayer/ResTypeID.h @@ -19,6 +19,7 @@ namespace PortabilityLayer bool operator!=(const ResTypeID &other) const; void ExportAsChars(char *chars) const; + int32_t ExportAsInt32() const; private: char m_id[4]; @@ -70,6 +71,11 @@ namespace PortabilityLayer { memcpy(chars, m_id, 4); } + + inline int32_t ResTypeID::ExportAsInt32() const + { + return ResTypeIDCodec::Decode(m_id); + } } #endif diff --git a/PortabilityLayer/ResourceManager.h b/PortabilityLayer/ResourceManager.h index 1d857fa..b8fed7b 100644 --- a/PortabilityLayer/ResourceManager.h +++ b/PortabilityLayer/ResourceManager.h @@ -1,6 +1,7 @@ #pragma once #include "VirtualDirectory.h" +#include "PascalStr.h" #include "PLErrorCodes.h" #include "PLHandle.h" @@ -25,21 +26,78 @@ namespace PortabilityLayer int16_t m_resID; }; - class ResourceArchive final + struct IResourceArchive + { + virtual void Destroy() = 0; + + virtual THandle LoadResource(const ResTypeID &resTypeID, int id) = 0; + + virtual bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const = 0; + virtual bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const = 0; + }; + + class ResourceArchiveBase : public IResourceArchive { public: - static ResourceArchive *Create(ZipFileProxy *zipFileProxy, GpIOStream *stream); - void Destroy(); + static const char *GetFileExtensionForResType(const ResTypeID &resTypeID, int &outValidationRule); + }; - THandle LoadResource(const ResTypeID &resTypeID, int id); - bool GetResourceSize(const ResTypeID &resTypeID, int id, size_t &outSize) const; + class ResourceArchiveDirectory final : public ResourceArchiveBase + { + public: + static ResourceArchiveDirectory *Create(VirtualDirectory_t directory, const PLPasStr &subdirectory); + void Destroy() override; - bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const; - bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const; + THandle LoadResource(const ResTypeID &resTypeID, int id) override; + + bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const override; + bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const override; + + bool Init(); private: - ResourceArchive(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles); - ~ResourceArchive(); + ResourceArchiveDirectory(VirtualDirectory_t directory, const PLPasStr &subdirectory); + ~ResourceArchiveDirectory(); + + struct ResTypeEntry + { + int32_t m_resTypeID; + size_t m_firstRes; + size_t m_lastRes; + }; + + bool IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex) const; + THandle GetResource(const ResTypeID &resTypeID, int id, bool load); + + static int ResTypeSearchPredicate(int32_t resTypeID, const ResTypeEntry &entry); + static int ResIDSearchPredicate(int16_t resTypeID, int16_t entry); + static bool ResTypeEntrySortPredicate(const ResTypeEntry &a, const ResTypeEntry &b); + + VirtualDirectory_t m_directory; + char m_subdirectory[256]; + + THandle m_resTypes; + size_t m_numResourceTypes; + + THandle m_resIDs; + ResourceArchiveRef *m_resourceHandles; + size_t m_numResources; + }; + + class ResourceArchiveZipFile final : public ResourceArchiveBase + { + public: + static ResourceArchiveZipFile *Create(ZipFileProxy *zipFileProxy, GpIOStream *stream); + void Destroy() override; + + THandle LoadResource(const ResTypeID &resTypeID, int id) override; + + bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const override; + bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const override; + + private: + ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles); + ~ResourceArchiveZipFile(); bool IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const; @@ -57,9 +115,9 @@ namespace PortabilityLayer virtual void Shutdown() = 0; virtual THandle GetAppResource(const ResTypeID &resTypeID, int16_t resID) const = 0; - virtual ResourceArchive *GetAppResourceArchive() const = 0; + virtual IResourceArchive *GetAppResourceArchive() const = 0; - virtual ResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const = 0; + virtual IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const = 0; virtual PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0; virtual void DissociateHandle(MMHandleBlock *hdl) const = 0; diff --git a/PortabilityLayer/ScanlineMaskBuilder.cpp b/PortabilityLayer/ScanlineMaskBuilder.cpp index 14db444..96443df 100644 --- a/PortabilityLayer/ScanlineMaskBuilder.cpp +++ b/PortabilityLayer/ScanlineMaskBuilder.cpp @@ -3,7 +3,7 @@ #include namespace PortabilityLayer -{ +{ ScanlineMaskBuilder::ScanlineMaskBuilder() : m_spans(nullptr) , m_numSpans(0) @@ -11,11 +11,11 @@ namespace PortabilityLayer , m_longestSpan(0) { } - + ScanlineMaskBuilder::~ScanlineMaskBuilder() { if (m_spans) - realloc(m_spans, 0); + (void)realloc(m_spans, 0); } bool ScanlineMaskBuilder::AppendSpan(size_t span)