diff --git a/Aerofoil.sln b/Aerofoil.sln index 45534e4..01d3c32 100644 --- a/Aerofoil.sln +++ b/Aerofoil.sln @@ -60,6 +60,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpFontHandler_FreeType2", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AerofoilSDL", "AerofoilSDL\AerofoilSDL.vcxproj", "{33542FF0-0473-4802-BC79-3B8261790F65}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MergeGPF", "MergeGPF\MergeGPF.vcxproj", "{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -168,6 +170,10 @@ Global {33542FF0-0473-4802-BC79-3B8261790F65}.Debug|x64.Build.0 = Debug|x64 {33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.ActiveCfg = Release|x64 {33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.Build.0 = Release|x64 + {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.ActiveCfg = Debug|x64 + {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.Build.0 = Debug|x64 + {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.ActiveCfg = Release|x64 + {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Aerofoil/GpFileStream_Win32.cpp b/Aerofoil/GpFileStream_Win32.cpp index abdf4cc..b196094 100644 --- a/Aerofoil/GpFileStream_Win32.cpp +++ b/Aerofoil/GpFileStream_Win32.cpp @@ -99,24 +99,6 @@ bool GpFileStream_Win32::SeekEnd(GpUFilePos_t loc) return SetFilePointerEx(m_handle, li, nullptr, FILE_END) != 0; } -bool GpFileStream_Win32::Truncate(GpUFilePos_t loc) -{ - if (!m_writeable) - return false; - - GpUFilePos_t oldPos = Tell(); - if (!SeekStart(loc)) - return false; - - if (!SetEndOfFile(m_handle)) - return false; - - if (!SeekStart(oldPos)) - return false; - - return true; -} - GpUFilePos_t GpFileStream_Win32::Size() const { LARGE_INTEGER fsize; diff --git a/Aerofoil/GpFileStream_Win32.h b/Aerofoil/GpFileStream_Win32.h index fbe0753..fdedf17 100644 --- a/Aerofoil/GpFileStream_Win32.h +++ b/Aerofoil/GpFileStream_Win32.h @@ -17,7 +17,6 @@ public: bool SeekStart(GpUFilePos_t loc) override; bool SeekCurrent(GpFilePos_t loc) override; bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; GpUFilePos_t Size() const override; GpUFilePos_t Tell() const override; void Close() override; diff --git a/Aerofoil/GpFileSystem_Win32.cpp b/Aerofoil/GpFileSystem_Win32.cpp index c0e57c5..2367e22 100644 --- a/Aerofoil/GpFileSystem_Win32.cpp +++ b/Aerofoil/GpFileSystem_Win32.cpp @@ -191,24 +191,24 @@ bool GpFileSystem_Win32::FileExists(PortabilityLayer::VirtualDirectory_t virtual return PathFileExistsW(winPath) != 0; } -bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) +bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) { wchar_t winPath[MAX_PATH + 1]; if (!ResolvePath(virtualDirectory, &path, 1, winPath)) { - *exists = false; + exists = false; return false; } DWORD attribs = GetFileAttributesW(winPath); if (attribs == INVALID_FILE_ATTRIBUTES) { - *exists = false; + exists = false; return false; } - *exists = true; + exists = true; return (attribs & FILE_ATTRIBUTE_READONLY) != 0; } @@ -319,11 +319,6 @@ bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const return false; } -bool GpFileSystem_Win32::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const -{ - return false; -} - void GpFileSystem_Win32::SetMainThreadRelay(IGpThreadRelay *relay) { (void)relay; @@ -341,7 +336,7 @@ bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const if (c >= '0' && c <= '9') continue; - if (c == '_' || c == '.' || c == '\'') + if (c == '_' || c == '.' || c == '\'' || c == '!') continue; if (c == ' ' && i != 0 && i != length - 1) diff --git a/Aerofoil/GpFileSystem_Win32.h b/Aerofoil/GpFileSystem_Win32.h index 10ddfbf..80050a7 100644 --- a/Aerofoil/GpFileSystem_Win32.h +++ b/Aerofoil/GpFileSystem_Win32.h @@ -13,7 +13,7 @@ public: GpFileSystem_Win32(); bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override; - bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override; + bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) 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; IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override; @@ -21,8 +21,6 @@ public: 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; - void SetMainThreadRelay(IGpThreadRelay *relay) override; void SetDelayCallback(DelayCallback_t delayCallback) override; diff --git a/AerofoilAndroid/app/build.gradle b/AerofoilAndroid/app/build.gradle index 199fce0..99d6ff8 100644 --- a/AerofoilAndroid/app/build.gradle +++ b/AerofoilAndroid/app/build.gradle @@ -66,7 +66,7 @@ android { } aaptOptions { - noCompress 'gpa' + noCompress 'gpf' } } diff --git a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp index ef58eff..a6a56dd 100644 --- a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp @@ -42,7 +42,6 @@ public: bool SeekStart(GpUFilePos_t loc) override; bool SeekCurrent(GpFilePos_t loc) override; bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; GpUFilePos_t Size() const override; GpUFilePos_t Tell() const override; void Close() override; @@ -70,7 +69,6 @@ public: bool SeekStart(GpUFilePos_t loc) override; bool SeekCurrent(GpFilePos_t loc) override; bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; GpUFilePos_t Size() const override; GpUFilePos_t Tell() const override; void Close() override; @@ -96,7 +94,6 @@ public: bool SeekStart(GpUFilePos_t loc) override; bool SeekCurrent(GpFilePos_t loc) override; bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; GpUFilePos_t Size() const override; GpUFilePos_t Tell() const override; void Close() override; @@ -168,11 +165,6 @@ bool GpFileStream_PFD::SeekEnd(GpUFilePos_t loc) return lseek64(m_fd, loc, SEEK_END) >= 0; } -bool GpFileStream_PFD::Truncate(GpUFilePos_t loc) -{ - return ftruncate64(m_fd, static_cast(loc)) >= 0; -} - GpUFilePos_t GpFileStream_PFD::Size() const { struct stat64 s; @@ -251,11 +243,6 @@ bool GpFileStream_SDLRWops::SeekEnd(GpUFilePos_t loc) return m_rw->seek(m_rw, -static_cast(loc), RW_SEEK_END) >= 0; } -bool GpFileStream_SDLRWops::Truncate(GpUFilePos_t loc) -{ - return false; -} - GpUFilePos_t GpFileStream_SDLRWops::Size() const { return m_rw->size(m_rw); @@ -346,12 +333,6 @@ bool GpFileStream_Android_File::SeekEnd(GpUFilePos_t loc) return lseek64(m_fd, -static_cast(loc), SEEK_END) >= 0; } -bool GpFileStream_Android_File::Truncate(GpUFilePos_t loc) -{ - fflush(m_f); - return ftruncate64(m_fd, static_cast(loc)) >= 0; -} - GpUFilePos_t GpFileStream_Android_File::Size() const { fflush(m_f); @@ -552,26 +533,26 @@ bool GpFileSystem_Android::FileExists(PortabilityLayer::VirtualDirectory_t virtu return stat(resolvedPath.c_str(), &s) == 0; } -bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) +bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) { std::string resolvedPath; bool isAsset; if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset)) { if (exists) - *exists = false; + exists = false; return false; } if (isAsset) { if (exists) - *exists = this->FileExists(virtualDirectory, path); + exists = this->FileExists(virtualDirectory, path); return true; } int permissions = access(resolvedPath.c_str(), W_OK | F_OK); - *exists = ((permissions & F_OK) != 0); + exists = ((permissions & F_OK) != 0); return ((permissions & W_OK) != 0); } @@ -771,11 +752,6 @@ bool GpFileSystem_Android::ValidateFilePathUnicodeChar(uint32_t c) const return false; } -bool GpFileSystem_Android::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const -{ - return false; -} - void GpFileSystem_Android::SetMainThreadRelay(IGpThreadRelay *relay) { m_relay = relay; diff --git a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h index 64b8b9e..10dbded 100644 --- a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h +++ b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h @@ -19,7 +19,7 @@ public: void ShutdownJNI(); bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override; - bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override; + bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) override; GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override; bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override; IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override; @@ -27,8 +27,6 @@ public: bool ValidateFilePath(const char *path, size_t pathLen) const override; bool ValidateFilePathUnicodeChar(uint32_t ch) const override; - bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const override; - void SetMainThreadRelay(IGpThreadRelay *relay) override; void SetDelayCallback(DelayCallback_t delayCallback) override; diff --git a/AerofoilAndroid/copy_packaged_resources.bat b/AerofoilAndroid/copy_packaged_resources.bat index e9e69fb..08631e9 100644 --- a/AerofoilAndroid/copy_packaged_resources.bat +++ b/AerofoilAndroid/copy_packaged_resources.bat @@ -7,6 +7,6 @@ mkdir Packaged cd Packaged rmdir /S /Q Houses mkdir Houses -copy ..\..\..\..\..\..\Packaged\*.gpa .\ +copy ..\..\..\..\..\..\Packaged\*.gpf .\ copy ..\..\..\..\..\..\Packaged\Houses\* Houses\ cd .. diff --git a/ConvertResources.bat b/ConvertResources.bat index 37da575..4608d53 100644 --- a/ConvertResources.bat +++ b/ConvertResources.bat @@ -2,14 +2,17 @@ rmdir /S /Q Packaged mkdir Packaged mkdir Packaged\Houses -mkdir Packaged\WinCursors x64\Release\MiniRez.exe "GliderProData\Glider PRO.r" Packaged\ApplicationResources.gpr x64\Release\gpr2gpa.exe "Packaged\ApplicationResources.gpr" "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpa" "ApplicationResourcePatches\manifest.json" +x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpf" data ozm5 0 0 locked + +x64\Release\MergeGPF.exe "Packaged\ApplicationResources.gpf" x64\Release\ConvertColorCursors.exe +attrib -R Packaged\ApplicationResources.gpf attrib -R Packaged\Houses\* x64\Release\hqx2gp.exe "GliderProData\Houses\Art Museum.binhex" "DefaultTimestamp.timestamp" "Packaged\Houses\Art Museum" @@ -70,11 +73,52 @@ x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\SpacePods x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\Teddy World.mov.gpf" MooV ozm5 0 0 locked x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\Titanic.mov.gpf" MooV ozm5 0 0 locked -del /Q Packaged\Houses\*.gpr -del /Q Packaged\ApplicationResources.gpr - copy /Y GliderProData\ConvertedMovies\*.mov.gpa Packaged\Houses\ +x64\Release\MergeGPF.exe "Packaged\Houses\Art Museum.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\California or Bust!.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Castle o' the Air.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\CD Demo House.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Davis Station.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Demo House.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Fun House.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Grand Prix.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\ImagineHouse PRO II.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\In The Mirror.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Land of Illusion.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Leviathan.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Metropolis.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Nemo's Market.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Rainbow's End.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Slumberland.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\SpacePods.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Teddy World.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\The Asylum Pro.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Titanic.gpf" + +x64\Release\MergeGPF.exe "Packaged\Houses\Art Museum.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Castle o' the Air.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\CD Demo House.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Davis Station.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Demo House.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Grand Prix.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\ImagineHouse PRO II.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Land of Illusion.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Leviathan.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Nemo's Market.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Rainbow's End.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Slumberland.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\SpacePods.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Teddy World.mov.gpf" +x64\Release\MergeGPF.exe "Packaged\Houses\Titanic.mov.gpf" + +del /Q Packaged\Houses\*.gpr +del /Q Packaged\Houses\*.gpa +del /Q Packaged\Houses\*.gpd +del /Q Packaged\ApplicationResources.gpr +del /Q Packaged\ApplicationResources.gpa + attrib +R Packaged\Houses\* +attrib +R Packaged\ApplicationResources.gpf pause diff --git a/FTagData/FTagData.cpp b/FTagData/FTagData.cpp index b067acc..274ba06 100644 --- a/FTagData/FTagData.cpp +++ b/FTagData/FTagData.cpp @@ -2,6 +2,8 @@ #include #include #include "MacFileInfo.h" +#include "CFileStream.h" +#include "CombinedTimestamp.h" int main(int argc, const char **argv) { @@ -29,19 +31,16 @@ int main(int argc, const char **argv) FILE *tsF = nullptr; errno_t ferr = fopen_s(&tsF, timestampPath.c_str(), "rb"); int64_t timestamp = 0; + PortabilityLayer::CombinedTimestamp ts; if (!ferr) { - uint8_t encodedTimestamp[8]; - if (fread(encodedTimestamp, 1, 8, tsF) != 8) + if (fread(&ts, 1, sizeof(ts), tsF) != sizeof(ts)) { fprintf(stderr, "Error reading timestamp file"); return -1; } - for (int i = 0; i < 8; i++) - timestamp |= static_cast(encodedTimestamp[i]) << (i * 8); - fclose(tsF); } @@ -52,7 +51,7 @@ int main(int argc, const char **argv) mfp.m_yPos = atoi(argv[6]); mfp.m_finderFlags = 0; mfp.m_protected = 0; - mfp.m_modifiedDate = mfp.m_creationDate = timestamp; + mfp.m_modifiedTimeMacEpoch = mfp.m_createdTimeMacEpoch = timestamp; for (int i = 7; i < argc; i++) { @@ -82,8 +81,11 @@ int main(int argc, const char **argv) errno_t err = fopen_s(&file, outPath.c_str(), "wb"); if (!err) { - fwrite(mps.m_data, PortabilityLayer::MacFilePropertiesSerialized::kSize, 1, file); - fclose(file); + PortabilityLayer::CFileStream stream(file); + + mps.WriteAsPackage(stream, ts); + + stream.Close(); } return 0; diff --git a/GpApp/GliderProtos.h b/GpApp/GliderProtos.h index b4a93e8..b09caf5 100644 --- a/GpApp/GliderProtos.h +++ b/GpApp/GliderProtos.h @@ -121,10 +121,10 @@ void ShiftWholeHouse (SInt16); void DoHouseInfo (void); // --- HouseInfo.c -Boolean OpenHouse (void); // --- HouseIO.c +Boolean OpenHouse (Boolean load); // --- HouseIO.c Boolean OpenSpecificHouse (const VFileSpec &); Boolean SaveHouseAs (void); -Boolean ReadHouse (void); +Boolean ReadHouse (GpIOStream *houseStream); Boolean WriteHouse (Boolean); Boolean CloseHouse (void); void OpenHouseResFork (void); diff --git a/GpApp/HighScores.cpp b/GpApp/HighScores.cpp index b0b4e6c..f3143a5 100644 --- a/GpApp/HighScores.cpp +++ b/GpApp/HighScores.cpp @@ -50,7 +50,7 @@ void GetHighScoreName (short); void UpdateBannerDialog (Dialog *); int16_t BannerFilter(void *context, Dialog *dialog, const TimeTaggedVOSEvent *evt); void GetHighScoreBanner (void); -Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream); +Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream, Boolean write); Str31 highBanner; @@ -686,23 +686,18 @@ void GetHighScoreBanner (void) //-------------------------------------------------------------- OpenHighScoresFile -Boolean OpenHighScoresFile (const VFileSpec &scoreSpec, GpIOStream *&scoresStream) +Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream, Boolean write) { - PLError_t theErr; - PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance(); - - theErr = fm->OpenFileData(scoreSpec.m_dir, scoreSpec.m_name, PortabilityLayer::EFilePermission_Any, scoresStream); + + PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".dat", write ? PortabilityLayer::EFilePermission_Write : PortabilityLayer::EFilePermission_Read, write ? GpFileCreationDispositions::kCreateOrOverwrite : GpFileCreationDispositions::kOpenExisting, outStream); if (theErr == PLErrors::kFileNotFound) { - theErr = fm->CreateFileAtCurrentTime(scoreSpec.m_dir, scoreSpec.m_name, 'ozm5', 'gliS'); - if (!CheckFileError(theErr, PSTR("New High Scores File"))) - return (false); - theErr = fm->OpenFileData(scoreSpec.m_dir, scoreSpec.m_name, PortabilityLayer::EFilePermission_Any, scoresStream); - if (!CheckFileError(theErr, PSTR("High Score"))) - return (false); + outStream = nil; + return (true); } - else if (!CheckFileError(theErr, PSTR("High Score"))) + + if (!CheckFileError(theErr, PSTR("High Score"))) return (false); return (true); @@ -721,18 +716,11 @@ Boolean WriteScoresToDisk (void) GpIOStream *scoresStream = nil; scoreSpec = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kHighScores, thisHouseName); - if (!OpenHighScoresFile(scoreSpec, scoresStream)) + if (!OpenHighScoresFile(scoreSpec, scoresStream, true)) { SysBeep(1); return (false); } - - if (!scoresStream->SeekStart(0)) - { - CheckFileError(PLErrors::kIOError, PSTR("High Scores File")); - scoresStream->Close(); - return(false); - } byteCount = sizeof(scoresType); theScores = &((*thisHouse)->highScores); @@ -744,13 +732,6 @@ Boolean WriteScoresToDisk (void) return(false); } - if (!scoresStream->Truncate(byteCount)) - { - CheckFileError(PLErrors::kIOError, PSTR("High Scores File")); - scoresStream->Close(); - return(false); - } - scoresStream->Close(); return (true); @@ -777,15 +758,22 @@ Boolean ReadScoresFromDisk (void) short volRefNum; char wasState; GpIOStream *scoresStream = nil; - + VFileSpec scoreSpec = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kHighScores, thisHouseName); - if (!OpenHighScoresFile(scoreSpec, scoresStream)) + if (!OpenHighScoresFile(scoreSpec, scoresStream, false)) { - SysBeep(1); return (false); } + + if (!scoresStream) + return (false); byteCount = scoresStream->Size(); + if (byteCount != sizeof(scoresType)) + { + scoresStream->Close(); + return (false); + } theScores = &((*thisHouse)->highScores); diff --git a/GpApp/House.cpp b/GpApp/House.cpp index 8a067b0..ff9d191 100644 --- a/GpApp/House.cpp +++ b/GpApp/House.cpp @@ -16,6 +16,7 @@ #include "FileManager.h" #include "FontFamily.h" #include "House.h" +#include "MacFileInfo.h" #include "PLStandardColors.h" #include "PLTimeTaggedVOSEvent.h" #include "RectUtils.h" @@ -66,6 +67,13 @@ static void FBUI_FreeFileDetails(void *fileDetails) { } +static bool FBUI_FilterFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename) +{ + PortabilityLayer::CompositeFile *cfile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(dirID, filename); + + return PortabilityLayer::ResTypeIDCodec::Decode(cfile->GetProperties().m_fileType) == 'gliH'; +} + static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetHouseDetailsAPI() { PortabilityLayer::FileBrowserUI_DetailsCallbackAPI api; @@ -74,6 +82,7 @@ static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetHouseDetailsAPI() api.m_drawFileDetailsCallback = FBUI_DrawFileDetails; api.m_loadFileDetailsCallback = FBUI_LoadFileDetails; api.m_freeFileDetailsCallback = FBUI_FreeFileDetails; + api.m_filterFileCallback = FBUI_FilterFile; return api; } @@ -98,7 +107,7 @@ Boolean CreateNewHouse (void) char savePath[sizeof(theSpec.m_name) + 1]; size_t savePathLength = 0; - if (!fm->PromptSaveFile(theSpec.m_dir, 'gliH', savePath, savePathLength, sizeof(theSpec.m_name), PSTR("My House"), PSTR("Create House"), GetHouseDetailsAPI())) + if (!fm->PromptSaveFile(theSpec.m_dir, ".gpf", savePath, savePathLength, sizeof(theSpec.m_name), PSTR("My House"), PSTR("Create House"), true, GetHouseDetailsAPI())) return false; assert(savePathLength < sizeof(theSpec.m_name) - 1); @@ -108,7 +117,7 @@ Boolean CreateNewHouse (void) if (fm->FileExists(theSpec.m_dir, theSpec.m_name)) { - if (!fm->DeleteFile(theSpec.m_dir, theSpec.m_name)) + if (!fm->DeleteCompositeFile(theSpec.m_dir, theSpec.m_name)) { CheckFileError(PLErrors::kAccessDenied, theSpec.m_name); return (false); @@ -133,7 +142,7 @@ Boolean CreateNewHouse (void) AddExtraHouse(theSpec); BuildHouseList(); InitCursor(); - if (!OpenHouse()) + if (!OpenHouse(false)) return (false); WriteOutPrefs(); diff --git a/GpApp/HouseIO.cpp b/GpApp/HouseIO.cpp index 59cf4d9..f6d7e3a 100644 --- a/GpApp/HouseIO.cpp +++ b/GpApp/HouseIO.cpp @@ -28,20 +28,18 @@ #define kDiscardChanges 2 -void LoopMovie (void); void OpenHouseMovie (void); void CloseHouseMovie (void); -Boolean IsFileReadOnly (const VFileSpec &); AnimationPlayer theMovie; Rect movieRect; PortabilityLayer::IResourceArchive *houseResFork; short wasHouseVersion; -GpIOStream *houseStream; Boolean houseOpen, fileDirty; Boolean changeLockStateOfHouse, saveHouseLocked, houseIsReadOnly; Boolean hasMovie, tvInRoom; +PortabilityLayer::CompositeFile *houseCFile; extern VFileSpec *theHousesSpecs; @@ -59,9 +57,7 @@ void OpenHouseMovie (void) { #ifdef COMPILEQT VFileSpec theSpec; - VFileInfo finderInfo; Handle spaceSaver; - PLError_t theErr; short movieRefNum; Boolean dataRefWasChanged; @@ -69,19 +65,20 @@ void OpenHouseMovie (void) { theSpec = theHousesSpecs[thisHouseIndex]; PasStringConcat(theSpec.m_name, PSTR(".mov")); - - theErr = FSpGetFInfo(theSpec, finderInfo); - if (theErr != PLErrors::kNone) - return; AnimationPackage *anim = AnimationPackage::Create(); if (!anim) return; - if (!anim->Load(theSpec.m_dir, theSpec.m_name)) + PLError_t theErr = anim->Load(theSpec.m_dir, theSpec.m_name); + + if (theErr != PLErrors::kNone) { anim->Destroy(); - YellowAlert(kYellowQTMovieNotLoaded, theErr); + + if (theErr != PLErrors::kFileNotFound) + YellowAlert(kYellowQTMovieNotLoaded, theErr); + return; } @@ -116,7 +113,7 @@ void CloseHouseMovie (void) //-------------------------------------------------------------- OpenHouse // Opens a house (whatever current selection is). Returns true if all went well. -Boolean OpenHouse (void) +Boolean OpenHouse (Boolean read) { PLError_t theErr; @@ -132,12 +129,21 @@ Boolean OpenHouse (void) if (!StrCmp::EqualCaseInsensitive(theHousesSpecs[thisHouseIndex].name, "\pDemo House")) return (false); #endif - - houseIsReadOnly = IsFileReadOnly(theHousesSpecs[thisHouseIndex]); - - theErr = PortabilityLayer::FileManager::GetInstance()->OpenFileData(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name, PortabilityLayer::EFilePermission_Any, houseStream); - if (!CheckFileError(theErr, thisHouseName)) + + houseCFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name); + if (!houseCFile) return (false); + + houseIsReadOnly = houseCFile->IsDataReadOnly(); + + GpIOStream *houseStream = nil; + theErr = houseCFile->OpenData(PortabilityLayer::EFilePermission_Any, GpFileCreationDispositions::kCreateOrOpen, houseStream); + if (!CheckFileError(theErr, thisHouseName)) + { + houseCFile->Close(); + houseCFile = nil; + return (false); + } houseOpen = true; OpenHouseResFork(); @@ -148,6 +154,16 @@ Boolean OpenHouse (void) tvInRoom = false; tvWithMovieNumber = -1; OpenHouseMovie(); + + if (read) + { + Boolean readOK = ReadHouse(houseStream); + houseStream->Close(); + + return readOK; + } + + houseStream->Close(); return (true); } @@ -173,9 +189,7 @@ Boolean OpenSpecificHouse (const VFileSpec &specs) { thisHouseIndex = i; PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) - itOpened = ReadHouse(); - else + if (!OpenHouse(true)) itOpened = false; break; } @@ -527,7 +541,7 @@ bool ByteSwapHouse(housePtr house, size_t sizeInBytes, bool isSwappedAfter) return true; } -Boolean ReadHouse (void) +Boolean ReadHouse (GpIOStream *houseStream) { long byteCount; PLError_t theErr; @@ -653,19 +667,27 @@ Boolean WriteHouse (Boolean checkIt) UInt32 timeStamp; long byteCount; PLError_t theErr; - + + if ((housesFound < 1) || (thisHouseIndex == -1)) + return(false); + if (!houseOpen) { YellowAlert(kYellowUnaccounted, 4); return (false); } - if (!houseStream->SeekStart(0)) + if (!houseCFile) { - CheckFileError(PLErrors::kIOError, thisHouseName); - return(false); + YellowAlert(kYellowUnaccounted, 4); + return (false); } - + + GpIOStream *houseStream = nil; + theErr = houseCFile->OpenData(PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOpen, houseStream); + if (theErr != PLErrors::kNone) + return (false); + CopyThisRoomToRoom(); if (checkIt) @@ -699,6 +721,7 @@ Boolean WriteHouse (Boolean checkIt) { CheckFileError(PLErrors::kIOError, thisHouseName); ByteSwapHouse(*thisHouse, static_cast(byteCount), false); + houseStream->Close(); return(false); } @@ -706,16 +729,13 @@ Boolean WriteHouse (Boolean checkIt) { CheckFileError(PLErrors::kIOError, thisHouseName); ByteSwapHouse(*thisHouse, static_cast(byteCount), false); + houseStream->Close(); return(false); } ByteSwapHouse(*thisHouse, static_cast(byteCount), false); - if (!houseStream->Truncate(byteCount)) - { - CheckFileError(PLErrors::kIOError, thisHouseName); - return(false); - } + houseStream->Close(); if (changeLockStateOfHouse) { @@ -749,7 +769,11 @@ Boolean CloseHouse (void) CloseHouseResFork(); CloseHouseMovie(); - houseStream->Close(); + if (houseCFile) + { + houseCFile->Close(); + houseCFile = nil; + } houseOpen = false; @@ -764,7 +788,7 @@ void OpenHouseResFork (void) PortabilityLayer::ResourceManager *rm = PortabilityLayer::ResourceManager::GetInstance(); if (houseResFork == nullptr) { - houseResFork = rm->LoadResFile(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name); + houseResFork = rm->LoadResFile(houseCFile); if (!houseResFork) YellowAlert(kYellowFailedResOpen, PLErrors::kResourceError); } @@ -817,8 +841,7 @@ Boolean QuerySaveChanges (void) if (!quitting) { whoCares = CloseHouse(); - if (OpenHouse()) - whoCares = ReadHouse(); + OpenHouse(true); } UpdateMenus(false); return (true); @@ -850,17 +873,6 @@ void YellowAlert (short whichAlert, short identifier) whoCares = PortabilityLayer::DialogManager::GetInstance()->DisplayAlert(kYellowAlert, &substitutions); } -//-------------------------------------------------------------- IsFileReadOnly - -Boolean IsFileReadOnly (const VFileSpec &spec) -{ - // Kind of annoying, but itch.io doesn't preserve read-only flags and there doesn't seem to be any way around that. - if (spec.m_dir == PortabilityLayer::VirtualDirectories::kApplicationData || spec.m_dir == PortabilityLayer::VirtualDirectories::kGameData) - return true; - - return PortabilityLayer::FileManager::GetInstance()->FileLocked(spec.m_dir, spec.m_name); -} - //-------------------------------------------------------------- LoadHousePicture THandle LoadHouseResource(const PortabilityLayer::ResTypeID &resTypeID, int16_t resID) diff --git a/GpApp/Main.cpp b/GpApp/Main.cpp index e4d41b4..609ebd4 100644 --- a/GpApp/Main.cpp +++ b/GpApp/Main.cpp @@ -8,6 +8,7 @@ #include "WindowDef.h" #include "BitmapImage.h" +#include "FileManager.h" #include "Externs.h" #include "Environ.h" #include "FontFamily.h" @@ -70,7 +71,7 @@ extern Str15 leftName, rightName, batteryName, bandName; extern Str15 highName; //extern long encryptedNumber; extern short maxFiles, numNeighbors, willMaxFiles; -extern GpIOStream *houseStream; +extern PortabilityLayer::CompositeFile *houseCFile; extern short isEditH, isEditV, isMapH, isMapV; extern short isToolsH, isToolsV, isCoordH, isCoordV; extern short isLinkH, isLinkV, toolMode, mapLeftRoom, mapTopRoom; @@ -225,12 +226,12 @@ void ReadInPrefs (void) isEditV = 41; isMapH = 3; // isMapV = qd.screenBits.bounds.bottom - 100; - isMapV = 100; + isMapV = 385; mapRoomsWide = 15; mapRoomsHigh = 4; // isToolsH = qd.screenBits.bounds.right - 120; - isToolsH = 100; - isToolsV = 35; + isToolsH = 525; + isToolsV = 41; isLinkH = 50; isLinkV = 80; // isCoordH = qd.screenBits.bounds.right - 55; @@ -361,8 +362,6 @@ void WriteOutPrefs (void) SysBeep(1); modulePrefs.Dispose(); - - UnivSetSoundVolume(wasVolume, thisMac.hasSM3); } void StepLoadScreenRing() @@ -1167,8 +1166,7 @@ int gpAppMain() InitSound(); SpinCursor(2); InitMusic(); SpinCursor(2); BuildHouseList(); - if (OpenHouse()) - whoCares = ReadHouse(); + OpenHouse(true); PlayPrioritySound(kBirdSound, kBirdPriority); DelayTicks(6); @@ -1201,7 +1199,8 @@ int gpAppMain() if (!CloseHouse()) { CloseHouseResFork(); - houseStream->Close(); + if (houseCFile) + houseCFile->Close(); houseOpen = false; } } diff --git a/GpApp/Menu.cpp b/GpApp/Menu.cpp index 2a4e748..4521450 100644 --- a/GpApp/Menu.cpp +++ b/GpApp/Menu.cpp @@ -426,6 +426,7 @@ void DoOptionsMenu (short theItem) } OpenMainWindow(); + RedrawSplashScreen(); incrementModeTime = TickCount() + kIdleSplashTicks; } else if (theMode == kSplashMode) // switching to edit mode diff --git a/GpApp/Play.cpp b/GpApp/Play.cpp index e5595db..1715dc0 100644 --- a/GpApp/Play.cpp +++ b/GpApp/Play.cpp @@ -278,12 +278,11 @@ void DoDemoGame (void) whoCares = CloseHouse(); thisHouseIndex = demoHouseIndex; PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) + if (OpenHouse(true)) { if (thisMac.isTouchscreen) DismissMainMenuUI(); - whoCares = ReadHouse(); demoGoing = true; NewGame(kNewGameMode); @@ -293,8 +292,7 @@ void DoDemoGame (void) whoCares = CloseHouse(); thisHouseIndex = wasHouseIndex; PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) - whoCares = ReadHouse(); + OpenHouse(true); incrementModeTime = TickCount() + kIdleSplashTicks; RedrawSplashScreen(); } diff --git a/GpApp/Prefs.cpp b/GpApp/Prefs.cpp index 57eb0f7..2a77a5d 100644 --- a/GpApp/Prefs.cpp +++ b/GpApp/Prefs.cpp @@ -22,7 +22,7 @@ #define kPrefCreatorType 'ozm5' #define kPrefFileType 'gliP' -#define kPrefFileName PSTR("Glider Prefs v2") +#define kPrefFileName PSTR("Glider Prefs") #define kDefaultPrefFName PSTR("Preferences") #define kPrefsStringsID 160 #define kNewPrefsAlertID 160 @@ -67,16 +67,8 @@ Boolean WritePrefs (const prefsInfo *thePrefs, short versionNow, THandleFileExists(PortabilityLayer::VirtualDirectories::kPrefs, fileName)) - { - theErr = fm->CreateFileAtCurrentTime(theSpecs.m_dir, theSpecs.m_name, kPrefCreatorType, kPrefFileType); - if (theErr != PLErrors::kNone) - { - CheckFileError(theErr, PSTR("Preferences")); - return(false); - } - } - theErr = fm->OpenFileData(theSpecs.m_dir, theSpecs.m_name, PortabilityLayer::EFilePermission_Write, fileStream); + + theErr = fm->OpenNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat", PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOverwrite, fileStream); if (theErr != PLErrors::kNone) { CheckFileError(theErr, PSTR("Preferences")); @@ -201,10 +193,10 @@ PLError_t ReadPrefs (prefsInfo *thePrefs, short versionNeed, Boolean *isOldVersi theSpecs = MakeVFileSpec(PortabilityLayer::VirtualDirectory_t::kPrefs, fileName); - if (!PortabilityLayer::FileManager::GetInstance()->FileExists(theSpecs.m_dir, theSpecs.m_name)) - return PLErrors::kFileNotFound; + theErr = fm->OpenNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, fileStream); + if (theErr == PLErrors::kFileNotFound) + return theErr; - theErr = fm->OpenFileData(theSpecs.m_dir, theSpecs.m_name, PortabilityLayer::EFilePermission_Read, fileStream); if (theErr != PLErrors::kNone) { CheckFileError(theErr, PSTR("Preferences")); @@ -326,7 +318,7 @@ Boolean DeletePrefs () theSpecs = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kPrefs, fileName); - return PortabilityLayer::FileManager::GetInstance()->DeleteFile(theSpecs.m_dir, theSpecs.m_name); + return PortabilityLayer::FileManager::GetInstance()->DeleteNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat"); } //-------------------------------------------------------------- RunFunctionOnAllPrefsHandlers diff --git a/GpApp/SavedGames.cpp b/GpApp/SavedGames.cpp index a1ddb33..e369e98 100644 --- a/GpApp/SavedGames.cpp +++ b/GpApp/SavedGames.cpp @@ -73,7 +73,7 @@ static void FBUI_DrawFileDetails(DrawSurface *surface, const Point &basePoint, c static void *FBUI_LoadFileDetails(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename) { GpIOStream *stream = nullptr; - if (PortabilityLayer::FileManager::GetInstance()->OpenFileData(dirID, filename, PortabilityLayer::EFilePermission_Read, stream) != PLErrors::kNone) + if (PortabilityLayer::FileManager::GetInstance()->OpenNonCompositeFile(dirID, filename, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, stream) != PLErrors::kNone) return nullptr; const size_t kPrefixSize = sizeof(game2Type) - sizeof(savedRoom); @@ -102,6 +102,11 @@ static void FBUI_FreeFileDetails(void *fileDetails) PortabilityLayer::MemoryManager::GetInstance()->Release(fileDetails); } +static bool FBUI_FilterFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename) +{ + return true; +} + static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetSavedGameDetailsAPI() { PortabilityLayer::FileBrowserUI_DetailsCallbackAPI api; @@ -110,6 +115,7 @@ static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetSavedGameDetailsAPI api.m_drawFileDetailsCallback = FBUI_DrawFileDetails; api.m_loadFileDetailsCallback = FBUI_LoadFileDetails; api.m_freeFileDetailsCallback = FBUI_FreeFileDetails; + api.m_filterFileCallback = FBUI_FilterFile; return api; } @@ -163,7 +169,7 @@ Boolean SaveGame2 (void) char savePath[sizeof(spec.m_name) + 1]; size_t savePathLength = 0; - if (!fm->PromptSaveFile(spec.m_dir, 'gliG', savePath, savePathLength, sizeof(spec.m_name), PLPasStr(gameNameStr), PSTR("Save Game"), GetSavedGameDetailsAPI())) + if (!fm->PromptSaveFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PLPasStr(gameNameStr), PSTR("Save Game"), false, GetSavedGameDetailsAPI())) { mm->Release(savedGame); return false; @@ -176,7 +182,7 @@ Boolean SaveGame2 (void) if (fm->FileExists(spec.m_dir, PLPasStr(spec.m_name))) { - if (!fm->DeleteFile(spec.m_dir, spec.m_name)) + if (!fm->DeleteNonCompositeFile(spec.m_dir, spec.m_name, ".dat")) { CheckFileError(PLErrors::kAccessDenied, PSTR("Saved Game")); return false; @@ -216,20 +222,15 @@ Boolean SaveGame2 (void) destRoom->objects[i] = srcRoom->objects[i]; } - PLError_t theErr = fm->CreateFileAtCurrentTime(spec.m_dir, spec.m_name, 'ozm5', 'gliG'); + PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOverwrite, gameStream); if (CheckFileError(theErr, PSTR("Saved Game"))) { - theErr = fm->OpenFileData(spec.m_dir, spec.m_name, PortabilityLayer::EFilePermission_Write, gameStream); - - if (CheckFileError(theErr, PSTR("Saved Game"))) + if (gameStream->Write(savedGame, byteCount) != byteCount) { - if (gameStream->Write(savedGame, byteCount) != byteCount) - { - CheckFileError(PLErrors::kIOError, PSTR("Saved Game")); - } - - gameStream->Close(); + CheckFileError(PLErrors::kIOError, PSTR("Saved Game")); } + + gameStream->Close(); } mm->Release(savedGame); @@ -274,7 +275,7 @@ Boolean OpenSavedGame (void) char savePath[sizeof(spec.m_name) + 1]; size_t savePathLength = 0; - if (!fm->PromptOpenFile(spec.m_dir, 'gliG', savePath, savePathLength, sizeof(spec.m_name), PSTR("Open Saved Game"), GetSavedGameDetailsAPI())) + if (!fm->PromptOpenFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PSTR("Open Saved Game"), true, GetSavedGameDetailsAPI())) return false; assert(savePathLength < sizeof(spec.m_name) - 1); @@ -282,15 +283,8 @@ Boolean OpenSavedGame (void) spec.m_name[0] = static_cast(savePathLength); memcpy(spec.m_name + 1, savePath, savePathLength); - PortabilityLayer::MacFileProperties props; - if (!fm->ReadFileProperties(spec.m_dir, spec.m_name, props)) - return false; - - if (memcmp(props.m_fileType, "gliG", 4)) - return false; - GpIOStream *gameStream = nullptr; - PLError_t theErr = fm->OpenFileData(spec.m_dir, spec.m_name, PortabilityLayer::EFilePermission_Read, gameStream); + PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, gameStream); if (!CheckFileError(theErr, PSTR("Saved Game"))) return(false); diff --git a/GpApp/SelectHouse.cpp b/GpApp/SelectHouse.cpp index 9a655a1..538989f 100644 --- a/GpApp/SelectHouse.cpp +++ b/GpApp/SelectHouse.cpp @@ -101,18 +101,26 @@ void UpdateLoadDialog (Dialog *theDialog) if (SectRect(&dialogRect, &tempRect, &dummyRect)) { - PortabilityLayer::IResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name); - if (resFile != nullptr) + PortabilityLayer::CompositeFile *cfile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name); + + bool haveHouseIcon = false; + GpIOStream *resStream = nil; + + if (cfile) { - if (!LargeIconPlot(surface, resFile, -16455, tempRect)) + PortabilityLayer::IResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(cfile); + if (resFile != nullptr) { - LoadDialogPICT(theDialog, kLoadIconFirstItem + i - housePage, - kDefaultHousePict8); + if (LargeIconPlot(surface, resFile, -16455, tempRect)) + haveHouseIcon = true; + + resFile->Destroy(); } - resFile->Destroy(); + cfile->Close(); } - else + + if (!haveHouseIcon) LoadDialogPICT(theDialog, kLoadIconFirstItem + i - housePage, kDefaultHousePict8); } @@ -411,11 +419,8 @@ void DoLoadHouse (void) whoCares = CloseHouse(); PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) - { - whoCares = ReadHouse(); + if (OpenHouse(true)) houseNameDirty = true; - } } leaving = true; } @@ -452,11 +457,8 @@ void DoLoadHouse (void) whoCares = CloseHouse(); PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) - { - whoCares = ReadHouse(); + if (OpenHouse(true)) houseNameDirty = true; - } } leaving = true; } @@ -489,11 +491,8 @@ void DoLoadHouse (void) whoCares = CloseHouse(); PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName); - if (OpenHouse()) - { - whoCares = ReadHouse(); + if (OpenHouse(true)) houseNameDirty = true; - } } leaving = true; } diff --git a/GpApp/SourceExport.cpp b/GpApp/SourceExport.cpp index 0ce1070..a347d18 100644 --- a/GpApp/SourceExport.cpp +++ b/GpApp/SourceExport.cpp @@ -96,41 +96,6 @@ SortableEntry SortableEntry::Create(const char *zipLocation, PortabilityLayer::V return entry; } -static void ConvertToMSDOSTimestamp(const PortabilityLayer::CombinedTimestamp &ts, uint16_t &msdosDate, uint16_t &msdosTime) -{ - int32_t yearsSince1980 = ts.GetLocalYear() - 1980; - uint8_t month = ts.m_localMonth; - uint8_t day = ts.m_localDay; - - uint8_t hour = ts.m_localHour; - uint8_t minute = ts.m_localMinute; - uint8_t second = ts.m_localSecond; - - if (yearsSince1980 < 0) - { - // Time machine - yearsSince1980 = 0; - second = 0; - minute = 0; - hour = 0; - day = 1; - month = 1; - } - else if (yearsSince1980 > 127) - { - // I was promised flying cars, but it's 2107 and you're still flying paper airplanes... - yearsSince1980 = 127; - second = 59; - minute = 59; - hour = 23; - day = 31; - month = 12; - } - - msdosTime = (second / 2) | (minute << 5) | (hour << 11); - msdosDate = day | (month << 5) | (yearsSince1980 << 9); -} - static void InitSourceExportWindow(SourceExportState *state) { static const int kLoadScreenHeight = 32; @@ -403,7 +368,7 @@ static bool RepackDirectory(SourceExportState &state, GpIOStream *outStream, std uint16_t dosDate = 0; uint16_t dosTime = 0; - ConvertToMSDOSTimestamp(state.m_ts, dosDate, dosTime); + state.m_ts.GetAsMSDOSTimestamp(dosDate, dosTime); IGpDirectoryCursor *dirCursor = PLDrivers::GetFileSystem()->ScanDirectory(virtualDir); if (!dirCursor) @@ -610,7 +575,7 @@ static bool AddZipDirectory(GpIOStream *stream, std::vectorTell(); diff --git a/GpCommon/GpIOStream.h b/GpCommon/GpIOStream.h index 866ab55..590252c 100644 --- a/GpCommon/GpIOStream.h +++ b/GpCommon/GpIOStream.h @@ -13,9 +13,23 @@ public: virtual bool SeekStart(GpUFilePos_t loc) = 0; virtual bool SeekCurrent(GpFilePos_t loc) = 0; virtual bool SeekEnd(GpUFilePos_t loc) = 0; - virtual bool Truncate(GpUFilePos_t loc) = 0; virtual GpUFilePos_t Size() const = 0; virtual GpUFilePos_t Tell() const = 0; virtual void Close() = 0; virtual void Flush() = 0; + + bool ReadExact(void *bytesOut, size_t size); + bool WriteExact(const void *bytesOut, size_t size); }; + +inline bool GpIOStream::ReadExact(void *bytesOut, size_t size) +{ + const size_t nRead = this->Read(bytesOut, size); + return nRead == size; +} + +inline bool GpIOStream::WriteExact(const void *bytes, size_t size) +{ + const size_t nWritten = this->Write(bytes, size); + return nWritten == size; +} diff --git a/GpCommon/IGpFileSystem.h b/GpCommon/IGpFileSystem.h index 3ae14c6..18435be 100644 --- a/GpCommon/IGpFileSystem.h +++ b/GpCommon/IGpFileSystem.h @@ -15,7 +15,7 @@ public: typedef void(*DelayCallback_t)(uint32_t ticks); virtual bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) = 0; - virtual bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0; + virtual bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) = 0; virtual GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0; virtual bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) = 0; virtual IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) = 0; @@ -23,7 +23,6 @@ public: virtual bool ValidateFilePath(const char *path, size_t pathLen) const = 0; virtual bool ValidateFilePathUnicodeChar(uint32_t ch) const = 0; - virtual bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const = 0; virtual void SetMainThreadRelay(IGpThreadRelay *relay) = 0; virtual void SetDelayCallback(DelayCallback_t delayCallback) = 0; diff --git a/MakeTimestamp/MakeTimestamp.cpp b/MakeTimestamp/MakeTimestamp.cpp index c3d8529..bbc9e4e 100644 --- a/MakeTimestamp/MakeTimestamp.cpp +++ b/MakeTimestamp/MakeTimestamp.cpp @@ -51,7 +51,7 @@ int main(int argc, const char **argv) SystemTimeToTzSpecificLocalTime(&tz, &utcST, &localST); PortabilityLayer::CombinedTimestamp ts; - ts.SetUTCTime(timeDelta); + ts.SetMacEpochTime(timeDelta); ts.SetLocalYear(localST.wYear); ts.m_localMonth = localST.wMonth; diff --git a/MergeGPF/MergeGPF.cpp b/MergeGPF/MergeGPF.cpp new file mode 100644 index 0000000..b1ea2fd --- /dev/null +++ b/MergeGPF/MergeGPF.cpp @@ -0,0 +1,388 @@ +#include "WindowsUnicodeToolShim.h" + +#include "CFileStream.h" +#include "CombinedTimestamp.h" +#include "DeflateCodec.h" +#include "MacFileInfo.h" +#include "ZipFile.h" + +#include +#include +#include + +int toolMain(int argc, const char **argv) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: MergeGPF "); + return -1; + } + + std::string metaFileName = argv[1]; + if (metaFileName.length() < 5) + { + fprintf(stderr, "Usage: MergeGPF "); + return -1; + } + + std::string fnameBase = metaFileName.substr(0, metaFileName.length() - 4); + + std::string resName = fnameBase + ".gpa"; + std::string dataName = fnameBase + ".gpd"; + + PortabilityLayer::MacFilePropertiesSerialized mfps; + + PortabilityLayer::ZipFileLocalHeader metaLH; + + { + FILE *metaF = fopen_utf8(argv[1], "rb"); + + PortabilityLayer::CFileStream metaStream(metaF); + + if (!mfps.ReadFromPackage(metaStream)) + { + fprintf(stderr, "Error reading metadata"); + return -1; + } + + metaStream.SeekStart(0); + metaStream.Read(&metaLH, sizeof(metaLH)); + metaStream.Close(); + } + + PortabilityLayer::MacFileProperties mfp; + mfps.Deserialize(mfp); + + mfps.Serialize(mfp); + + size_t insertedMetaFSize = sizeof(metaLH) + strlen(PortabilityLayer::MacFilePropertiesSerialized::GetPackagedName()) + sizeof(mfps.m_data); + size_t insertedDataSize = 0; + + FILE *mergedF = fopen_utf8(argv[1], "wb"); + if (!mergedF) + { + fprintf(stderr, "Error reopening metadata file"); + return -1; + } + + PortabilityLayer::CFileStream mergedStream(mergedF); + + uint32_t numFiles = 0; + uint32_t cdirSize = 0; + const char *metaPackagedName = PortabilityLayer::MacFilePropertiesSerialized::GetPackagedName(); + const char *dataPackagedName = "!data"; + + PortabilityLayer::ZipCentralDirectoryFileHeader metaCDir; + PortabilityLayer::ZipCentralDirectoryFileHeader dataCDir; + + bool hasData = false; + + // Write metadata + { + GpUFilePos_t metaLHStart = mergedStream.Tell(); + + PortabilityLayer::ZipFileLocalHeader metaCopyLH; + + metaCopyLH.m_signature = PortabilityLayer::ZipFileLocalHeader::kSignature; + metaCopyLH.m_versionRequired = PortabilityLayer::ZipConstants::kStoredRequiredVersion; + metaCopyLH.m_flags = 0; + metaCopyLH.m_method = PortabilityLayer::ZipConstants::kStoredMethod; + metaCopyLH.m_modificationTime = metaLH.m_modificationTime; + metaCopyLH.m_modificationDate = metaLH.m_modificationDate; + metaCopyLH.m_crc = PortabilityLayer::DeflateContext::CRC32(0, mfps.m_data, sizeof(mfps.m_data)); + metaCopyLH.m_compressedSize = sizeof(mfps.m_data); + metaCopyLH.m_uncompressedSize = sizeof(mfps.m_data); + metaCopyLH.m_fileNameLength = strlen(metaPackagedName); + metaCopyLH.m_extraFieldLength = 0; + + if (!mergedStream.WriteExact(&metaCopyLH, sizeof(metaCopyLH)) || !mergedStream.WriteExact(metaPackagedName, strlen(metaPackagedName)) || !mergedStream.WriteExact(mfps.m_data, sizeof(mfps.m_data))) + { + fprintf(stderr, "Error writing metadata"); + return -1; + } + + numFiles++; + cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + strlen(metaPackagedName); + + + metaCDir.m_signature = PortabilityLayer::ZipCentralDirectoryFileHeader::kSignature; + metaCDir.m_versionCreated = PortabilityLayer::ZipConstants::kCompressedRequiredVersion; + metaCDir.m_versionRequired = PortabilityLayer::ZipConstants::kStoredRequiredVersion; + metaCDir.m_flags = 0; + metaCDir.m_method = PortabilityLayer::ZipConstants::kStoredMethod; + metaCDir.m_modificationTime = metaLH.m_modificationTime; + metaCDir.m_modificationDate = metaLH.m_modificationDate; + metaCDir.m_crc = metaLH.m_crc; + metaCDir.m_compressedSize = metaLH.m_compressedSize; + metaCDir.m_uncompressedSize = metaLH.m_uncompressedSize; + metaCDir.m_fileNameLength = metaLH.m_fileNameLength; + metaCDir.m_extraFieldLength = metaLH.m_extraFieldLength; + metaCDir.m_commentLength = 0; + metaCDir.m_diskNumber = 0; + metaCDir.m_internalAttributes = 0; + metaCDir.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes; + metaCDir.m_localHeaderOffset = metaLHStart; + } + + FILE *dataF = fopen_utf8(dataName.c_str(), "rb"); + if (dataF) + { + GpUFilePos_t dataLHStart = mergedStream.Tell(); + PortabilityLayer::ZipFileLocalHeader dataLH; + + dataLH.m_signature = PortabilityLayer::ZipFileLocalHeader::kSignature; + dataLH.m_versionRequired = PortabilityLayer::ZipConstants::kCompressedRequiredVersion; + dataLH.m_flags = 0; + dataLH.m_method = PortabilityLayer::ZipConstants::kDeflatedMethod; + dataLH.m_modificationTime = metaLH.m_modificationTime; + dataLH.m_modificationDate = metaLH.m_modificationDate; + dataLH.m_crc = 0; + dataLH.m_compressedSize = 0; + dataLH.m_uncompressedSize = 0; + dataLH.m_fileNameLength = strlen(dataPackagedName); + dataLH.m_extraFieldLength = 0; + + if (!mergedStream.WriteExact(&dataLH, sizeof(dataLH)) || !mergedStream.WriteExact(dataPackagedName, strlen(dataPackagedName))) + { + fprintf(stderr, "Error compressing data"); + return -1; + } + + PortabilityLayer::DeflateContext *ctx = PortabilityLayer::DeflateContext::Create(&mergedStream, 9); + + uint8_t compressBuffer[1024]; + uint32_t crc = 0; + size_t uncompressedSize = 0; + GpUFilePos_t compressedDataStart = mergedStream.Tell(); + for (;;) + { + size_t dataRead = fread(compressBuffer, 1, sizeof(compressBuffer), dataF); + + if (dataRead == 0) + break; + + uncompressedSize += dataRead; + + if (!ctx->Append(compressBuffer, dataRead)) + { + fprintf(stderr, "Error compressing data"); + return -1; + } + + crc = PortabilityLayer::DeflateContext::CRC32(crc, compressBuffer, dataRead); + } + + if (!ctx->Flush()) + { + fprintf(stderr, "Error compressing data"); + return -1; + } + + ctx->Destroy(); + + GpUFilePos_t compressedDataEnd = mergedStream.Tell(); + + dataLH.m_crc = crc; + dataLH.m_compressedSize = (compressedDataEnd - compressedDataStart); + dataLH.m_uncompressedSize = uncompressedSize; + + if (!mergedStream.SeekStart(dataLHStart) || !mergedStream.Write(&dataLH, sizeof(dataLH)) || !mergedStream.SeekStart(compressedDataEnd)) + { + fprintf(stderr, "Error compressing data"); + return -1; + } + + numFiles++; + cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + strlen(dataPackagedName); + hasData = true; + + fclose(dataF); + + insertedDataSize += sizeof(dataLH) + strlen(dataPackagedName) + (compressedDataEnd - compressedDataStart); + + dataCDir.m_signature = PortabilityLayer::ZipCentralDirectoryFileHeader::kSignature; + dataCDir.m_versionCreated = PortabilityLayer::ZipConstants::kCompressedRequiredVersion; + dataCDir.m_versionRequired = PortabilityLayer::ZipConstants::kCompressedRequiredVersion; + dataCDir.m_flags = 0; + dataCDir.m_method = PortabilityLayer::ZipConstants::kDeflatedMethod; + dataCDir.m_modificationTime = dataLH.m_modificationTime; + dataCDir.m_modificationDate = dataLH.m_modificationDate; + dataCDir.m_crc = dataLH.m_crc; + dataCDir.m_compressedSize = dataLH.m_compressedSize; + dataCDir.m_uncompressedSize = dataLH.m_uncompressedSize; + dataCDir.m_fileNameLength = dataLH.m_fileNameLength; + dataCDir.m_extraFieldLength = dataLH.m_extraFieldLength; + dataCDir.m_commentLength = 0; + dataCDir.m_diskNumber = 0; + dataCDir.m_internalAttributes = 0; + dataCDir.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes; + dataCDir.m_localHeaderOffset = dataLHStart; + } + + std::vector resCentralDir; + std::vector fileNameBytes; + std::vector fileNameSizes; + + FILE *resF = fopen_utf8(resName.c_str(), "rb"); + { + PortabilityLayer::ZipEndOfCentralDirectoryRecord eocd; + + PortabilityLayer::CFileStream resStream(resF); + + if (!resStream.SeekEnd(sizeof(PortabilityLayer::ZipEndOfCentralDirectoryRecord)) || !resStream.ReadExact(&eocd, sizeof(eocd)) || !resStream.SeekStart(eocd.m_centralDirStartOffset)) + { + fprintf(stderr, "Error reading res data"); + return -1; + } + + size_t numResFiles = eocd.m_numCentralDirRecords; + + for (size_t i = 0; i < numResFiles; i++) + { + PortabilityLayer::ZipCentralDirectoryFileHeader cdirFile; + + if (!resStream.ReadExact(&cdirFile, sizeof(cdirFile))) + { + fprintf(stderr, "Error reading cdir entry"); + return -1; + } + + size_t fileNameLength = cdirFile.m_fileNameLength; + fileNameSizes.push_back(fileNameLength); + + if (fileNameLength > 0) + { + fileNameBytes.resize(fileNameBytes.size() + fileNameLength); + if (!resStream.Read(&fileNameBytes[fileNameBytes.size() - fileNameLength], fileNameLength)) + { + fprintf(stderr, "Error reading cdir entry"); + return -1; + } + } + + if (!resStream.SeekCurrent(cdirFile.m_extraFieldLength + cdirFile.m_commentLength)) + { + fprintf(stderr, "Error reading cdir entry"); + return -1; + } + + resCentralDir.push_back(cdirFile); + + numFiles++; + cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + fileNameLength; + } + + for (size_t i = 0; i < resCentralDir.size(); i++) + { + PortabilityLayer::ZipCentralDirectoryFileHeader &cdirHeader = resCentralDir[i]; + + PortabilityLayer::ZipFileLocalHeader resLH; + + if (!resStream.SeekStart(cdirHeader.m_localHeaderOffset) || !resStream.ReadExact(&resLH, sizeof(resLH)) || resLH.m_fileNameLength != cdirHeader.m_fileNameLength || resLH.m_compressedSize != cdirHeader.m_compressedSize || resLH.m_uncompressedSize != cdirHeader.m_uncompressedSize) + { + fprintf(stderr, "Error reading res"); + return -1; + } + + size_t chunkSizes[] = { resLH.m_fileNameLength, resLH.m_extraFieldLength, resLH.m_compressedSize }; + + resLH.m_extraFieldLength = 0; + + cdirHeader.m_localHeaderOffset = mergedStream.Tell(); + cdirHeader.m_extraFieldLength = 0; + cdirHeader.m_commentLength = 0; + + if (!mergedStream.WriteExact(&resLH, sizeof(resLH))) + { + fprintf(stderr, "Error copying resource header"); + return -1; + } + + for (int chunk = 0; chunk < 3; chunk++) + { + size_t chunkCopySize = chunkSizes[chunk]; + + if (chunk == 1) + { + // Strip extra field + if (!resStream.SeekCurrent(chunkCopySize)) + { + fprintf(stderr, "Error copying resource"); + return -1; + } + } + else + { + uint8_t copyBuffer[1024]; + while (chunkCopySize > 0) + { + size_t amountToCopy = std::min(sizeof(copyBuffer), chunkCopySize); + + if (!resStream.ReadExact(copyBuffer, amountToCopy) || !mergedStream.WriteExact(copyBuffer, amountToCopy)) + { + fprintf(stderr, "Error copying resource"); + return -1; + } + + chunkCopySize -= amountToCopy; + } + } + } + } + + resStream.Close(); + } + + GpUFilePos_t cdirPos = mergedStream.Tell(); + + // Write metadata cdir + if (!mergedStream.WriteExact(&metaCDir, sizeof(metaCDir)) || !mergedStream.WriteExact(metaPackagedName, strlen(metaPackagedName))) + { + fprintf(stderr, "Error writing directory"); + return -1; + } + + if (hasData) + { + if (!mergedStream.WriteExact(&dataCDir, sizeof(dataCDir)) || !mergedStream.WriteExact(dataPackagedName, strlen(dataPackagedName))) + { + fprintf(stderr, "Error writing directory"); + return -1; + } + } + + size_t fnameBytesOffset = 0; + for (size_t i = 0; i < resCentralDir.size(); i++) + { + size_t fnameSize = fileNameSizes[i]; + const PortabilityLayer::ZipCentralDirectoryFileHeader &cdir = resCentralDir[i]; + + if (!mergedStream.WriteExact(&cdir, sizeof(cdir)) || (fnameSize > 0 && !mergedStream.WriteExact(&fileNameBytes[fnameBytesOffset], fnameSize))) + { + fprintf(stderr, "Error writing directory"); + return -1; + } + + fnameBytesOffset += fnameSize; + } + + PortabilityLayer::ZipEndOfCentralDirectoryRecord eocd; + eocd.m_signature = PortabilityLayer::ZipEndOfCentralDirectoryRecord::kSignature; + eocd.m_thisDiskNumber = 0; + eocd.m_centralDirDisk = 0; + eocd.m_numCentralDirRecordsThisDisk = numFiles; + eocd.m_numCentralDirRecords = numFiles; + eocd.m_centralDirectorySizeBytes = mergedStream.Tell() - cdirPos; + eocd.m_centralDirStartOffset = cdirPos; + eocd.m_commentLength = 0; + + if (!mergedStream.WriteExact(&eocd, sizeof(eocd))) + { + fprintf(stderr, "Error writing EOCD"); + return -1; + } + + mergedStream.Close(); + + return 0; +} diff --git a/MergeGPF/MergeGPF.vcxproj b/MergeGPF/MergeGPF.vcxproj new file mode 100644 index 0000000..808798b --- /dev/null +++ b/MergeGPF/MergeGPF.vcxproj @@ -0,0 +1,92 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {36DAF5FA-6ADB-4F20-9810-1610DE0AE653} + MergeGPF + 10.0.17763.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + Disabled + true + true + + + + + {6ec62b0f-9353-40a4-a510-3788f1368b33} + + + {15009625-1120-405e-8bba-69a16cd6713d} + + + + + + + + + \ No newline at end of file diff --git a/MergeGPF/MergeGPF.vcxproj.filters b/MergeGPF/MergeGPF.vcxproj.filters new file mode 100644 index 0000000..f3f177d --- /dev/null +++ b/MergeGPF/MergeGPF.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/PortabilityLayer/Android.mk b/PortabilityLayer/Android.mk index 554bcbb..18452c0 100644 --- a/PortabilityLayer/Android.mk +++ b/PortabilityLayer/Android.mk @@ -29,12 +29,14 @@ LOCAL_SRC_FILES := \ EllipsePlotter.cpp \ FileBrowserUI.cpp \ FileManager.cpp \ + FileSectionStream.cpp \ FontFamily.cpp \ FontManager.cpp \ FontRenderer.cpp \ GPArchive.cpp \ HostSuspendHook.cpp \ IconLoader.cpp \ + InflateStream.cpp \ InputManager.cpp \ LinePlotter.cpp \ MacBinary2.cpp \ diff --git a/PortabilityLayer/CFileStream.cpp b/PortabilityLayer/CFileStream.cpp index d25df47..f013edc 100644 --- a/PortabilityLayer/CFileStream.cpp +++ b/PortabilityLayer/CFileStream.cpp @@ -70,12 +70,7 @@ namespace PortabilityLayer if (!m_file) return false; - return fseek(m_file, static_cast(loc), SEEK_END) == 0; - } - - bool CFileStream::Truncate(GpUFilePos_t loc) - { - return false; + return fseek(m_file, -static_cast(loc), SEEK_END) == 0; } GpUFilePos_t CFileStream::Tell() const diff --git a/PortabilityLayer/CFileStream.h b/PortabilityLayer/CFileStream.h index 3ff4ba0..e24196e 100644 --- a/PortabilityLayer/CFileStream.h +++ b/PortabilityLayer/CFileStream.h @@ -24,7 +24,6 @@ namespace PortabilityLayer bool SeekStart(GpUFilePos_t loc) override; bool SeekCurrent(GpFilePos_t loc) override; bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; GpUFilePos_t Size() const override; GpUFilePos_t Tell() const override; void Close() override; diff --git a/PortabilityLayer/CombinedTimestamp.h b/PortabilityLayer/CombinedTimestamp.h index b639162..85ea457 100644 --- a/PortabilityLayer/CombinedTimestamp.h +++ b/PortabilityLayer/CombinedTimestamp.h @@ -6,7 +6,7 @@ namespace PortabilityLayer { struct CombinedTimestamp { - uint8_t m_utcTimestamp[8]; + uint8_t m_macEpochTimestamp[8]; uint8_t m_localYear[4]; uint8_t m_localMonth; @@ -18,26 +18,30 @@ namespace PortabilityLayer uint8_t m_padding[3]; - int64_t GetUTCTime() const; - void SetUTCTime(int64_t timestamp); + static const int32_t kMacEpochToUTC = 2082844800; + + int64_t GetMacEpochTime() const; + void SetMacEpochTime(int64_t timestamp); int32_t GetLocalYear() const; void SetLocalYear(int32_t year); + + void GetAsMSDOSTimestamp(uint16_t &msdosDate, uint16_t &msdosTime) const; }; - inline int64_t CombinedTimestamp::GetUTCTime() const + inline int64_t CombinedTimestamp::GetMacEpochTime() const { int64_t result = 0; for (int i = 0; i < 8; i++) - result |= static_cast(m_utcTimestamp[i]) << (i * 8); + result |= static_cast(m_macEpochTimestamp[i]) << (i * 8); return result; } - void CombinedTimestamp::SetUTCTime(int64_t timestamp) + inline void CombinedTimestamp::SetMacEpochTime(int64_t timestamp) { for (int i = 0; i < 8; i++) - m_utcTimestamp[i] = static_cast((timestamp >> (i * 8)) & 0xff); + m_macEpochTimestamp[i] = static_cast((timestamp >> (i * 8)) & 0xff); } inline int32_t CombinedTimestamp::GetLocalYear() const @@ -49,9 +53,45 @@ namespace PortabilityLayer return result; } - void CombinedTimestamp::SetLocalYear(int32_t timestamp) + inline void CombinedTimestamp::SetLocalYear(int32_t timestamp) { for (int i = 0; i < 4; i++) m_localYear[i] = static_cast((timestamp >> (i * 8)) & 0xff); } + + inline void CombinedTimestamp::GetAsMSDOSTimestamp(uint16_t &msdosDate, uint16_t &msdosTime) const + { + int32_t localYear = this->GetLocalYear(); + uint8_t month = this->m_localMonth; + uint8_t day = this->m_localDay; + + uint8_t hour = this->m_localHour; + uint8_t minute = this->m_localMinute; + uint8_t second = this->m_localSecond; + + int32_t yearsSince1980 = localYear - 1980; + + if (localYear < 1980) + { + // Time machine + yearsSince1980 = 0; + second = 0; + minute = 0; + hour = 0; + day = 1; + month = 1; + } + else if (localYear > 1980 + 127) + { + yearsSince1980 = 127; + second = 59; + minute = 59; + hour = 23; + day = 31; + month = 12; + } + + msdosTime = (second / 2) | (minute << 5) | (hour << 11); + msdosDate = day | (month << 5) | (yearsSince1980 << 9); + } } diff --git a/PortabilityLayer/DeflateCodec.cpp b/PortabilityLayer/DeflateCodec.cpp index 825e07a..b87b64c 100644 --- a/PortabilityLayer/DeflateCodec.cpp +++ b/PortabilityLayer/DeflateCodec.cpp @@ -109,6 +109,32 @@ namespace PortabilityLayer uint8_t m_flushBuffer[1024]; }; + + class InflateContextImpl final : public InflateContext + { + public: + static InflateContext *Create(); + + void Destroy() override; + + bool Append(const void *buffer, size_t size, size_t &sizeConsumed) override; + bool Read(void *buffer, size_t size, size_t &sizeRead) override; + + bool Reset() override; + + bool Init(); + + private: + InflateContextImpl(); + ~InflateContextImpl(); + + bool m_streamInitialized; + bool m_isEndOfStream; + z_stream m_zStream; + + uint8_t m_flushBuffer[1024]; + const uint8_t *m_readPos; + }; } PortabilityLayer::DeflateContextImpl::DeflateContextImpl(GpIOStream *stream, int compressionLevel) @@ -197,6 +223,131 @@ bool PortabilityLayer::DeflateContextImpl::Flush() } + +PortabilityLayer::InflateContext *PortabilityLayer::InflateContextImpl::Create() +{ + void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::InflateContextImpl)); + if (!storage) + return nullptr; + + InflateContextImpl *obj = new (storage) InflateContextImpl(); + if (!obj->Init()) + { + obj->Destroy(); + return nullptr; + } + + return obj; +} + +void PortabilityLayer::InflateContextImpl::Destroy() +{ + this->~InflateContextImpl(); + PortabilityLayer::MemoryManager::GetInstance()->Release(this); +} + +bool PortabilityLayer::InflateContextImpl::Append(const void *buffer, size_t size, size_t &sizeConsumed) +{ + size_t consumed = 0; + + m_zStream.avail_in = size; + m_zStream.next_in = static_cast(const_cast(buffer)); + + for (;;) + { + if (m_isEndOfStream) + { + m_zStream.avail_in = 0; + m_zStream.next_in = nullptr; + } + + if (m_zStream.avail_in == 0 || m_zStream.avail_out == 0) + { + sizeConsumed = consumed; + return true; + } + + size_t lastAvailIn = m_zStream.avail_in; + + int result = inflate(&m_zStream, Z_NO_FLUSH); + if (result == Z_STREAM_END) + m_isEndOfStream = true; + else if (result != Z_OK) + return false; + + consumed += lastAvailIn - m_zStream.avail_in; + } +} + +bool PortabilityLayer::InflateContextImpl::Read(void *buffer, size_t size, size_t &sizeRead) +{ + size_t amountInOutputBuffer = static_cast(m_zStream.next_out) - m_readPos; + + if (size > amountInOutputBuffer) + size = amountInOutputBuffer; + + if (size > 0) + { + if (buffer) + memcpy(buffer, m_readPos, size); + + m_readPos += size; + + if (m_readPos == m_zStream.next_out) + { + m_zStream.avail_out = sizeof(m_flushBuffer); + m_zStream.next_out = m_flushBuffer; + m_readPos = m_flushBuffer; + } + } + + sizeRead = size; + return true; +} + +bool PortabilityLayer::InflateContextImpl::Reset() +{ + if (inflateReset2(&m_zStream, -15) != Z_OK) + return false; + + m_isEndOfStream = false; + m_zStream.avail_out = sizeof(m_flushBuffer); + m_zStream.next_out = m_flushBuffer; + m_readPos = m_flushBuffer; + + return true; +} + +bool PortabilityLayer::InflateContextImpl::Init() +{ + m_zStream.zalloc = ZlibAllocShim; + m_zStream.zfree = ZlibFreeShim; + m_zStream.opaque = MemoryManager::GetInstance(); + + if (inflateInit2(&m_zStream, -15) != Z_OK) + return false; + + m_zStream.next_out = m_flushBuffer; + m_zStream.avail_out = sizeof(m_flushBuffer); + + m_streamInitialized = true; + return true; +} + +PortabilityLayer::InflateContextImpl::InflateContextImpl() + : m_streamInitialized(false) + , m_isEndOfStream(false) + , m_readPos(m_flushBuffer) +{ + memset(&m_zStream, 0, sizeof(m_zStream)); +} + +PortabilityLayer::InflateContextImpl::~InflateContextImpl() +{ + if (m_streamInitialized) + inflateEnd(&m_zStream); +} + PortabilityLayer::DeflateContext *PortabilityLayer::DeflateContext::Create(GpIOStream *stream, int compressionLevel) { void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::DeflateContextImpl)); @@ -217,3 +368,9 @@ uint32_t PortabilityLayer::DeflateContext::CRC32(uint32_t inputValue, const void { return crc32(inputValue, static_cast(buffer), bufferLength); } + + +PortabilityLayer::InflateContext *PortabilityLayer::InflateContext::Create() +{ + return InflateContextImpl::Create(); +} diff --git a/PortabilityLayer/DeflateCodec.h b/PortabilityLayer/DeflateCodec.h index c16b655..98ff4ce 100644 --- a/PortabilityLayer/DeflateCodec.h +++ b/PortabilityLayer/DeflateCodec.h @@ -19,6 +19,19 @@ namespace PortabilityLayer static uint32_t CRC32(uint32_t inputValue, const void *buffer, size_t bufferLength); }; + class InflateContext + { + public: + static InflateContext *Create(); + + virtual void Destroy() = 0; + + virtual bool Append(const void *buffer, size_t size, size_t &sizeConsumed) = 0; + virtual bool Read(void *buffer, size_t size, size_t &sizeRead) = 0; + + virtual bool Reset() = 0; + }; + class DeflateCodec { public: diff --git a/PortabilityLayer/FileBrowserUI.cpp b/PortabilityLayer/FileBrowserUI.cpp index 6f3b338..a425f9e 100644 --- a/PortabilityLayer/FileBrowserUI.cpp +++ b/PortabilityLayer/FileBrowserUI.cpp @@ -565,8 +565,10 @@ namespace PortabilityLayer return hit; } - bool FileBrowserUI::Prompt(Mode mode, VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) + bool FileBrowserUI::Prompt(Mode mode, VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) { + size_t extensionLength = strlen(extension); + int dialogID = 0; bool isObstructive = false; int windowHeight = 272; @@ -603,35 +605,18 @@ namespace PortabilityLayer { size_t nameLength = strlen(fileName); - if (nameLength < 4) + if (nameLength < extensionLength) continue; - const char *nameExt = fileName + (nameLength - 4); + const char *nameExt = fileName + (nameLength - extensionLength); - if (!memcmp(nameExt, ".gpf", 4)) + if (!memcmp(nameExt, extension, extensionLength)) { - GpIOStream *metadataStream = fs->OpenFile(dirID, fileName, false, GpFileCreationDispositions::kOpenExisting); - if (!metadataStream) + PLPasStr fnamePStr = PLPasStr(nameLength - extensionLength, fileName); + if (!callbackAPI.m_filterFileCallback(dirID, fnamePStr)) continue; - MacFilePropertiesSerialized serializedMetadata; - if (metadataStream->Read(&serializedMetadata, sizeof(serializedMetadata)) != sizeof(serializedMetadata)) - { - metadataStream->Close(); - continue; - } - - metadataStream->Close(); - - MacFileProperties metadata; - serializedMetadata.Deserialize(metadata); - - char ftype[4]; - fileType.ExportAsChars(ftype); - if (memcmp(metadata.m_fileType, ftype, 4)) - continue; - - if (!uiImpl.AppendName(fileName, nameLength - 4, callbackAPI.m_loadFileDetailsCallback(dirID, PLPasStr(nameLength - 4, fileName)))) + if (!uiImpl.AppendName(fileName, nameLength - extensionLength, callbackAPI.m_loadFileDetailsCallback(dirID, fnamePStr))) { dirCursor->Destroy(); return false; @@ -763,7 +748,11 @@ namespace PortabilityLayer { PLPasStr uiFileName = uiImpl.GetSelectedFileName(); - PortabilityLayer::FileManager::GetInstance()->DeleteFile(dirID, uiFileName); + if (composites) + PortabilityLayer::FileManager::GetInstance()->DeleteCompositeFile(dirID, uiFileName); + else + PortabilityLayer::FileManager::GetInstance()->DeleteNonCompositeFile(dirID, uiFileName, extension); + uiImpl.RemoveSelectedFile(); dialog->GetItems()[kOkayButton - 1].GetWidget()->SetEnabled(false); dialog->GetItems()[kDeleteButton - 1].GetWidget()->SetEnabled(false); diff --git a/PortabilityLayer/FileBrowserUI.h b/PortabilityLayer/FileBrowserUI.h index df3bb7f..ff3de18 100644 --- a/PortabilityLayer/FileBrowserUI.h +++ b/PortabilityLayer/FileBrowserUI.h @@ -20,6 +20,7 @@ namespace PortabilityLayer void *(*m_loadFileDetailsCallback)(VirtualDirectory_t dirID, const PLPasStr &filename); void(*m_freeFileDetailsCallback)(void *fileDetails); + bool(*m_filterFileCallback)(VirtualDirectory_t dirID, const PLPasStr &filename); }; class FileBrowserUI @@ -32,6 +33,6 @@ namespace PortabilityLayer Mode_Open, }; - static bool Prompt(Mode mode, VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI); + static bool Prompt(Mode mode, VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI); }; } diff --git a/PortabilityLayer/FileManager.cpp b/PortabilityLayer/FileManager.cpp index eb2003e..52cae37 100644 --- a/PortabilityLayer/FileManager.cpp +++ b/PortabilityLayer/FileManager.cpp @@ -1,5 +1,6 @@ #include "FileManager.h" +#include "CombinedTimestamp.h" #include "FileBrowserUI.h" #include "IGpFileSystem.h" #include "IGpSystemServices.h" @@ -7,6 +8,7 @@ #include "MacBinary2.h" #include "MacFileMem.h" #include "ResTypeID.h" +#include "ZipFileProxy.h" #include "PLDrivers.h" #include "PLPasStr.h" @@ -18,68 +20,180 @@ namespace PortabilityLayer { class VirtualFile; + class CompositeFileImpl; + + typedef char ExtendedFileName_t[64 + 4]; + + namespace FileManagerTools + { + bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension); + }; class FileManagerImpl final : public FileManager { public: + CompositeFile *OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) override; + PLError_t OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override; + bool FileExists(VirtualDirectory_t dirID, const PLPasStr &filename) override; - bool FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename) override; - bool DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename) override; + + bool DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext) override; + bool DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) override; PLError_t CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp) override; PLError_t CreateFileAtCurrentTime(VirtualDirectory_t dirID, const PLPasStr &filename, const ResTypeID &fileCreator, const ResTypeID &fileType) override; - PLError_t OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outRefNum) override; - PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outRefNum) override; - bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) override; - PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override; PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override; - bool PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override; - bool PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override; + bool PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override; + bool PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override; static FileManagerImpl *GetInstance(); private: - typedef char ExtendedFileName_t[64 + 4]; - PLError_t OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, GpIOStream *&outRefNum); PLError_t RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream); - static bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension); - static FileManagerImpl ms_instance; }; + class CompositeFileImpl final : public CompositeFile + { + public: + PLError_t OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream) override; + PLError_t OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared) override; + const MacFileProperties &GetProperties() const override; + + bool IsDataReadOnly() const override; + + void Close() override; + + static CompositeFileImpl *Create(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex); + + private: + CompositeFileImpl(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex); + ~CompositeFileImpl(); + + VirtualDirectory_t m_dirID; + PascalStr<255> m_filename; + GpIOStream *m_stream; + ZipFileProxy *m_zipFile; + MacFileProperties m_mfp; + size_t m_inlineDataIndex; + bool m_resInline; + bool m_dataInline; + }; + + + CompositeFile *FileManagerImpl::OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) + { + ExtendedFileName_t extFN; + if (!FileManagerTools::ConstructFilename(extFN, filename, ".gpf")) + return nullptr; + + GpIOStream *stream = PLDrivers::GetFileSystem()->OpenFile(dirID, extFN, false, GpFileCreationDispositions::kOpenExisting); + if (!stream) + return nullptr; + + ZipFileProxy *zipFile = ZipFileProxy::Create(stream); + if (!zipFile) + { + stream->Close(); + return nullptr; + } + + size_t metaIndex = 0; + if (!zipFile->IndexFile("!!meta", metaIndex)) + { + stream->Close(); + return nullptr; + } + + MacFilePropertiesSerialized mfps; + + GpIOStream *metaStream = zipFile->OpenFile(metaIndex); + if (!metaStream) + { + zipFile->Destroy(); + stream->Close(); + return nullptr; + } + + if (!metaStream->ReadExact(mfps.m_data, sizeof(mfps.m_data))) + { + metaStream->Close(); + zipFile->Destroy(); + stream->Close(); + return nullptr; + } + + metaStream->Close(); + + MacFileProperties mfp; + mfps.Deserialize(mfp); + + size_t dataIndex = 0; + bool hasData = zipFile->IndexFile("!data", dataIndex); + + size_t nonResFiles = 1 + (hasData ? 1 : 0); + bool hasResources = (zipFile->NumFiles() > nonResFiles); + + if (!hasData && !hasResources) + { + zipFile->Destroy(); + zipFile = nullptr; + + stream->Close(); + stream = nullptr; + } + + CompositeFile *compositeFile = CompositeFileImpl::Create(dirID, filename, stream, zipFile, mfp, hasResources, hasData, hasData ? dataIndex : 0); + if (!compositeFile) + { + if (zipFile) + zipFile->Destroy(); + + if (stream) + stream->Close(); + + return nullptr; + } + + return compositeFile; + } + + PLError_t FileManagerImpl::OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) + { + return RawOpenFileFork(dirID, filename, extension, filePermission, true, creationDisposition, outStream); + } + bool FileManagerImpl::FileExists(VirtualDirectory_t dirID, const PLPasStr &filename) { ExtendedFileName_t extFN; - if (!ConstructFilename(extFN, filename, ".gpf")) + if (!FileManagerTools::ConstructFilename(extFN, filename, ".gpf")) return false; return PLDrivers::GetFileSystem()->FileExists(dirID, extFN); } - bool FileManagerImpl::FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename) + bool FileManagerImpl::DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext) { - const char *exts[3] = { ".gpf", ".gpa", ".gpd" }; + ExtendedFileName_t extFN; + if (!FileManagerTools::ConstructFilename(extFN, filename, ext)) + return true; - for (int extIndex = 0; extIndex < sizeof(exts) / sizeof(exts[0]); extIndex++) + bool existed = false; + if (!PLDrivers::GetFileSystem()->DeleteFile(dirID, extFN, existed)) { - ExtendedFileName_t extFN; - if (!ConstructFilename(extFN, filename, exts[extIndex])) - return true; - - bool exists = false; - if (PLDrivers::GetFileSystem()->FileLocked(dirID, extFN, &exists) && exists) - return true; + if (!existed) + return false; } - return false; + return true; } - bool FileManagerImpl::DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename) + bool FileManagerImpl::DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) { const size_t numExts = 3; @@ -89,7 +203,7 @@ namespace PortabilityLayer for (int extIndex = 0; extIndex < numExts; extIndex++) { ExtendedFileName_t extFN; - if (!ConstructFilename(extFN, filename, exts[extIndex])) + if (!FileManagerTools::ConstructFilename(extFN, filename, exts[extIndex])) return true; bool existed = false; @@ -111,7 +225,7 @@ namespace PortabilityLayer serialized.Serialize(mfp); ExtendedFileName_t extFN; - if (!ConstructFilename(extFN, filename, ".gpf")) + if (!FileManagerTools::ConstructFilename(extFN, filename, ".gpf")) return PLErrors::kBadFileName; GpIOStream *stream = nullptr; @@ -119,7 +233,9 @@ namespace PortabilityLayer if (err) return err; - if (stream->Write(serialized.m_data, sizeof(serialized.m_data)) != sizeof(serialized.m_data)) + CombinedTimestamp ts; + + if (!serialized.WriteAsPackage(*stream, ts)) { stream->Close(); return PLErrors::kIOError; @@ -135,38 +251,11 @@ namespace PortabilityLayer MacFileProperties mfp; fileCreator.ExportAsChars(mfp.m_fileCreator); fileType.ExportAsChars(mfp.m_fileType); - mfp.m_creationDate = mfp.m_modifiedDate = PLDrivers::GetSystemServices()->GetTime(); + mfp.m_createdTimeMacEpoch = mfp.m_modifiedTimeMacEpoch = PLDrivers::GetSystemServices()->GetTime(); return CreateFile(dirID, filename, mfp); } - PLError_t FileManagerImpl::OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, GpIOStream *&outStream) - { - return OpenFileFork(dirID, filename, ".gpd", permission, outStream); - } - - PLError_t FileManagerImpl::OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, GpIOStream *&outStream) - { - return OpenFileFork(dirID, filename, ".gpa", permission, outStream); - } - - bool FileManagerImpl::ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) - { - GpIOStream *stream = nullptr; - PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, stream); - if (err) - return false; - - MacFilePropertiesSerialized serialized; - bool readOk = (stream->Read(serialized.m_data, MacFilePropertiesSerialized::kSize) == MacFilePropertiesSerialized::kSize); - stream->Close(); - - if (readOk) - serialized.Deserialize(properties); - - return readOk; - } - PLError_t FileManagerImpl::RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream) { return RawOpenFileFork(dirID, filename, ".gpd", permission, ignoreMeta, createDisposition, outStream); @@ -177,18 +266,18 @@ namespace PortabilityLayer return RawOpenFileFork(dirID, filename, ".gpa", permission, ignoreMeta, createDisposition, outStream); } - bool FileManagerImpl::PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &detailsAPI) + bool FileManagerImpl::PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &detailsAPI) { ExtendedFileName_t extFN; - if (!ConstructFilename(extFN, initialFileName, "")) + if (!FileManagerTools::ConstructFilename(extFN, initialFileName, "")) return false; - return FileBrowserUI::Prompt(FileBrowserUI::Mode_Save, dirID, fileType, path, outPathLength, pathCapacity, initialFileName, promptText, detailsAPI); + return FileBrowserUI::Prompt(FileBrowserUI::Mode_Save, dirID, extension, path, outPathLength, pathCapacity, initialFileName, promptText, composites, detailsAPI); } - bool FileManagerImpl::PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &detailsAPI) + bool FileManagerImpl::PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &detailsAPI) { - return FileBrowserUI::Prompt(FileBrowserUI::Mode_Open, dirID, fileType, path, outPathLength, pathCapacity, PSTR(""), promptText, detailsAPI); + return FileBrowserUI::Prompt(FileBrowserUI::Mode_Open, dirID, extension, path, outPathLength, pathCapacity, PSTR(""), promptText, composites, detailsAPI); } FileManagerImpl *FileManagerImpl::GetInstance() @@ -220,14 +309,14 @@ namespace PortabilityLayer if (!ignoreMeta) { - if (!ConstructFilename(gpfExtFN, filename, ".gpf")) + if (!FileManagerTools::ConstructFilename(gpfExtFN, filename, ".gpf")) return PLErrors::kBadFileName; if (!PLDrivers::GetFileSystem()->FileExists(dirID, gpfExtFN)) return PLErrors::kFileNotFound; } - if (!ConstructFilename(extFN, filename, ext)) + if (!FileManagerTools::ConstructFilename(extFN, filename, ext)) return PLErrors::kBadFileName; GpIOStream *fstream = nullptr; @@ -253,14 +342,30 @@ namespace PortabilityLayer } if (!fstream) + { + if (ignoreMeta) + { + if (!PLDrivers::GetFileSystem()->FileExists(dirID, extFN)) + return PLErrors::kFileNotFound; + } + return PLErrors::kAccessDenied; + } outStream = fstream; return PLErrors::kNone; } - bool FileManagerImpl::ConstructFilename(ExtendedFileName_t &extFN, const PLPasStr &fn, const char *extension) + FileManagerImpl FileManagerImpl::ms_instance; + + FileManager *FileManager::GetInstance() + { + return FileManagerImpl::GetInstance(); + } + + + bool FileManagerTools::ConstructFilename(ExtendedFileName_t &extFN, const PLPasStr &fn, const char *extension) { const size_t fnameSize = fn.Length(); if (fnameSize >= 64) @@ -275,10 +380,109 @@ namespace PortabilityLayer return true; } - FileManagerImpl FileManagerImpl::ms_instance; - FileManager *FileManager::GetInstance() + // ========================================================================== + PLError_t CompositeFileImpl::OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream) { - return FileManagerImpl::GetInstance(); + if (m_dataInline) + { + if (filePermission == EFilePermission_Any || filePermission == EFilePermission_Read) + { + GpIOStream *stream = m_zipFile->OpenFile(m_inlineDataIndex); + if (!stream) + return PLErrors::kIOError; + + outStream = stream; + return PLErrors::kNone; + } + else + return PLErrors::kAccessDenied; + } + else + return FileManager::GetInstance()->RawOpenFileData(m_dirID, m_filename.ToShortStr(), filePermission, true, disposition, outStream); + } + + PLError_t CompositeFileImpl::OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared) + { + if (m_resInline) + { + outStream = nullptr; + outProxy = m_zipFile; + outIsProxyShared = true; + return PLErrors::kNone; + } + else + { + GpIOStream *stream = nullptr; + PLError_t err = FileManager::GetInstance()->RawOpenFileResources(m_dirID, m_filename.ToShortStr(), EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, stream); + if (err != PLErrors::kNone) + return err; + + ZipFileProxy *proxy = ZipFileProxy::Create(stream); + if (!proxy) + { + stream->Close(); + return PLErrors::kIOError; + } + + outStream = stream; + outProxy = proxy; + outIsProxyShared = false; + return PLErrors::kNone; + } + } + + const MacFileProperties &CompositeFileImpl::GetProperties() const + { + return m_mfp; + } + + bool CompositeFileImpl::IsDataReadOnly() const + { + if (m_dataInline) + return true; + + ExtendedFileName_t extFN; + if (!FileManagerTools::ConstructFilename(extFN, m_filename.ToShortStr(), ".gpd")) + return false; + + bool exists = false; + return PLDrivers::GetFileSystem()->FileLocked(m_dirID, extFN, exists); + } + + void CompositeFileImpl::Close() + { + this->~CompositeFileImpl(); + free(this); + } + + CompositeFileImpl *CompositeFileImpl::Create(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex) + { + void *storage = malloc(sizeof(CompositeFileImpl)); + if (!storage) + return nullptr; + + return new (storage) CompositeFileImpl(dirID, filename, stream, zipFile, mfp, resInline, dataInline, inlineDataIndex); + } + + CompositeFileImpl::CompositeFileImpl(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex) + : m_dirID(dirID) + , m_filename(filename) + , m_stream(stream) + , m_zipFile(zipFile) + , m_mfp(mfp) + , m_resInline(resInline) + , m_dataInline(dataInline) + , m_inlineDataIndex(inlineDataIndex) + { + } + + CompositeFileImpl::~CompositeFileImpl() + { + if (m_zipFile) + m_zipFile->Destroy(); + + if (m_stream) + m_stream->Close(); } } diff --git a/PortabilityLayer/FileManager.h b/PortabilityLayer/FileManager.h index e23088a..4dc2c5a 100644 --- a/PortabilityLayer/FileManager.h +++ b/PortabilityLayer/FileManager.h @@ -17,27 +17,43 @@ namespace PortabilityLayer class ResTypeID; struct MacFileProperties; struct FileBrowserUI_DetailsCallbackAPI; + class ZipFileProxy; + + class CompositeFile + { + public: + virtual PLError_t OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream) = 0; + virtual PLError_t OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared) = 0; + virtual const MacFileProperties &GetProperties() const = 0; + + virtual bool IsDataReadOnly() const = 0; + + virtual void Close() = 0; + + protected: + inline CompositeFile() {} + inline ~CompositeFile() {} + }; class FileManager { public: + virtual CompositeFile *OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 0; + virtual PLError_t OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) = 0; + virtual bool FileExists(VirtualDirectory_t dirID, const PLPasStr &filename) = 0; - virtual bool FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename) = 0; - virtual bool DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 0; + + virtual bool DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext) = 0; + virtual bool DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 0; virtual PLError_t CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp) = 0; virtual PLError_t CreateFileAtCurrentTime(VirtualDirectory_t dirID, const PLPasStr &filename, const ResTypeID &fileCreator, const ResTypeID &fileType) = 0; - // OpenFileData + OpenFileResources require that the file already exists (i.e. has a .gpf), but the fork may not - virtual PLError_t OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outStream) = 0; - virtual PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outStream) = 0; - virtual bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) = 0; - virtual PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream) = 0; virtual PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream) = 0; - virtual bool PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0; - virtual bool PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0; + virtual bool PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0; + virtual bool PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0; static FileManager *GetInstance(); }; diff --git a/PortabilityLayer/FileSectionStream.cpp b/PortabilityLayer/FileSectionStream.cpp new file mode 100644 index 0000000..cdc2c16 --- /dev/null +++ b/PortabilityLayer/FileSectionStream.cpp @@ -0,0 +1,171 @@ +#include "FileSectionStream.h" + +#include +#include + +namespace PortabilityLayer +{ + class FileSectionStreamImpl final : public GpIOStream + { + public: + FileSectionStreamImpl(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size); + ~FileSectionStreamImpl(); + + size_t Read(void *bytesOut, size_t size) override; + size_t Write(const void *bytes, size_t size) override; + bool IsSeekable() const override; + bool IsReadOnly() const override; + bool IsWriteOnly() const override; + bool SeekStart(GpUFilePos_t loc) override; + bool SeekCurrent(GpFilePos_t loc) override; + bool SeekEnd(GpUFilePos_t loc) override; + GpUFilePos_t Size() const override; + GpUFilePos_t Tell() const override; + void Close() override; + void Flush() override; + + private: + GpIOStream *m_stream; + GpUFilePos_t m_start; + GpUFilePos_t m_size; + + GpUFilePos_t m_expectedPosition; + bool m_isSeekable; + }; + + FileSectionStreamImpl::FileSectionStreamImpl(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size) + : m_stream(stream) + , m_start(start) + , m_size(size) + , m_expectedPosition(start) + , m_isSeekable(stream->IsSeekable()) + { + } + + FileSectionStreamImpl::~FileSectionStreamImpl() + { + } + + size_t FileSectionStreamImpl::Read(void *bytesOut, size_t size) + { + if (m_stream->Tell() != m_expectedPosition) + { + if (!m_stream->SeekStart(m_expectedPosition)) + return 0; + } + + GpUFilePos_t localPos = m_expectedPosition - m_start; + GpUFilePos_t availableBytes = m_size - localPos; + + if (size > availableBytes) + size = static_cast(availableBytes); + + const size_t actuallyRead = m_stream->Read(bytesOut, size); + m_expectedPosition += actuallyRead; + + return actuallyRead; + } + + size_t FileSectionStreamImpl::Write(const void *bytes, size_t size) + { + return 0; + } + + bool FileSectionStreamImpl::IsSeekable() const + { + return m_isSeekable; + } + + bool FileSectionStreamImpl::IsReadOnly() const + { + return true; + } + + bool FileSectionStreamImpl::IsWriteOnly() const + { + return false; + } + + bool FileSectionStreamImpl::SeekStart(GpUFilePos_t loc) + { + if (loc == m_expectedPosition - m_start) + return true; + + if (!m_isSeekable) + return false; + + if (loc >= m_size) + return false; + else + { + m_expectedPosition = m_start + loc; + return true; + } + } + + bool FileSectionStreamImpl::SeekCurrent(GpFilePos_t loc) + { + GpUFilePos_t localPos = m_expectedPosition - m_start; + + if (loc < 0) + { + GpUFilePos_t negativePos = static_cast(-loc); + if (negativePos > localPos) + return false; + + m_expectedPosition -= negativePos; + return true; + } + else if (loc > 0) + { + GpUFilePos_t positivePos = static_cast(loc); + if (m_size - localPos < positivePos) + return false; + + m_expectedPosition += positivePos; + return true; + } + else + return true; + } + + bool FileSectionStreamImpl::SeekEnd(GpUFilePos_t loc) + { + if (loc > m_size) + return false; + + m_expectedPosition = m_start + (m_size - loc); + return true; + } + + GpUFilePos_t FileSectionStreamImpl::Size() const + { + return m_size; + } + + GpUFilePos_t FileSectionStreamImpl::Tell() const + { + return m_expectedPosition - m_start; + } + + void FileSectionStreamImpl::Close() + { + this->~FileSectionStreamImpl(); + free(this); + } + + void FileSectionStreamImpl::Flush() + { + m_stream->Flush(); + } + + GpIOStream *FileSectionStream::Create(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size) + { + void *storage = malloc(sizeof(FileSectionStreamImpl)); + + if (!storage) + return nullptr; + + return new (storage) FileSectionStreamImpl(stream, start, size); + } +} diff --git a/PortabilityLayer/FileSectionStream.h b/PortabilityLayer/FileSectionStream.h new file mode 100644 index 0000000..efdea2c --- /dev/null +++ b/PortabilityLayer/FileSectionStream.h @@ -0,0 +1,11 @@ +#pragma once + +#include "GpIOStream.h" + +namespace PortabilityLayer +{ + namespace FileSectionStream + { + GpIOStream *Create(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size); + }; +} diff --git a/PortabilityLayer/FontManager.cpp b/PortabilityLayer/FontManager.cpp index c7c1845..2cd1f8b 100644 --- a/PortabilityLayer/FontManager.cpp +++ b/PortabilityLayer/FontManager.cpp @@ -29,7 +29,6 @@ namespace PortabilityLayer FontFamily *GetHandwritingFont(int textSize, int variationFlags) const override; FontFamily *GetMonospaceFont(int textSize, int variationFlags) const override; - RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) override; RenderedFont *GetRenderedFontFromFamily(FontFamily *font, int size, bool aa, int flags) override; RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const override; @@ -51,7 +50,7 @@ namespace PortabilityLayer struct CachedRenderedFont { RenderedFont *m_rfont; - const IGpFont *m_font; + int m_fontCacheID; int m_size; uint32_t m_lastUsage; bool m_aa; @@ -59,6 +58,9 @@ namespace PortabilityLayer FontManagerImpl(); + bool FindOrReserveCacheSlot(int cacheID, int size, bool aa, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF); + void ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags); + void ResetUsageCounter(); static int CRFSortPredicate(const void *a, const void *b); @@ -147,7 +149,7 @@ namespace PortabilityLayer return m_monospaceFont; } - RenderedFont *FontManagerImpl::GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) + bool FontManagerImpl::FindOrReserveCacheSlot(int cacheID, int size, bool aa, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF) { CachedRenderedFont *newCacheSlot = &m_cachedRenderedFonts[0]; @@ -160,7 +162,7 @@ namespace PortabilityLayer break; } - if (crf.m_font == font && crf.m_size == size && crf.m_aa == aa) + if (crf.m_fontCacheID == cacheID && crf.m_size == size && crf.m_aa == aa) { crf.m_lastUsage = m_usageCounter; RenderedFont *rf = crf.m_rfont; @@ -169,52 +171,70 @@ namespace PortabilityLayer else m_usageCounter++; - return rf; + outRF = rf; + return true; } if (newCacheSlot->m_rfont != nullptr && crf.m_lastUsage < newCacheSlot->m_lastUsage) newCacheSlot = &crf; } - RenderedFont *rfont = FontRenderer::GetInstance()->RenderFont(font, size, aa, fontHacks); - if (!rfont) - return nullptr; + outCacheSlot = newCacheSlot; + return false; + } - if (newCacheSlot->m_rfont) - newCacheSlot->m_rfont->Destroy(); + void FontManagerImpl::ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags) + { + if (cacheSlot.m_rfont) + cacheSlot.m_rfont->Destroy(); - newCacheSlot->m_font = font; - newCacheSlot->m_lastUsage = m_usageCounter; - newCacheSlot->m_size = size; - newCacheSlot->m_rfont = rfont; - newCacheSlot->m_aa = aa; + cacheSlot.m_fontCacheID = cacheID; + cacheSlot.m_lastUsage = m_usageCounter; + cacheSlot.m_size = size; + cacheSlot.m_rfont = rfont; + cacheSlot.m_aa = aa; if (m_usageCounter == UINT32_MAX) ResetUsageCounter(); else m_usageCounter++; - - return rfont; } RenderedFont *FontManagerImpl::GetRenderedFontFromFamily(FontFamily *fontFamily, int size, bool aa, int flags) { PortabilityLayer::FontManager *fm = PortabilityLayer::FontManager::GetInstance(); - RenderedFont *rfont = fm->LoadCachedRenderedFont(fontFamily->GetCacheID(), size, aa, flags); - if (rfont) + RenderedFont *rfont = nullptr; + CachedRenderedFont *cacheSlot = nullptr; + int cacheID = fontFamily->GetCacheID(); + + if (this->FindOrReserveCacheSlot(cacheID, size, aa, cacheSlot, rfont)) return rfont; + rfont = fm->LoadCachedRenderedFont(cacheID, size, aa, flags); + if (rfont) + { + ReplaceCachedRenderedFont(*cacheSlot, rfont, cacheID, size, aa, flags); + return rfont; + } + const int variation = fontFamily->GetVariationForFlags(flags); IGpFont *hostFont = fontFamily->GetFontForVariation(variation); if (!hostFont) return nullptr; - rfont = fm->GetRenderedFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation)); + + rfont = FontRenderer::GetInstance()->RenderFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation)); if (rfont) + { fm->SaveCachedRenderedFont(rfont, fontFamily->GetCacheID(), size, aa, flags); + ReplaceCachedRenderedFont(*cacheSlot, rfont, cacheID, size, aa, flags); + + return rfont; + } + return rfont; } diff --git a/PortabilityLayer/FontManager.h b/PortabilityLayer/FontManager.h index 485646f..ea60800 100644 --- a/PortabilityLayer/FontManager.h +++ b/PortabilityLayer/FontManager.h @@ -20,7 +20,6 @@ namespace PortabilityLayer virtual FontFamily *GetHandwritingFont(int fontSize, int variationFlags) const = 0; virtual FontFamily *GetMonospaceFont(int fontSize, int variationFlags) const = 0; - virtual RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) = 0; virtual RenderedFont *GetRenderedFontFromFamily(FontFamily *fontFamily, int fontSize, bool aa, int flags) = 0; virtual RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const = 0; diff --git a/PortabilityLayer/GPArchive.cpp b/PortabilityLayer/GPArchive.cpp index 765dbb9..9dae6d1 100644 --- a/PortabilityLayer/GPArchive.cpp +++ b/PortabilityLayer/GPArchive.cpp @@ -27,7 +27,11 @@ static const char *gs_forbiddenNames[] = static bool IsCharForbidden(char c) { - return (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$' || c == '#'); + if ((c & 0x80) != 0) + return true; + + // <= '$' includes space, ! " # $ + return (c <= '$' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~'); } namespace PortabilityLayer diff --git a/PortabilityLayer/InflateStream.cpp b/PortabilityLayer/InflateStream.cpp new file mode 100644 index 0000000..a05d653 --- /dev/null +++ b/PortabilityLayer/InflateStream.cpp @@ -0,0 +1,250 @@ +#include "InflateStream.h" +#include "DeflateCodec.h" + +#include +#include + +namespace PortabilityLayer +{ + class InflateStreamImpl final : public GpIOStream + { + public: + InflateStreamImpl(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize, InflateContext *inflateContext); + InflateStreamImpl(); + + size_t Read(void *bytesOut, size_t size) override; + size_t Write(const void *bytes, size_t size) override; + bool IsSeekable() const override; + bool IsReadOnly() const override; + bool IsWriteOnly() const override; + bool SeekStart(GpUFilePos_t loc) override; + bool SeekCurrent(GpFilePos_t loc) override; + bool SeekEnd(GpUFilePos_t loc) override; + GpUFilePos_t Size() const override; + GpUFilePos_t Tell() const override; + void Close() override; + void Flush() override; + + private: + GpIOStream *m_stream; + InflateContext *m_inflateContext; + bool m_contextBroken; + + GpUFilePos_t m_start; + GpUFilePos_t m_compressedSize; + GpUFilePos_t m_compressedPos; + size_t m_decompressedSize; + size_t m_decompressedPos; + + uint8_t m_compressedInputBytes[1024]; + size_t m_compressedInputReadOffset; + size_t m_compressedInputSize; + }; + + + InflateStreamImpl::InflateStreamImpl(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize, InflateContext *inflateContext) + : m_stream(stream) + , m_start(start) + , m_compressedPos(start) + , m_compressedSize(compressedSize) + , m_decompressedSize(decompressedSize) + , m_decompressedPos(0) + , m_inflateContext(inflateContext) + , m_compressedInputReadOffset(0) + , m_compressedInputSize(0) + , m_contextBroken(false) + { + } + + InflateStreamImpl::InflateStreamImpl() + { + } + + size_t InflateStreamImpl::Read(void *bytesOut, size_t size) + { + if (m_contextBroken) + return 0; + + size_t sizeAvailable = m_decompressedSize - m_decompressedPos; + if (size > sizeAvailable) + size = sizeAvailable; + + size_t totalConsumed = 0; + + while (size > 0) + { + size_t sizeConsumed = 0; + if (!m_inflateContext->Read(bytesOut, size, sizeConsumed)) + { + m_contextBroken = true; + return 0; + } + + if (bytesOut) + bytesOut = static_cast(bytesOut) + sizeConsumed; + size -= sizeConsumed; + m_decompressedPos += sizeConsumed; + totalConsumed += sizeConsumed; + + if (sizeConsumed == 0) + { + if (m_compressedInputSize == m_compressedInputReadOffset) + { + if (m_compressedPos != m_stream->Tell()) + { + if (!m_stream->SeekStart(m_compressedPos)) + return 0; + } + + GpUFilePos_t compressedAvailable = m_compressedSize - (m_compressedPos - m_start); + if (compressedAvailable == 0) + return totalConsumed; + + size_t compressedToRead = sizeof(m_compressedInputBytes); + + if (compressedToRead > compressedAvailable) + compressedToRead = compressedAvailable; + + if (!m_stream->ReadExact(m_compressedInputBytes, compressedToRead)) + return 0; + + m_compressedInputReadOffset = 0; + m_compressedInputSize = compressedToRead; + m_compressedPos += compressedToRead; + } + + size_t compressedInputAvailable = m_compressedInputSize - m_compressedInputReadOffset; + size_t compressedInputConsumed = 0; + + if (!m_inflateContext->Append(m_compressedInputBytes + m_compressedInputReadOffset, compressedInputAvailable, compressedInputConsumed)) + { + m_contextBroken = true; + return 0; + } + + if (compressedInputConsumed == 0) + return 0; // This should never happen + + m_compressedInputReadOffset += compressedInputConsumed; + } + } + + return totalConsumed; + } + + size_t InflateStreamImpl::Write(const void *bytes, size_t size) + { + return 0; + } + + bool InflateStreamImpl::IsSeekable() const + { + return true; + } + + bool InflateStreamImpl::IsReadOnly() const + { + return true; + } + + bool InflateStreamImpl::IsWriteOnly() const + { + return false; + } + + bool InflateStreamImpl::SeekStart(GpUFilePos_t loc) + { + if (m_contextBroken) + return false; + + if (loc > m_decompressedSize) + return false; + + if (loc < m_decompressedPos) + { + if (!m_inflateContext->Reset()) + { + m_contextBroken = true; + return false; + } + + m_decompressedPos = 0; + m_compressedInputReadOffset = 0; + m_compressedInputSize = 0; + m_compressedPos = m_start; + } + + size_t skipAhead = static_cast(loc) - m_decompressedPos; + + return this->Read(nullptr, skipAhead) == skipAhead; + } + + bool InflateStreamImpl::SeekCurrent(GpFilePos_t loc) + { + if (m_contextBroken) + return false; + + if (loc < 0) + { + GpUFilePos_t negativePos = static_cast(-loc); + if (negativePos > m_decompressedPos) + return false; + + return SeekStart(m_decompressedPos - negativePos); + } + else if (loc > 0) + { + GpUFilePos_t positivePos = static_cast(-loc); + if (positivePos > m_decompressedSize - m_decompressedPos) + return false; + + return this->Read(nullptr, static_cast(positivePos)); + } + else + return true; + } + + bool InflateStreamImpl::SeekEnd(GpUFilePos_t loc) + { + if (loc > m_decompressedSize) + return false; + + return this->SeekStart(m_decompressedSize - loc); + } + + GpUFilePos_t InflateStreamImpl::Size() const + { + return m_decompressedSize; + } + + GpUFilePos_t InflateStreamImpl::Tell() const + { + return m_decompressedPos; + } + + void InflateStreamImpl::Close() + { + this->~InflateStreamImpl(); + free(this); + } + + void InflateStreamImpl::Flush() + { + } + + GpIOStream *InflateStream::Create(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize) + { + InflateContext *inflateContext = InflateContext::Create(); + if (!inflateContext) + return nullptr; + + void *storage = malloc(sizeof(InflateStreamImpl)); + if (!storage) + { + inflateContext->Destroy(); + return nullptr; + } + + return new (storage) InflateStreamImpl(stream, start, compressedSize, decompressedSize, inflateContext); + } +} diff --git a/PortabilityLayer/InflateStream.h b/PortabilityLayer/InflateStream.h new file mode 100644 index 0000000..da21d17 --- /dev/null +++ b/PortabilityLayer/InflateStream.h @@ -0,0 +1,11 @@ +#pragma once + +#include "GpIOStream.h" + +namespace PortabilityLayer +{ + namespace InflateStream + { + GpIOStream *Create(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize); + }; +} diff --git a/PortabilityLayer/MacBinary2.cpp b/PortabilityLayer/MacBinary2.cpp index caf5d57..61b3c3f 100644 --- a/PortabilityLayer/MacBinary2.cpp +++ b/PortabilityLayer/MacBinary2.cpp @@ -24,7 +24,7 @@ namespace const unsigned int Protected = 81; const unsigned int DataForkSize = 83; const unsigned int ResourceForkSize = 87; - const unsigned int CreationDate = 91; + const unsigned int CreatedDate = 91; const unsigned int ModifiedDate = 95; const unsigned int CommentLength = 99; const unsigned int FinderFlagsLow = 101; @@ -75,8 +75,8 @@ namespace PortabilityLayer mb2Header[MB2FileOffsets::Protected] = fileInfo.m_properties.m_protected; BytePack::BigUInt32(mb2Header + MB2FileOffsets::DataForkSize, fileInfo.m_dataForkSize); BytePack::BigUInt32(mb2Header + MB2FileOffsets::ResourceForkSize, fileInfo.m_resourceForkSize); - BytePack::BigUInt32(mb2Header + MB2FileOffsets::CreationDate, static_cast(fileInfo.m_properties.m_creationDate)); - BytePack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate, static_cast(fileInfo.m_properties.m_modifiedDate)); + BytePack::BigUInt32(mb2Header + MB2FileOffsets::CreatedDate, static_cast(fileInfo.m_properties.m_createdTimeMacEpoch)); + BytePack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate, static_cast(fileInfo.m_properties.m_modifiedTimeMacEpoch)); BytePack::BigUInt16(mb2Header + MB2FileOffsets::CommentLength, fileInfo.m_commentSize); mb2Header[MB2FileOffsets::FinderFlagsLow] = static_cast(fileInfo.m_properties.m_finderFlags & 0xff); @@ -130,8 +130,8 @@ namespace PortabilityLayer fileInfo.m_properties.m_protected = mb2Header[MB2FileOffsets::Protected]; fileInfo.m_dataForkSize = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::DataForkSize); fileInfo.m_resourceForkSize = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ResourceForkSize); - fileInfo.m_properties.m_creationDate = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::CreationDate); - fileInfo.m_properties.m_modifiedDate = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate); + fileInfo.m_properties.m_createdTimeMacEpoch = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::CreatedDate); + fileInfo.m_properties.m_modifiedTimeMacEpoch = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate); fileInfo.m_commentSize = ByteUnpack::BigUInt16(mb2Header + MB2FileOffsets::CommentLength); fileInfo.m_properties.m_finderFlags |= mb2Header[MB2FileOffsets::FinderFlagsLow]; diff --git a/PortabilityLayer/MacFileInfo.cpp b/PortabilityLayer/MacFileInfo.cpp index 25c852a..cb52185 100644 --- a/PortabilityLayer/MacFileInfo.cpp +++ b/PortabilityLayer/MacFileInfo.cpp @@ -1,63 +1,185 @@ -#include "MacFileInfo.h" -#include "PLBigEndian.h" - -#include - -namespace PortabilityLayer -{ - - static const unsigned int kOffsetFileType = 0; - static const unsigned int kOffsetFileCreator = 4; - static const unsigned int kOffsetXPos = 8; - static const unsigned int kOffsetYPos = 10; - static const unsigned int kOffsetFinderFlags = 12; - static const unsigned int kProtected = 14; - static const unsigned int kCreationDate = 15; - static const unsigned int kModifiedDate = 19; - - static const unsigned int kSize = 23; - - uint8_t m_data[kSize]; - - void MacFilePropertiesSerialized::Deserialize(MacFileProperties &props) const - { - memcpy(props.m_fileType, m_data + kOffsetFileType, 4); - memcpy(props.m_fileCreator, m_data + kOffsetFileCreator, 4); - memcpy(&props.m_xPos, m_data + kOffsetXPos, 2); - memcpy(&props.m_yPos, m_data + kOffsetYPos, 2); - memcpy(&props.m_finderFlags, m_data + kOffsetFinderFlags, 2); - memcpy(&props.m_protected, m_data + kProtected, 1); - memcpy(&props.m_creationDate, m_data + kCreationDate, 8); - memcpy(&props.m_modifiedDate, m_data + kModifiedDate, 8); - - PortabilityLayer::ByteSwap::BigInt16(props.m_xPos); - PortabilityLayer::ByteSwap::BigInt16(props.m_yPos); - PortabilityLayer::ByteSwap::BigUInt16(props.m_finderFlags); - PortabilityLayer::ByteSwap::BigInt64(props.m_creationDate); - PortabilityLayer::ByteSwap::BigInt64(props.m_modifiedDate); - } - - void MacFilePropertiesSerialized::Serialize(const MacFileProperties &props) - { - int16_t xPos = props.m_xPos; - int16_t yPos = props.m_yPos; - uint16_t finderFlags = props.m_finderFlags; - uint64_t creationDate = props.m_creationDate; - uint64_t modifiedDate = props.m_modifiedDate; - - PortabilityLayer::ByteSwap::BigInt16(xPos); - PortabilityLayer::ByteSwap::BigInt16(yPos); - PortabilityLayer::ByteSwap::BigUInt16(finderFlags); - PortabilityLayer::ByteSwap::BigUInt64(creationDate); - PortabilityLayer::ByteSwap::BigUInt64(modifiedDate); - - memcpy(m_data + kOffsetFileType, props.m_fileType, 4); - memcpy(m_data + kOffsetFileCreator, props.m_fileCreator, 4); - memcpy(m_data + kOffsetXPos, &xPos, 2); - memcpy(m_data + kOffsetYPos, &yPos, 2); - memcpy(m_data + kOffsetFinderFlags, &finderFlags, 2); - memcpy(m_data + kProtected, &props.m_protected, 1); - memcpy(m_data + kCreationDate, &creationDate, 8); - memcpy(m_data + kModifiedDate, &modifiedDate, 8); - } -} +#include "MacFileInfo.h" + +#include "DeflateCodec.h" +#include "GpIOStream.h" +#include "ZipFile.h" +#include "PLBigEndian.h" +#include "CombinedTimestamp.h" + +#include + +namespace PortabilityLayer +{ + void MacFilePropertiesSerialized::Deserialize(MacFileProperties &props) const + { + memcpy(props.m_fileType, m_data + kOffsetFileType, 4); + memcpy(props.m_fileCreator, m_data + kOffsetFileCreator, 4); + memcpy(&props.m_xPos, m_data + kOffsetXPos, 2); + memcpy(&props.m_yPos, m_data + kOffsetYPos, 2); + memcpy(&props.m_finderFlags, m_data + kOffsetFinderFlags, 2); + memcpy(&props.m_protected, m_data + kOffsetProtected, 1); + memcpy(&props.m_createdTimeMacEpoch, m_data + kOffsetCreatedDate, 8); + memcpy(&props.m_modifiedTimeMacEpoch, m_data + kOffsetModifiedDate, 8); + + PortabilityLayer::ByteSwap::BigInt16(props.m_xPos); + PortabilityLayer::ByteSwap::BigInt16(props.m_yPos); + PortabilityLayer::ByteSwap::BigUInt16(props.m_finderFlags); + PortabilityLayer::ByteSwap::BigInt64(props.m_createdTimeMacEpoch); + PortabilityLayer::ByteSwap::BigInt64(props.m_modifiedTimeMacEpoch); + } + + void MacFilePropertiesSerialized::Serialize(const MacFileProperties &props) + { + int16_t xPos = props.m_xPos; + int16_t yPos = props.m_yPos; + uint16_t finderFlags = props.m_finderFlags; + uint64_t createdDate = props.m_createdTimeMacEpoch; + uint64_t modifiedDate = props.m_modifiedTimeMacEpoch; + + PortabilityLayer::ByteSwap::BigInt16(xPos); + PortabilityLayer::ByteSwap::BigInt16(yPos); + PortabilityLayer::ByteSwap::BigUInt16(finderFlags); + PortabilityLayer::ByteSwap::BigUInt64(createdDate); + PortabilityLayer::ByteSwap::BigUInt64(modifiedDate); + + memcpy(m_data + kOffsetFileType, props.m_fileType, 4); + memcpy(m_data + kOffsetFileCreator, props.m_fileCreator, 4); + memcpy(m_data + kOffsetXPos, &xPos, 2); + memcpy(m_data + kOffsetYPos, &yPos, 2); + memcpy(m_data + kOffsetFinderFlags, &finderFlags, 2); + memcpy(m_data + kOffsetProtected, &props.m_protected, 1); + memcpy(m_data + kOffsetCreatedDate, &createdDate, 8); + memcpy(m_data + kOffsetModifiedDate, &modifiedDate, 8); + } + + bool MacFilePropertiesSerialized::WriteAsPackage(GpIOStream &stream, const CombinedTimestamp &ts) const + { + if (!WriteIsolated(stream, ts)) + return false; + + const char *packagedName = GetPackagedName(); + + uint16_t msdosDate, msdosTime; + ts.GetAsMSDOSTimestamp(msdosDate, msdosTime); + + ZipCentralDirectoryFileHeader cdh; + cdh.m_signature = ZipCentralDirectoryFileHeader::kSignature; + cdh.m_versionCreated = ZipConstants::kCompressedRequiredVersion; + cdh.m_versionRequired = ZipConstants::kStoredRequiredVersion; + cdh.m_flags = 0; + cdh.m_method = ZipConstants::kStoredMethod; + cdh.m_modificationTime = msdosTime; + cdh.m_modificationDate = msdosDate; + cdh.m_crc = PortabilityLayer::DeflateContext::CRC32(0, m_data, sizeof(m_data)); + cdh.m_compressedSize = sizeof(m_data); + cdh.m_uncompressedSize = sizeof(m_data); + cdh.m_fileNameLength = strlen(packagedName); + cdh.m_extraFieldLength = 0; + cdh.m_commentLength = 0; + cdh.m_diskNumber = 0; + cdh.m_internalAttributes = ZipConstants::kArchivedAttributes; + cdh.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes; + cdh.m_localHeaderOffset = 0; + + if (!stream.WriteExact(&cdh, sizeof(cdh))) + return false; + + if (!stream.WriteExact(packagedName, strlen(packagedName))) + return false; + + ZipEndOfCentralDirectoryRecord eod; + eod.m_signature = ZipEndOfCentralDirectoryRecord::kSignature; + eod.m_thisDiskNumber = 0; + eod.m_centralDirDisk = 0; + eod.m_numCentralDirRecordsThisDisk = 1; + eod.m_numCentralDirRecords = 1; + eod.m_centralDirectorySizeBytes = sizeof(ZipCentralDirectoryFileHeader) + strlen(packagedName); + eod.m_centralDirStartOffset = sizeof(ZipFileLocalHeader) + strlen(packagedName) + sizeof(m_data); + eod.m_commentLength = 0; + + if (stream.Write(&eod, sizeof(eod)) != sizeof(eod)) + return false; + + return true; + } + + bool MacFilePropertiesSerialized::WriteIsolated(GpIOStream &stream, const CombinedTimestamp &ts) const + { + static const char *packagedName = GetPackagedName(); + + ZipFileLocalHeader lh; + static const uint32_t kSignature = 0x04034b50; + + uint16_t msdosDate, msdosTime; + ts.GetAsMSDOSTimestamp(msdosDate, msdosTime); + + lh.m_signature = ZipFileLocalHeader::kSignature; + lh.m_versionRequired = ZipConstants::kStoredRequiredVersion; + lh.m_flags = 0; + lh.m_method = ZipConstants::kStoredMethod; + lh.m_modificationTime = msdosTime; + lh.m_modificationDate = msdosDate; + lh.m_crc = DeflateContext::CRC32(0, m_data, sizeof(m_data)); + lh.m_compressedSize = sizeof(m_data); + lh.m_uncompressedSize = sizeof(m_data); + lh.m_fileNameLength = strlen(packagedName); + lh.m_extraFieldLength = 0; + + if (stream.Write(&lh, sizeof(lh)) != sizeof(lh)) + return false; + + if (stream.Write(packagedName, strlen(packagedName)) != strlen(packagedName)) + return false; + + if (stream.Write(m_data, sizeof(m_data)) != sizeof(m_data)) + return false; + + return true; + } + + bool MacFilePropertiesSerialized::ReadFromPackage(GpIOStream &stream) + { + const char *packagedName = GetPackagedName(); + + ZipFileLocalHeader lh; + if (stream.Read(&lh, sizeof(lh)) != sizeof(lh)) + return false; + + if (lh.m_signature != ZipFileLocalHeader::kSignature) + return false; + if (lh.m_versionRequired != ZipConstants::kStoredRequiredVersion) + return false; + if (lh.m_flags != 0) + return false; + if (lh.m_method != ZipConstants::kStoredMethod) + return false; + + if (lh.m_compressedSize != sizeof(m_data)) + return false; + + if (lh.m_uncompressedSize != sizeof(m_data)) + return false; + + if (lh.m_fileNameLength != strlen(packagedName)) + return false; + + if (lh.m_extraFieldLength != 0) + return false; + + if (!stream.SeekCurrent(lh.m_fileNameLength)) + return false; + + if (stream.Read(m_data, sizeof(m_data)) != sizeof(m_data)) + return false; + + if (lh.m_crc != DeflateContext::CRC32(0, m_data, sizeof(m_data))) + return false; + + return true; + } + + const char *MacFilePropertiesSerialized::GetPackagedName() + { + return "!!meta"; + } +} diff --git a/PortabilityLayer/MacFileInfo.h b/PortabilityLayer/MacFileInfo.h index 564434b..4db98fb 100644 --- a/PortabilityLayer/MacFileInfo.h +++ b/PortabilityLayer/MacFileInfo.h @@ -1,86 +1,96 @@ -#pragma once - -#include "DataTypes.h" -#include "PascalStr.h" - -namespace PortabilityLayer -{ - enum FinderFileFlags - { - FINDER_FILE_FLAG_LOCKED = (1 << 15), - FINDER_FILE_FLAG_INVISIBLE = (1 << 14), - FINDER_FILE_FLAG_BUNDLE = (1 << 13), - FINDER_FILE_FLAG_SYSTEM = (1 << 12), - FINDER_FILE_FLAG_COPY_PROTECTED = (1 << 11), - FINDER_FILE_FLAG_BUSY = (1 << 10), - FINDER_FILE_FLAG_CHANGED = (1 << 9), - FINDER_FILE_FLAG_INITED = (1 << 8), - }; - - struct MacFileProperties - { - MacFileProperties(); - - char m_fileType[4]; - char m_fileCreator[4]; - int16_t m_xPos; - int16_t m_yPos; - uint16_t m_finderFlags; - uint8_t m_protected; - int64_t m_creationDate; - int64_t m_modifiedDate; - }; - - struct MacFilePropertiesSerialized - { - static const unsigned int kOffsetFileType = 0; - static const unsigned int kOffsetFileCreator = 4; - static const unsigned int kOffsetXPos = 8; - static const unsigned int kOffsetYPos = 10; - static const unsigned int kOffsetFinderFlags = 12; - static const unsigned int kProtected = 14; - static const unsigned int kCreationDate = 15; - static const unsigned int kModifiedDate = 23; - - static const unsigned int kSize = 31; - - uint8_t m_data[kSize]; - - void Deserialize(MacFileProperties &props) const; - void Serialize(const MacFileProperties &props); - }; - - struct MacFileInfo - { - MacFileInfo(); - - PascalStr<64> m_fileName; - uint16_t m_commentSize; - uint32_t m_dataForkSize; - uint32_t m_resourceForkSize; - - MacFileProperties m_properties; - }; -} - -namespace PortabilityLayer -{ - inline MacFileProperties::MacFileProperties() - : m_xPos(0) - , m_yPos(0) - , m_finderFlags(0) - , m_protected(0) - , m_creationDate(0) - , m_modifiedDate(0) - { - m_fileType[0] = m_fileType[1] = m_fileType[2] = m_fileType[3] = '\0'; - m_fileCreator[0] = m_fileCreator[1] = m_fileCreator[2] = m_fileCreator[3] = '\0'; - } - - inline MacFileInfo::MacFileInfo() - : m_dataForkSize(0) - , m_resourceForkSize(0) - , m_commentSize(0) - { - } -} +#pragma once + +#include "DataTypes.h" +#include "PascalStr.h" + +class GpIOStream; + +namespace PortabilityLayer +{ + struct CombinedTimestamp; + + enum FinderFileFlags + { + FINDER_FILE_FLAG_LOCKED = (1 << 15), + FINDER_FILE_FLAG_INVISIBLE = (1 << 14), + FINDER_FILE_FLAG_BUNDLE = (1 << 13), + FINDER_FILE_FLAG_SYSTEM = (1 << 12), + FINDER_FILE_FLAG_COPY_PROTECTED = (1 << 11), + FINDER_FILE_FLAG_BUSY = (1 << 10), + FINDER_FILE_FLAG_CHANGED = (1 << 9), + FINDER_FILE_FLAG_INITED = (1 << 8), + }; + + struct MacFileProperties + { + MacFileProperties(); + + char m_fileType[4]; + char m_fileCreator[4]; + int16_t m_xPos; + int16_t m_yPos; + uint16_t m_finderFlags; + uint8_t m_protected; + int64_t m_createdTimeMacEpoch; + int64_t m_modifiedTimeMacEpoch; + }; + + struct MacFilePropertiesSerialized + { + static const unsigned int kOffsetFileType = 0; + static const unsigned int kOffsetFileCreator = 4; + static const unsigned int kOffsetXPos = 8; + static const unsigned int kOffsetYPos = 10; + static const unsigned int kOffsetFinderFlags = 12; + static const unsigned int kOffsetProtected = 14; + static const unsigned int kOffsetCreatedDate = 15; + static const unsigned int kOffsetModifiedDate = 23; + + static const unsigned int kSize = 31; + + uint8_t m_data[kSize]; + + void Deserialize(MacFileProperties &props) const; + void Serialize(const MacFileProperties &props); + + bool WriteAsPackage(GpIOStream &stream, const CombinedTimestamp &ts) const; + bool WriteIsolated(GpIOStream &stream, const CombinedTimestamp &ts) const; + bool ReadFromPackage(GpIOStream &stream); + + static const char *GetPackagedName(); + }; + + struct MacFileInfo + { + MacFileInfo(); + + PascalStr<64> m_fileName; + uint16_t m_commentSize; + uint32_t m_dataForkSize; + uint32_t m_resourceForkSize; + + MacFileProperties m_properties; + }; +} + +namespace PortabilityLayer +{ + inline MacFileProperties::MacFileProperties() + : m_xPos(0) + , m_yPos(0) + , m_finderFlags(0) + , m_protected(0) + , m_createdTimeMacEpoch(0) + , m_modifiedTimeMacEpoch(0) + { + m_fileType[0] = m_fileType[1] = m_fileType[2] = m_fileType[3] = '\0'; + m_fileCreator[0] = m_fileCreator[1] = m_fileCreator[2] = m_fileCreator[3] = '\0'; + } + + inline MacFileInfo::MacFileInfo() + : m_dataForkSize(0) + , m_resourceForkSize(0) + , m_commentSize(0) + { + } +} diff --git a/PortabilityLayer/MemReaderStream.cpp b/PortabilityLayer/MemReaderStream.cpp index 27bd2fb..cf051b6 100644 --- a/PortabilityLayer/MemReaderStream.cpp +++ b/PortabilityLayer/MemReaderStream.cpp @@ -1,125 +1,120 @@ #include "MemReaderStream.h" -#include "MemoryManager.h" - -#include - -namespace PortabilityLayer -{ - MemReaderStream::MemReaderStream(const void *memStream, size_t size) - : m_bytes(static_cast(memStream)) - , m_size(size) - , m_loc(0) - { +#include "MemoryManager.h" + +#include + +namespace PortabilityLayer +{ + MemReaderStream::MemReaderStream(const void *memStream, size_t size) + : m_bytes(static_cast(memStream)) + , m_size(size) + , m_loc(0) + { } MemReaderStream::~MemReaderStream() - { - } - - size_t MemReaderStream::Read(void *bytesOut, size_t size) - { - size_t available = m_size - m_loc; - if (size > available) - size = available; - - memcpy(bytesOut, m_bytes + m_loc, size); - m_loc += size; - - return size; - } - - size_t MemReaderStream::Write(const void *bytes, size_t size) - { - return 0; - } - - bool IsSeekable() - { - return true; - } - - bool MemReaderStream::IsSeekable() const - { - return true; - } - - bool MemReaderStream::IsReadOnly() const - { - return true; - } - - bool MemReaderStream::IsWriteOnly() const - { - return false; - } - - bool MemReaderStream::SeekStart(GpUFilePos_t loc) - { - if (loc > m_size) - m_loc = m_size; - else - m_loc = static_cast(loc); - - return true; - } - - bool MemReaderStream::SeekCurrent(GpFilePos_t loc) - { - if (loc < 0) - { - if (static_cast(m_loc) + loc < 0) - m_loc = 0; - else - m_loc = static_cast(static_cast(m_loc) + loc); - } - else - { - const size_t available = m_size - m_loc; - if (static_cast(loc) > available) - m_loc = m_size; - else - m_loc = static_cast(static_cast(m_loc) + loc); - } - - return true; - } - - bool MemReaderStream::SeekEnd(GpUFilePos_t loc) - { - if (m_size < loc) - m_loc = 0; - else - m_loc = m_size - static_cast(loc); - - return true; - } - - bool MemReaderStream::Truncate(GpUFilePos_t loc) - { - return false; - } - - GpUFilePos_t MemReaderStream::Size() const - { - return m_size; - } - - GpUFilePos_t MemReaderStream::Tell() const - { - return static_cast(m_loc); - } - - void MemReaderStream::Close() - { - } - - void MemReaderStream::Flush() - { + { + } + + size_t MemReaderStream::Read(void *bytesOut, size_t size) + { + size_t available = m_size - m_loc; + if (size > available) + size = available; + + memcpy(bytesOut, m_bytes + m_loc, size); + m_loc += size; + + return size; + } + + size_t MemReaderStream::Write(const void *bytes, size_t size) + { + return 0; + } + + bool IsSeekable() + { + return true; + } + + bool MemReaderStream::IsSeekable() const + { + return true; + } + + bool MemReaderStream::IsReadOnly() const + { + return true; + } + + bool MemReaderStream::IsWriteOnly() const + { + return false; + } + + bool MemReaderStream::SeekStart(GpUFilePos_t loc) + { + if (loc > m_size) + m_loc = m_size; + else + m_loc = static_cast(loc); + + return true; + } + + bool MemReaderStream::SeekCurrent(GpFilePos_t loc) + { + if (loc < 0) + { + if (static_cast(m_loc) + loc < 0) + m_loc = 0; + else + m_loc = static_cast(static_cast(m_loc) + loc); + } + else + { + const size_t available = m_size - m_loc; + if (static_cast(loc) > available) + m_loc = m_size; + else + m_loc = static_cast(static_cast(m_loc) + loc); + } + + return true; + } + + bool MemReaderStream::SeekEnd(GpUFilePos_t loc) + { + if (m_size < loc) + m_loc = 0; + else + m_loc = m_size - static_cast(loc); + + return true; + } + + GpUFilePos_t MemReaderStream::Size() const + { + return m_size; + } + + GpUFilePos_t MemReaderStream::Tell() const + { + return static_cast(m_loc); + } + + void MemReaderStream::Close() + { + } + + void MemReaderStream::Flush() + { } MemBufferReaderStream::~MemBufferReaderStream() - { + { if (m_buffer) MemoryManager::GetInstance()->Release(m_buffer); } @@ -132,7 +127,7 @@ namespace PortabilityLayer return new (storage) MemBufferReaderStream(buffer, size); } - + void MemBufferReaderStream::Close() { this->~MemBufferReaderStream(); @@ -144,4 +139,4 @@ namespace PortabilityLayer , m_buffer(buffer) { } -} +} diff --git a/PortabilityLayer/MemReaderStream.h b/PortabilityLayer/MemReaderStream.h index 6962afa..b73dc9b 100644 --- a/PortabilityLayer/MemReaderStream.h +++ b/PortabilityLayer/MemReaderStream.h @@ -1,55 +1,54 @@ -#pragma once -#ifndef __PL_MEM_READER_STREAM_H__ -#define __PL_MEM_READER_STREAM_H__ - -#include "CoreDefs.h" -#include "GpIOStream.h" - -namespace PortabilityLayer -{ - class MemReaderStream : public GpIOStream - { - public: +#pragma once +#ifndef __PL_MEM_READER_STREAM_H__ +#define __PL_MEM_READER_STREAM_H__ + +#include "CoreDefs.h" +#include "GpIOStream.h" + +namespace PortabilityLayer +{ + class MemReaderStream : public GpIOStream + { + public: MemReaderStream(const void *memStream, size_t size); - virtual ~MemReaderStream(); - - size_t Read(void *bytesOut, size_t size) override; - size_t Write(const void *bytes, size_t size) override; - bool IsSeekable() const override; - bool IsReadOnly() const override; - bool IsWriteOnly() const override; - bool SeekStart(GpUFilePos_t loc) override; - bool SeekCurrent(GpFilePos_t loc) override; - bool SeekEnd(GpUFilePos_t loc) override; - bool Truncate(GpUFilePos_t loc) override; - GpUFilePos_t Size() const override; - GpUFilePos_t Tell() const override; - void Close() override; - void Flush() override; - - private: - MemReaderStream() GP_DELETED; - - const uint8_t *m_bytes; - size_t m_size; - size_t m_loc; + virtual ~MemReaderStream(); + + size_t Read(void *bytesOut, size_t size) override; + size_t Write(const void *bytes, size_t size) override; + bool IsSeekable() const override; + bool IsReadOnly() const override; + bool IsWriteOnly() const override; + bool SeekStart(GpUFilePos_t loc) override; + bool SeekCurrent(GpFilePos_t loc) override; + bool SeekEnd(GpUFilePos_t loc) override; + GpUFilePos_t Size() const override; + GpUFilePos_t Tell() const override; + void Close() override; + void Flush() override; + + private: + MemReaderStream() GP_DELETED; + + const uint8_t *m_bytes; + size_t m_size; + size_t m_loc; }; class MemBufferReaderStream final : public MemReaderStream { public: ~MemBufferReaderStream() override; - + static MemBufferReaderStream *Create(void *buffer, size_t size); - + void Close() override; private: MemBufferReaderStream() GP_DELETED; MemBufferReaderStream(void *buffer, size_t size); - void *m_buffer; - }; -} - -#endif + void *m_buffer; + }; +} + +#endif diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index b927a92..8ebf1e5 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -81,7 +81,7 @@ static bool ConvertFilenameToSafePStr(const char *str, uint8_t *pstr) { const char c = *str++; - if (c == '.' || c == ' ' || c == '_' || c == '\'' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + if (c == '.' || c == ' ' || c == '_' || c == '!' || c == '\'' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue; else return false; @@ -425,18 +425,6 @@ VFileSpec MakeVFileSpec(PortabilityLayer::VirtualDirectory_t dir, const PLPasStr return spec; } -PLError_t FSpGetFInfo(const VFileSpec &spec, VFileInfo &finfo) -{ - PortabilityLayer::MacFileProperties mfp; - if (!PortabilityLayer::FileManager::GetInstance()->ReadFileProperties(spec.m_dir, spec.m_name, mfp)) - return PLErrors::kFileNotFound; - - finfo.m_type = PortabilityLayer::ResTypeID(mfp.m_fileType); - finfo.m_creator = PortabilityLayer::ResTypeID(mfp.m_fileCreator); - - return PLErrors::kNone; -} - DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t dirID) { PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); @@ -469,10 +457,10 @@ DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t d PortabilityLayer::MacFileProperties mfp; PortabilityLayer::MacFilePropertiesSerialized mfs; - const size_t gpfSize = stream->Read(mfs.m_data, PortabilityLayer::MacFilePropertiesSerialized::kSize); + bool deserializedOK = mfs.ReadFromPackage(*stream); stream->Close(); - if (gpfSize != PortabilityLayer::MacFilePropertiesSerialized::kSize) + if (!deserializedOK) continue; mfs.Deserialize(mfp); diff --git a/PortabilityLayer/PLCore.h b/PortabilityLayer/PLCore.h index a56f482..5d95872 100644 --- a/PortabilityLayer/PLCore.h +++ b/PortabilityLayer/PLCore.h @@ -243,13 +243,10 @@ void GetIndString(unsigned char *str, int stringsID, int fnameIndex); // Fetches VFileSpec MakeVFileSpec(PortabilityLayer::VirtualDirectory_t dir, const PLPasStr &fileName); -PLError_t FSpGetFInfo(const VFileSpec &spec, VFileInfo &finfoOut); - DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t dirID); void DisposeDirectoryFiles(DirectoryFileListEntry *firstDFL); void GetMouse(Window *window, Point *point); -Boolean Button(); // Returns true if there's a mouse down event in the queue Boolean StillDown(); Boolean WaitMouseUp(); diff --git a/PortabilityLayer/PLErrorCodes.h b/PortabilityLayer/PLErrorCodes.h index 0a6cf27..6179f8b 100644 --- a/PortabilityLayer/PLErrorCodes.h +++ b/PortabilityLayer/PLErrorCodes.h @@ -23,8 +23,6 @@ namespace PLErrors kIOError, kResourceError, - - kUserCancelled_TEMP, }; } diff --git a/PortabilityLayer/PLMovies.cpp b/PortabilityLayer/PLMovies.cpp index 1cde9b7..f03aa71 100644 --- a/PortabilityLayer/PLMovies.cpp +++ b/PortabilityLayer/PLMovies.cpp @@ -1,6 +1,7 @@ #include "PLMovies.h" #include "BitmapImage.h" +#include "FileManager.h" #include "MemoryManager.h" #include "PLQDraw.h" #include "PLResources.h" @@ -56,15 +57,19 @@ void AnimationPackage::Destroy() PortabilityLayer::MemoryManager::GetInstance()->Release(this); } -bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, const PLPasStr &path) +PLError_t AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &name) { - m_resArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(virtualDir, path); + m_compositeFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(dirID, name); + if (!m_compositeFile) + return PLErrors::kFileNotFound; + + m_resArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(m_compositeFile); if (!m_resArchive) - return false; + return PLErrors::kResourceError; THandle movieMetadataRes = m_resArchive->LoadResource('muvi', 0); if (!movieMetadataRes) - return false; + return PLErrors::kResourceError; const void *movieMetadata = *movieMetadataRes; @@ -74,25 +79,25 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con movieMetadataRes.Dispose(); if (document.HasParseError() || !document.IsObject()) - return false; + return PLErrors::kResourceError; if (!document.HasMember("frameRateNumerator") || !document.HasMember("frameRateDenominator")) - return false; + return PLErrors::kResourceError; const rapidjson::Value &frameRateNumeratorJSON = document["frameRateNumerator"]; const rapidjson::Value &frameRateDenominatorJSON = document["frameRateDenominator"]; if (!frameRateNumeratorJSON.IsInt() && !frameRateDenominatorJSON.IsInt()) - return false; + return PLErrors::kResourceError; const int frameRateNumerator = frameRateNumeratorJSON.GetInt(); const int frameRateDenominator = frameRateDenominatorJSON.GetInt(); if (frameRateNumerator < 1 || frameRateDenominator < 1) - return false; + return PLErrors::kResourceError; if (frameRateDenominator > INT_MAX / 60 || frameRateDenominator * 60 < frameRateNumerator) - return false; // We only support up to 60fps + return PLErrors::kResourceError; // We only support up to 60fps m_frameRateNumerator = frameRateNumerator; m_frameRateDenominator = frameRateDenominator; @@ -101,7 +106,7 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con for (;;) { if (numFrames + 1 > 0x7fff) - return false; + return PLErrors::kResourceError; THandle frameRes = m_resArchive->LoadResource('PICT', numFrames + 1); if (!frameRes) @@ -109,18 +114,18 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con else { if (frameRes.MMBlock()->m_size < sizeof(BitmapImage)) - return false; + return PLErrors::kResourceError; numFrames++; } } if (numFrames == 0) - return false; + return PLErrors::kResourceError; void *imageListStorage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(THandle) * numFrames); if (!imageListStorage) - return false; + return PLErrors::kResourceError; m_images = static_cast*>(imageListStorage); @@ -132,7 +137,7 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con m_numImages = numFrames; - return true; + return PLErrors::kNone; } const THandle &AnimationPackage::GetFrame(size_t index) const @@ -159,6 +164,7 @@ uint32_t AnimationPackage::GetFrameRateDenominator() const AnimationPackage::AnimationPackage() : m_images(nullptr) , m_resArchive(nullptr) + , m_compositeFile(nullptr) , m_numImages(0) { } @@ -167,6 +173,10 @@ AnimationPackage::~AnimationPackage() { if (m_resArchive) m_resArchive->Destroy(); + + if (m_compositeFile) + m_compositeFile->Close(); + PortabilityLayer::MemoryManager::GetInstance()->Release(m_images); } diff --git a/PortabilityLayer/PLMovies.h b/PortabilityLayer/PLMovies.h index 9e4e5a5..5253616 100644 --- a/PortabilityLayer/PLMovies.h +++ b/PortabilityLayer/PLMovies.h @@ -8,6 +8,8 @@ namespace PortabilityLayer { struct IResourceArchive; + class MultiStreamFile; + class CompositeFile; } struct DrawSurface; @@ -40,7 +42,7 @@ public: static AnimationPackage *Create(); void Destroy(); - bool Load(PortabilityLayer::VirtualDirectory_t virtualDir, const PLPasStr &path); + PLError_t Load(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &name); const THandle &GetFrame(size_t index) const; size_t NumFrames() const; @@ -53,6 +55,7 @@ private: THandle *m_images; PortabilityLayer::IResourceArchive *m_resArchive; + PortabilityLayer::CompositeFile *m_compositeFile; size_t m_numImages; uint32_t m_frameRateNumerator; diff --git a/PortabilityLayer/PLResourceManager.cpp b/PortabilityLayer/PLResourceManager.cpp index daf0ad6..e05435b 100644 --- a/PortabilityLayer/PLResourceManager.cpp +++ b/PortabilityLayer/PLResourceManager.cpp @@ -100,6 +100,7 @@ namespace namespace PortabilityLayer { + class CompositeFile; struct MMHandleBlock; struct IResourceArchive; @@ -114,7 +115,7 @@ namespace PortabilityLayer THandle GetAppResource(const ResTypeID &resTypeID, int16_t resID) const override; IResourceArchive *GetAppResourceArchive() const override; - IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const override; + IResourceArchive *LoadResFile(CompositeFile *file) const override; PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) override; void DissociateHandle(MMHandleBlock *hdl) const override; @@ -125,21 +126,23 @@ namespace PortabilityLayer private: void UnloadAndDestroyResourceFile(IResourceArchive *rf); - IResourceArchive *LoadResDirectory(VirtualDirectory_t virtualDir, const PLPasStr &filename) const; - IResourceArchive *m_appResArchive; + CompositeFile *m_appResFile; static ResourceManagerImpl ms_instance; }; ResourceManagerImpl::ResourceManagerImpl() : m_appResArchive(nullptr) + , m_appResFile(nullptr) { } void ResourceManagerImpl::Init() { - m_appResArchive = LoadResFile(VirtualDirectories::kApplicationData, PSTR("ApplicationResources")); + m_appResFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(VirtualDirectories::kApplicationData, PSTR("ApplicationResources")); + if (m_appResFile) + m_appResArchive = LoadResFile(m_appResFile); } void ResourceManagerImpl::Shutdown() @@ -147,6 +150,9 @@ namespace PortabilityLayer if (m_appResArchive) m_appResArchive->Destroy(); + if (m_appResFile) + m_appResFile->Close(); + m_appResArchive = nullptr; } @@ -168,47 +174,25 @@ namespace PortabilityLayer 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; } - IResourceArchive *ResourceManagerImpl::LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const + IResourceArchive *ResourceManagerImpl::LoadResFile(CompositeFile *file) const { - if (PLDrivers::GetFileSystem()->IsVirtualDirectoryLooseResources(virtualDir)) - return LoadResDirectory(virtualDir, filename); - GpIOStream *fStream = nullptr; - if (FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, fStream) != PLErrors::kNone) + ZipFileProxy *proxy = nullptr; + bool proxyIsShared = false; + if (file->OpenResources(fStream, proxy, proxyIsShared) != PLErrors::kNone) return nullptr; - ZipFileProxy *proxy = ZipFileProxy::Create(fStream); - if (!proxy) - { - fStream->Close(); - return nullptr; - } - - IResourceArchive *archive = ResourceArchiveZipFile::Create(proxy, fStream); + IResourceArchive *archive = ResourceArchiveZipFile::Create(proxy, proxyIsShared, fStream); if (!archive) { proxy->Destroy(); - fStream->Close(); + if (fStream) + fStream->Close(); return nullptr; } @@ -296,7 +280,7 @@ namespace PortabilityLayer // =========================================================================================== - ResourceArchiveZipFile *ResourceArchiveZipFile::Create(ZipFileProxy *zipFileProxy, GpIOStream *stream) + ResourceArchiveZipFile *ResourceArchiveZipFile::Create(ZipFileProxy *zipFileProxy, bool proxyIsShared, GpIOStream *stream) { PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); @@ -320,7 +304,7 @@ namespace PortabilityLayer return nullptr; } - return new (storage) ResourceArchiveZipFile(zipFileProxy, stream, refs); + return new (storage) ResourceArchiveZipFile(zipFileProxy, proxyIsShared, stream, refs); } void ResourceArchiveZipFile::Destroy() @@ -509,8 +493,9 @@ namespace PortabilityLayer return THandle(handle); } - ResourceArchiveZipFile::ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles) + ResourceArchiveZipFile::ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, bool proxyIsShared, GpIOStream *stream, ResourceArchiveRef *resourceHandles) : m_zipFileProxy(zipFileProxy) + , m_proxyIsShared(proxyIsShared) , m_stream(stream) , m_resourceHandles(resourceHandles) { @@ -532,336 +517,10 @@ namespace PortabilityLayer mm->Release(m_resourceHandles); - m_zipFileProxy->Destroy(); - m_stream->Close(); - } + if (!m_proxyIsShared) + m_zipFileProxy->Destroy(); - // ======================================================================================== - - 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() - { - IGpFileSystem *fs = PLDrivers::GetFileSystem(); - - const char *typePaths[1] = { this->m_subdirectory }; - - IGpDirectoryCursor *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 }; - IGpDirectoryCursor *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 = PLDrivers::GetFileSystem()->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(); + if (m_stream) + m_stream->Close(); } } diff --git a/PortabilityLayer/PortabilityLayer.vcxproj b/PortabilityLayer/PortabilityLayer.vcxproj index 0dfdd39..5f4a730 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj +++ b/PortabilityLayer/PortabilityLayer.vcxproj @@ -101,6 +101,7 @@ + @@ -110,6 +111,7 @@ + @@ -230,12 +232,14 @@ + + diff --git a/PortabilityLayer/PortabilityLayer.vcxproj.filters b/PortabilityLayer/PortabilityLayer.vcxproj.filters index 4d5ce51..1139198 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj.filters +++ b/PortabilityLayer/PortabilityLayer.vcxproj.filters @@ -420,6 +420,12 @@ Header Files + + Header Files + + + Header Files + @@ -677,5 +683,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/PortabilityLayer/ResourceManager.h b/PortabilityLayer/ResourceManager.h index b8fed7b..ea49208 100644 --- a/PortabilityLayer/ResourceManager.h +++ b/PortabilityLayer/ResourceManager.h @@ -16,6 +16,7 @@ namespace PortabilityLayer class ResourceFile; class ResTypeID; class ZipFileProxy; + class CompositeFile; struct ResourceArchiveRef { @@ -42,52 +43,10 @@ namespace PortabilityLayer static const char *GetFileExtensionForResType(const ResTypeID &resTypeID, int &outValidationRule); }; - class ResourceArchiveDirectory final : public ResourceArchiveBase - { - public: - static ResourceArchiveDirectory *Create(VirtualDirectory_t directory, const PLPasStr &subdirectory); - 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; - - bool Init(); - - private: - 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); + static ResourceArchiveZipFile *Create(ZipFileProxy *zipFileProxy, bool proxyIsShared, GpIOStream *stream); void Destroy() override; THandle LoadResource(const ResTypeID &resTypeID, int id) override; @@ -96,7 +55,7 @@ namespace PortabilityLayer bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const override; private: - ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles); + ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, bool proxyIsShared, GpIOStream *stream, ResourceArchiveRef *resourceHandles); ~ResourceArchiveZipFile(); bool IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const; @@ -104,8 +63,9 @@ namespace PortabilityLayer THandle GetResource(const ResTypeID &resTypeID, int id, bool load); ZipFileProxy *m_zipFileProxy; - GpIOStream *m_stream; + GpIOStream *m_stream; // This may be null, i.e. a composite file may own it instead ResourceArchiveRef *m_resourceHandles; + bool m_proxyIsShared; }; class ResourceManager @@ -117,7 +77,7 @@ namespace PortabilityLayer virtual THandle GetAppResource(const ResTypeID &resTypeID, int16_t resID) const = 0; virtual IResourceArchive *GetAppResourceArchive() const = 0; - virtual IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const = 0; + virtual IResourceArchive *LoadResFile(CompositeFile *file) const = 0; virtual PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0; virtual void DissociateHandle(MMHandleBlock *hdl) const = 0; diff --git a/PortabilityLayer/ZipFileProxy.cpp b/PortabilityLayer/ZipFileProxy.cpp index 2d92aa0..a4d9f44 100644 --- a/PortabilityLayer/ZipFileProxy.cpp +++ b/PortabilityLayer/ZipFileProxy.cpp @@ -1,7 +1,9 @@ #include "ZipFileProxy.h" #include "BinarySearch.h" +#include "FileSectionStream.h" #include "GpIOStream.h" +#include "InflateStream.h" #include "MemoryManager.h" #include "ZipFile.h" @@ -227,6 +229,38 @@ namespace PortabilityLayer return false; } + GpIOStream *ZipFileProxy::OpenFile(size_t index) const + { + ZipCentralDirectoryFileHeader centralDirHeader = m_sortedFiles[index].Get(); + + if (!m_stream->SeekStart(centralDirHeader.m_localHeaderOffset)) + return nullptr; + + ZipFileLocalHeader localHeader; + if (m_stream->Read(&localHeader, sizeof(ZipFileLocalHeader)) != sizeof(ZipFileLocalHeader)) + return nullptr; + + if (!m_stream->SeekCurrent(localHeader.m_fileNameLength + localHeader.m_extraFieldLength)) + return nullptr; + + if (localHeader.m_compressedSize != centralDirHeader.m_compressedSize || localHeader.m_uncompressedSize != centralDirHeader.m_uncompressedSize || localHeader.m_method != centralDirHeader.m_method) + return nullptr; + + const size_t compressedSize = centralDirHeader.m_compressedSize; + const size_t uncompressedSize = centralDirHeader.m_uncompressedSize; + if (localHeader.m_method == PortabilityLayer::ZipConstants::kStoredMethod) + { + if (uncompressedSize != compressedSize) + return nullptr; + + return FileSectionStream::Create(m_stream, m_stream->Tell(), uncompressedSize); + } + else if (localHeader.m_method == PortabilityLayer::ZipConstants::kDeflatedMethod) + return InflateStream::Create(m_stream, m_stream->Tell(), compressedSize, uncompressedSize); + else + return nullptr; + } + size_t ZipFileProxy::NumFiles() const { return m_numFiles; diff --git a/PortabilityLayer/ZipFileProxy.h b/PortabilityLayer/ZipFileProxy.h index 70f7e9d..4a529fe 100644 --- a/PortabilityLayer/ZipFileProxy.h +++ b/PortabilityLayer/ZipFileProxy.h @@ -16,6 +16,8 @@ namespace PortabilityLayer bool IndexFile(const char *path, size_t &outIndex) const; bool LoadFile(size_t index, void *outBuffer); + GpIOStream *OpenFile(size_t index) const; + bool HasPrefix(const char *path) const; bool FindFirstWithPrefix(const char *resPrefix, size_t &outFileIndex) const; @@ -23,7 +25,6 @@ namespace PortabilityLayer size_t GetFileSize(size_t index) const; void GetFileName(size_t index, const char *&outName, size_t &outLength) const; - static ZipFileProxy *Create(GpIOStream *stream); private: diff --git a/ReleasePackageInstaller/Product.wxs b/ReleasePackageInstaller/Product.wxs index 648d6e3..871a8db 100644 --- a/ReleasePackageInstaller/Product.wxs +++ b/ReleasePackageInstaller/Product.wxs @@ -82,7 +82,7 @@ - + diff --git a/bin2gp/bin2gp.cpp b/bin2gp/bin2gp.cpp index 3a57c87..42825f0 100644 --- a/bin2gp/bin2gp.cpp +++ b/bin2gp/bin2gp.cpp @@ -22,7 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "CFileStream.h" +#include "CFileStream.h" +#include "CombinedTimestamp.h" #include "ScopedPtr.h" #include "MacBinary2.h" #include "MacFileMem.h" @@ -33,9 +34,9 @@ using namespace PortabilityLayer; int main(int argc, const char **argv) { - if (argc != 3) + if (argc != 4) { - fprintf(stderr, "Usage: bin2gp "); + fprintf(stderr, "Usage: bin2gp "); return -1; } @@ -53,13 +54,34 @@ int main(int argc, const char **argv) return -1; } +#ifdef _CRT_INSECURE_DEPRECATE + FILE *tsF = nullptr; + if (fopen_s(&tsF, argv[2], "rb")) + tsF = nullptr; +#else + FILE *tsF = fopen(argv[2], "rb"); +#endif + + if (!tsF) + { + fprintf(stderr, "Could not open timestamp file"); + return -1; + } + + PortabilityLayer::CombinedTimestamp ts; + if (!fread(&ts, sizeof(ts), 1, tsF)) + { + fprintf(stderr, "Could not read timestamp"); + return -1; + } + CFileStream fs(f, true, false, true); ScopedPtr memFile = MacBinary2::ReadBin(&fs); fs.Close(); - std::string fname = argv[2]; + std::string fname = argv[3]; const char* extensions[] = { ".gpf", ".gpr", ".gpd", ".gpc" }; @@ -105,10 +127,19 @@ int main(int argc, const char **argv) #endif if (!outF) - continue; - - fwrite(bufferToWrite, 1, sizeToWrite, outF); - fclose(outF); + continue; + + if (i == 0) + { + CFileStream stream(outF); + sp.WriteAsPackage(stream, ts); + stream.Close(); + } + else + { + fwrite(bufferToWrite, 1, sizeToWrite, outF); + fclose(outF); + } } return 0; diff --git a/gpr2gpa/gpr2gpa.cpp b/gpr2gpa/gpr2gpa.cpp index 32f2838..18a7680 100644 --- a/gpr2gpa/gpr2gpa.cpp +++ b/gpr2gpa/gpr2gpa.cpp @@ -185,41 +185,6 @@ bool TryDeflate(const std::vector &uncompressed, std::vector & return true; } -void ConvertToMSDOSTimestamp(const PortabilityLayer::CombinedTimestamp &ts, uint16_t &msdosDate, uint16_t &msdosTime) -{ - int32_t yearsSince1980 = ts.GetLocalYear() - 1980; - uint8_t month = ts.m_localMonth; - uint8_t day = ts.m_localDay; - - uint8_t hour = ts.m_localHour; - uint8_t minute = ts.m_localMinute; - uint8_t second = ts.m_localSecond; - - if (yearsSince1980 < 0) - { - // Time machine - yearsSince1980 = 0; - second = 0; - minute = 0; - hour = 0; - day = 1; - month = 1; - } - else if (yearsSince1980 > 127) - { - // I was promised flying cars, but it's 2107 and you're still flying paper airplanes... - yearsSince1980 = 127; - second = 59; - minute = 59; - hour = 23; - day = 31; - month = 12; - } - - msdosTime = (second / 2) | (minute << 5) | (hour << 11); - msdosDate = day | (month << 5) | (yearsSince1980 << 9); -} - void ExportZipFile(const char *path, std::vector &entries, const PortabilityLayer::CombinedTimestamp &ts) { FILE *outF = fopen_utf8(path, "wb"); @@ -232,7 +197,7 @@ void ExportZipFile(const char *path, std::vector &entries, const P uint16_t msdosModificationTime = 0; uint16_t msdosModificationDate = 0; - ConvertToMSDOSTimestamp(ts, msdosModificationDate, msdosModificationTime); + ts.GetAsMSDOSTimestamp(msdosModificationDate, msdosModificationTime); std::vector cdirRecords; diff --git a/hqx2gp/hqx2gp.cpp b/hqx2gp/hqx2gp.cpp index 77d418d..4f632d0 100644 --- a/hqx2gp/hqx2gp.cpp +++ b/hqx2gp/hqx2gp.cpp @@ -23,10 +23,13 @@ SOFTWARE. */ #include "CFileStream.h" +#include "CombinedTimestamp.h" +#include "DeflateCodec.h" #include "ScopedPtr.h" #include "BinHex4.h" #include "MacBinary2.h" #include "MacFileMem.h" +#include "ZipFile.h" #include @@ -68,19 +71,15 @@ int main(int argc, const char **argv) return -1; } - int64_t timestamp = 0; + PortabilityLayer::CombinedTimestamp ts; { - uint8_t encodedTimestamp[8]; - if (fread(encodedTimestamp, 1, 8, tsF) != 8) + if (fread(&ts, 1, sizeof(ts), tsF) != sizeof(ts)) { fprintf(stderr, "Error reading timestamp file"); return -1; } - for (int i = 0; i < 8; i++) - timestamp |= static_cast(encodedTimestamp[i]) << (i * 8); - } fclose(tsF); @@ -96,7 +95,7 @@ int main(int argc, const char **argv) const char* extensions[] = { ".gpf", ".gpr", ".gpd", ".gpc" }; MacFileProperties mfp = memFile->FileInfo().m_properties; - mfp.m_creationDate = mfp.m_modifiedDate = timestamp; + mfp.m_createdTimeMacEpoch = mfp.m_modifiedTimeMacEpoch = ts.GetMacEpochTime(); MacFilePropertiesSerialized sp; sp.Serialize(mfp); @@ -142,8 +141,17 @@ int main(int argc, const char **argv) if (!outF) continue; - fwrite(bufferToWrite, 1, sizeToWrite, outF); - fclose(outF); + if (i == 0) + { + CFileStream stream(outF); + sp.WriteAsPackage(stream, ts); + stream.Close(); + } + else + { + fwrite(bufferToWrite, 1, sizeToWrite, outF); + fclose(outF); + } } return 0; diff --git a/unpacktool/CompactProParser.cpp b/unpacktool/CompactProParser.cpp index 53a4ad1..5c4a74c 100644 --- a/unpacktool/CompactProParser.cpp +++ b/unpacktool/CompactProParser.cpp @@ -201,8 +201,8 @@ ArchiveItemList *CompactProParser::ParseDirectory(uint32_t numEntries, IFileRead item.m_dataForkDesc.m_compressionMethod = (fileEntry.m_flags & 4) ? CompressionMethods::kCompactProLZHRLE : CompressionMethods::kCompactProRLE; } - item.m_macProperties.m_creationDate = fileEntry.m_creationDate; - item.m_macProperties.m_modifiedDate = fileEntry.m_modificationDate; + item.m_macProperties.m_createdTimeMacEpoch = fileEntry.m_creationDate; + item.m_macProperties.m_modifiedTimeMacEpoch = fileEntry.m_modificationDate; memcpy(item.m_macProperties.m_fileCreator, fileEntry.m_fileCreator, 4); memcpy(item.m_macProperties.m_fileType, fileEntry.m_fileType, 4); item.m_macProperties.m_finderFlags = fileEntry.m_finderFlags; diff --git a/unpacktool/StuffIt5Parser.cpp b/unpacktool/StuffIt5Parser.cpp index a2a2ea2..943c9a9 100644 --- a/unpacktool/StuffIt5Parser.cpp +++ b/unpacktool/StuffIt5Parser.cpp @@ -237,8 +237,8 @@ static void ConvertItem(const StuffIt5Block &block, ArchiveItem &item) item.m_children = ConvertToItemList(block.m_children); else { - item.m_macProperties.m_creationDate = block.m_header.m_creationDate; - item.m_macProperties.m_modifiedDate = block.m_header.m_modificationDate; + item.m_macProperties.m_createdTimeMacEpoch = block.m_header.m_creationDate; + item.m_macProperties.m_modifiedTimeMacEpoch = block.m_header.m_modificationDate; memcpy(item.m_macProperties.m_fileCreator, block.m_annex1.m_fileCreator, 4); memcpy(item.m_macProperties.m_fileType, block.m_annex1.m_fileType, 4); item.m_macProperties.m_finderFlags = block.m_annex1.m_finderFlags; diff --git a/unpacktool/StuffItParser.cpp b/unpacktool/StuffItParser.cpp index 3615e90..8ed43e3 100644 --- a/unpacktool/StuffItParser.cpp +++ b/unpacktool/StuffItParser.cpp @@ -98,8 +98,8 @@ struct StuffItHeader void ParseMacFileInfo(ArchiveItem &item, const uint8_t *header, IFileReader::FilePos_t filePos) { - item.m_macProperties.m_creationDate = ParseUInt32BE(header + SITFH_CREATIONDATE); - item.m_macProperties.m_modifiedDate = ParseUInt32BE(header + SITFH_MODDATE); + item.m_macProperties.m_createdTimeMacEpoch = ParseUInt32BE(header + SITFH_CREATIONDATE); + item.m_macProperties.m_modifiedTimeMacEpoch = ParseUInt32BE(header + SITFH_MODDATE); memcpy(item.m_macProperties.m_fileCreator, header + SITFH_CREATOR, 4); memcpy(item.m_macProperties.m_fileType, header + SITFH_FTYPE, 4); item.m_macProperties.m_finderFlags = ParseUInt16BE(header + SITFH_FNDRFLAGS); diff --git a/unpacktool/unpacktool.cpp b/unpacktool/unpacktool.cpp index b81244a..e91b7fd 100644 --- a/unpacktool/unpacktool.cpp +++ b/unpacktool/unpacktool.cpp @@ -5,6 +5,7 @@ #include "StuffItParser.h" #include "StuffIt5Parser.h" #include "CompactProParser.h" +#include "CFileStream.h" #include "UTF8.h" #include "UTF16.h" @@ -24,6 +25,8 @@ #include "CSInputBuffer.h" #include "WindowsUnicodeToolShim.h" +#include "CombinedTimestamp.h" + #include #include @@ -207,7 +210,7 @@ void MakeIntermediateDirectories(const std::string &path) } } -int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader); +int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader, const PortabilityLayer::CombinedTimestamp &ts); int ExtractSingleFork(const ArchiveCompressedChunkDesc &chunkDesc, const std::string &path, IFileReader &reader) { @@ -321,7 +324,7 @@ int ExtractSingleFork(const ArchiveCompressedChunkDesc &chunkDesc, const std::st return 0; } -int ExtractFile(const ArchiveItem &item, const std::string &path, IFileReader &reader) +int ExtractFile(const ArchiveItem &item, const std::string &path, IFileReader &reader, const PortabilityLayer::CombinedTimestamp &ts) { PortabilityLayer::MacFilePropertiesSerialized mfps; mfps.Serialize(item.m_macProperties); @@ -337,14 +340,16 @@ int ExtractFile(const ArchiveItem &item, const std::string &path, IFileReader &r return -1; } - if (fwrite(mfps.m_data, 1, PortabilityLayer::MacFilePropertiesSerialized::kSize, metadataF) != PortabilityLayer::MacFilePropertiesSerialized::kSize) + PortabilityLayer::CFileStream metadataStream(metadataF); + + if (mfps.WriteAsPackage(metadataStream, ts)) { fprintf(stderr, "A problem occurred writing metadata"); - fclose(metadataF); + metadataStream.Close(); return -1; } - fclose(metadataF); + metadataStream.Close(); int returnCode = ExtractSingleFork(item.m_dataForkDesc, dataPath, reader); if (returnCode) @@ -357,7 +362,7 @@ int ExtractFile(const ArchiveItem &item, const std::string &path, IFileReader &r return 0; } -int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, IFileReader &reader) +int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, IFileReader &reader, const PortabilityLayer::CombinedTimestamp &ts) { std::string path(reinterpret_cast(item.m_fileNameUTF8.data()), item.m_fileNameUTF8.size()); @@ -377,24 +382,24 @@ int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, path.append("\\"); - int returnCode = RecursiveExtractFiles(depth + 1, item.m_children, path, reader); + int returnCode = RecursiveExtractFiles(depth + 1, item.m_children, path, reader, ts); if (returnCode) return returnCode; return 0; } else - return ExtractFile(item, path, reader); + return ExtractFile(item, path, reader, ts); } -int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader) +int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader, const PortabilityLayer::CombinedTimestamp &ts) { const std::vector &items = itemList->m_items; const size_t numChildren = items.size(); for (size_t i = 0; i < numChildren; i++) { - int returnCode = ExtractItem(depth, items[i], path, reader); + int returnCode = ExtractItem(depth, items[i], path, reader, ts); if (returnCode) return returnCode; } @@ -404,9 +409,9 @@ int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::strin int toolMain(int argc, const char **argv) { - if (argc != 3) + if (argc != 4) { - fprintf(stderr, "Usage: unpacktool "); + fprintf(stderr, "Usage: unpacktool "); return -1; } @@ -418,6 +423,24 @@ int toolMain(int argc, const char **argv) return -1; } + + FILE *tsFile = fopen_utf8(argv[2], "rb"); + + if (!tsFile) + { + fprintf(stderr, "Could not open timestamp file"); + return -1; + } + + PortabilityLayer::CombinedTimestamp ts; + if (!fread(&ts, sizeof(ts), 1, tsFile)) + { + fprintf(stderr, "Could not read timestamp"); + return -1; + } + + fclose(tsFile); + CFileReader reader(inputArchive); IArchiveParser *parsers[] = @@ -448,12 +471,12 @@ int toolMain(int argc, const char **argv) printf("Decompressing files...\n"); - std::string currentPath = argv[2]; + std::string currentPath = argv[3]; TerminateDirectoryPath(currentPath); MakeIntermediateDirectories(currentPath); - int returnCode = RecursiveExtractFiles(0, archiveItemList, currentPath, reader); + int returnCode = RecursiveExtractFiles(0, archiveItemList, currentPath, reader, ts); delete archiveItemList; diff --git a/unpacktool/unpacktool.vcxproj b/unpacktool/unpacktool.vcxproj index be86c0e..2c12b92 100644 --- a/unpacktool/unpacktool.vcxproj +++ b/unpacktool/unpacktool.vcxproj @@ -40,8 +40,9 @@ - + + @@ -50,6 +51,7 @@ +