From 928efdd52737f6a9d9097716d81d55cbcfb7da78 Mon Sep 17 00:00:00 2001 From: elasota Date: Thu, 23 Jan 2020 22:36:12 -0500 Subject: [PATCH] Switch timestamp format to combined UTC + local so fewer stupid hacks are required. --- ConvertResources.bat | 52 +++++------ DefaultTimestamp.timestamp | Bin 8 -> 20 bytes MakeTimestamp/MakeTimestamp.cpp | 28 +++++- MakeTimestamp/MakeTimestamp.vcxproj | 4 + PortabilityLayer/CombinedTimestamp.h | 57 ++++++++++++ PortabilityLayer/PortabilityLayer.vcxproj | 1 + .../PortabilityLayer.vcxproj.filters | 3 + gpr2gpa/gpr2gpa.cpp | 88 ++++++------------ 8 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 PortabilityLayer/CombinedTimestamp.h diff --git a/ConvertResources.bat b/ConvertResources.bat index ea7235b..c2959e1 100644 --- a/ConvertResources.bat +++ b/ConvertResources.bat @@ -5,10 +5,7 @@ mkdir ResourceTemp x64\Release\MiniRez.exe "GliderProData\Glider PRO.r" Packaged\ApplicationResources.gpr -copy /B /Y NUL Packaged\Empty.txt -x64\Release\FTagData.exe "Packaged\Empty.txt" "DefaultTimestamp.timestamp" "Packaged\ApplicationResources" APPL ozm5 0 0 locked - -x64\Release\gpr2gpa.exe Packaged\ApplicationResources Packaged\ApplicationResources.gpa +x64\Release\gpr2gpa.exe "Packaged\ApplicationResources.gpr" "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpa" x64\Release\ConvertColorCursors.exe @@ -35,28 +32,28 @@ x64\Release\hqx2gp.exe "GliderProData\Houses\Teddy World.binhex" "DefaultTimesta x64\Release\hqx2gp.exe "GliderProData\Houses\The Asylum Pro.binhex" "DefaultTimestamp.timestamp" "Packaged\Houses\The Asylum Pro" x64\Release\hqx2gp.exe "GliderProData\Houses\Titanic.binhex" "DefaultTimestamp.timestamp" "Packaged\Houses\Titanic" -x64\Release\gpr2gpa.exe "Packaged\Houses\Art Museum" "Packaged\Houses\Art Museum.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\California or Bust!" "Packaged\Houses\California or Bust!.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Castle o' the Air" "Packaged\Houses\Castle o' the Air.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\CD Demo House" "Packaged\Houses\CD Demo House.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Davis Station" "Packaged\Houses\Davis Station.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Demo House" "Packaged\Houses\Demo House.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Empty House" "Packaged\Houses\Empty House.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Fun House" "Packaged\Houses\Fun House.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Grand Prix" "Packaged\Houses\Grand Prix.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\ImagineHouse PRO II" "Packaged\Houses\ImagineHouse PRO II.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\In The Mirror" "Packaged\Houses\In The Mirror.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Land of Illusion" "Packaged\Houses\Land of Illusion.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Leviathan" "Packaged\Houses\Leviathan.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Metropolis" "Packaged\Houses\Metropolis.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Nemo's Market" "Packaged\Houses\Nemo's Market.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Rainbow's End" "Packaged\Houses\Rainbow's End.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Sampler" "Packaged\Houses\Sampler.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Slumberland" "Packaged\Houses\Slumberland.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\SpacePods" "Packaged\Houses\SpacePods.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Teddy World" "Packaged\Houses\Teddy World.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\The Asylum Pro" "Packaged\Houses\The Asylum Pro.gpa" -x64\Release\gpr2gpa.exe "Packaged\Houses\Titanic" "Packaged\Houses\Titanic.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Art Museum.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Art Museum.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\California or Bust!.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\California or Bust!.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Castle o' the Air.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Castle o' the Air.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\CD Demo House.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\CD Demo House.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Davis Station.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Davis Station.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Demo House.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Demo House.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Empty House.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Empty House.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Fun House.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Fun House.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Grand Prix.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Grand Prix.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\ImagineHouse PRO II.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\ImagineHouse PRO II.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\In The Mirror.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\In The Mirror.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Land of Illusion.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Land of Illusion.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Leviathan.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Leviathan.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Metropolis.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Metropolis.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Nemo's Market.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Nemo's Market.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Rainbow's End.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Rainbow's End.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Sampler.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Sampler.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Slumberland.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Slumberland.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\SpacePods.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\SpacePods.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Teddy World.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Teddy World.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\The Asylum Pro.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\The Asylum Pro.gpa" +x64\Release\gpr2gpa.exe "Packaged\Houses\Titanic.gpr" "DefaultTimestamp.timestamp" "Packaged\Houses\Titanic.gpa" x64\Release\FTagData.exe "GliderProData\Houses\Art Museum.mov" "DefaultTimestamp.timestamp" "Packaged\Houses\Art Museum.mov" MooV ozm5 0 0 locked x64\Release\FTagData.exe "GliderProData\Houses\Castle o' the Air.mov" "DefaultTimestamp.timestamp" "Packaged\Houses\Castle o' the Air.mov" MooV ozm5 0 0 locked @@ -76,8 +73,5 @@ x64\Release\FTagData.exe "GliderProData\Houses\Titanic.mov" "DefaultTimestamp.ti del /Q Packaged\Houses\*.gpr del /Q Packaged\ApplicationResources.gpr -del /Q Packaged\ApplicationResources.gpf -del /Q Packaged\ApplicationResources.gpd -del /Q Packaged\Empty.txt pause diff --git a/DefaultTimestamp.timestamp b/DefaultTimestamp.timestamp index d2c0e8d6e50e62a4d5e7a210c349719b8c118465..40dc4e7c027e4b997dfe61c72acb6523dab113c3 100644 GIT binary patch literal 20 YcmWgP4!Fet1W(u*7#PJx^>l!203=BQyZ`_I literal 8 NcmZo#;eU$(2mlVu0#pD1 diff --git a/MakeTimestamp/MakeTimestamp.cpp b/MakeTimestamp/MakeTimestamp.cpp index 100e6a6..c3d8529 100644 --- a/MakeTimestamp/MakeTimestamp.cpp +++ b/MakeTimestamp/MakeTimestamp.cpp @@ -2,6 +2,8 @@ #include #include +#include "CombinedTimestamp.h" + int main(int argc, const char **argv) { if (argc != 2) @@ -39,11 +41,29 @@ int main(int argc, const char **argv) return -1; } - uint8_t encodedTimestamp[8]; - for (int i = 0; i < 8; i++) - encodedTimestamp[i] = static_cast((timeDelta >> (i * 8)) & 0xff); + TIME_ZONE_INFORMATION tz; + GetTimeZoneInformation(&tz); - fwrite(encodedTimestamp, 8, 1, f); + SYSTEMTIME utcST; + FileTimeToSystemTime(×tampFT, &utcST); + + SYSTEMTIME localST; + SystemTimeToTzSpecificLocalTime(&tz, &utcST, &localST); + + PortabilityLayer::CombinedTimestamp ts; + ts.SetUTCTime(timeDelta); + + ts.SetLocalYear(localST.wYear); + ts.m_localMonth = localST.wMonth; + ts.m_localDay = localST.wDay; + + ts.m_localHour = localST.wHour; + ts.m_localMinute = localST.wMinute; + ts.m_localSecond = localST.wSecond; + + memset(ts.m_padding, 0, sizeof(ts.m_padding)); + + fwrite(&ts, sizeof(ts), 1, f); fclose(f); diff --git a/MakeTimestamp/MakeTimestamp.vcxproj b/MakeTimestamp/MakeTimestamp.vcxproj index 56fff49..2665816 100644 --- a/MakeTimestamp/MakeTimestamp.vcxproj +++ b/MakeTimestamp/MakeTimestamp.vcxproj @@ -58,15 +58,19 @@ + + + + diff --git a/PortabilityLayer/CombinedTimestamp.h b/PortabilityLayer/CombinedTimestamp.h new file mode 100644 index 0000000..b639162 --- /dev/null +++ b/PortabilityLayer/CombinedTimestamp.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + struct CombinedTimestamp + { + uint8_t m_utcTimestamp[8]; + + uint8_t m_localYear[4]; + uint8_t m_localMonth; + uint8_t m_localDay; + + uint8_t m_localHour; + uint8_t m_localMinute; + uint8_t m_localSecond; + + uint8_t m_padding[3]; + + int64_t GetUTCTime() const; + void SetUTCTime(int64_t timestamp); + + int32_t GetLocalYear() const; + void SetLocalYear(int32_t year); + }; + + inline int64_t CombinedTimestamp::GetUTCTime() const + { + int64_t result = 0; + for (int i = 0; i < 8; i++) + result |= static_cast(m_utcTimestamp[i]) << (i * 8); + + return result; + } + + void CombinedTimestamp::SetUTCTime(int64_t timestamp) + { + for (int i = 0; i < 8; i++) + m_utcTimestamp[i] = static_cast((timestamp >> (i * 8)) & 0xff); + } + + inline int32_t CombinedTimestamp::GetLocalYear() const + { + int32_t result = 0; + for (int i = 0; i < 4; i++) + result |= static_cast(m_localYear[i]) << (i * 8); + + return result; + } + + void CombinedTimestamp::SetLocalYear(int32_t timestamp) + { + for (int i = 0; i < 4; i++) + m_localYear[i] = static_cast((timestamp >> (i * 8)) & 0xff); + } +} diff --git a/PortabilityLayer/PortabilityLayer.vcxproj b/PortabilityLayer/PortabilityLayer.vcxproj index 6326259..d74c5ee 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj +++ b/PortabilityLayer/PortabilityLayer.vcxproj @@ -147,6 +147,7 @@ + diff --git a/PortabilityLayer/PortabilityLayer.vcxproj.filters b/PortabilityLayer/PortabilityLayer.vcxproj.filters index 03f9620..3e11827 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj.filters +++ b/PortabilityLayer/PortabilityLayer.vcxproj.filters @@ -465,6 +465,9 @@ Header Files + + Header Files + diff --git a/gpr2gpa/gpr2gpa.cpp b/gpr2gpa/gpr2gpa.cpp index 0c25f9a..eb828cb 100644 --- a/gpr2gpa/gpr2gpa.cpp +++ b/gpr2gpa/gpr2gpa.cpp @@ -1,5 +1,6 @@ #include "BMPFormat.h" #include "CFileStream.h" +#include "CombinedTimestamp.h" #include "GPArchive.h" #include "MemReaderStream.h" #include "QDPictDecoder.h" @@ -94,64 +95,42 @@ bool TryDeflate(const std::vector &uncompressed, std::vector & return true; } -void ConvertToMSDOSTimestamp(int64_t timestamp, uint16_t &msdosDate, uint16_t &msdosTime) +void ConvertToMSDOSTimestamp(const PortabilityLayer::CombinedTimestamp &ts, uint16_t &msdosDate, uint16_t &msdosTime) { - SYSTEMTIME epochStart; - epochStart.wYear = 1904; - epochStart.wMonth = 1; - epochStart.wDayOfWeek = 5; - epochStart.wDay = 1; - epochStart.wHour = 0; - epochStart.wMinute = 0; - epochStart.wSecond = 0; - epochStart.wMilliseconds = 0; + int32_t yearsSince1980 = ts.GetLocalYear() - 1980; + uint8_t month = ts.m_localMonth; + uint8_t day = ts.m_localDay; - FILETIME epochStartFT; - SystemTimeToFileTime(&epochStart, &epochStartFT); + uint8_t hour = ts.m_localHour; + uint8_t minute = ts.m_localMinute; + uint8_t second = ts.m_localSecond; - int64_t epochStart64 = (static_cast(epochStartFT.dwLowDateTime) & 0xffffffff) | (static_cast(epochStartFT.dwHighDateTime) << 32); - int64_t offsetDate64 = (epochStart64 + timestamp * 10000000); - - FILETIME utcTimestampFT; - utcTimestampFT.dwLowDateTime = static_cast(offsetDate64 & 0xffffffff); - utcTimestampFT.dwHighDateTime = static_cast((offsetDate64 >> 32) & 0xffffffff); - - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - SYSTEMTIME utcTimestampST; - FileTimeToSystemTime(&utcTimestampFT, &utcTimestampST); - - SYSTEMTIME localTimestampST; - SystemTimeToTzSpecificLocalTime(&tzInfo, &utcTimestampST, &localTimestampST); - - DWORD yearsSince1980 = localTimestampST.wYear - 1980; if (yearsSince1980 < 0) { // Time machine yearsSince1980 = 0; - localTimestampST.wSecond = 0; - localTimestampST.wMinute = 0; - localTimestampST.wHour = 0; - localTimestampST.wDay = 1; - localTimestampST.wMonth = 1; + 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; - localTimestampST.wSecond = 59; - localTimestampST.wMinute = 59; - localTimestampST.wHour = 23; - localTimestampST.wDay = 31; - localTimestampST.wMonth = 12; + second = 59; + minute = 59; + hour = 23; + day = 31; + month = 12; } - msdosTime = (localTimestampST.wSecond / 2) | (localTimestampST.wMinute << 5) | (localTimestampST.wHour << 11); - msdosDate = localTimestampST.wDay | (localTimestampST.wMonth << 5) | (yearsSince1980 << 9); + msdosTime = (second / 2) | (minute << 5) | (hour << 11); + msdosDate = day | (month << 5) | (yearsSince1980 << 9); } -void ExportZipFile(const char *path, const std::vector &entries, const PortabilityLayer::MacFileProperties &mfp) +void ExportZipFile(const char *path, const std::vector &entries, const PortabilityLayer::CombinedTimestamp &ts) { FILE *outF = nullptr; if (fopen_s(&outF, path, "wb")) @@ -163,7 +142,7 @@ void ExportZipFile(const char *path, const std::vector &entries, c uint16_t msdosModificationTime = 0; uint16_t msdosModificationDate = 0; - ConvertToMSDOSTimestamp(mfp.m_modifiedDate, msdosModificationDate, msdosModificationTime); + ConvertToMSDOSTimestamp(ts, msdosModificationDate, msdosModificationTime); std::vector cdirRecords; @@ -742,40 +721,35 @@ bool ImportSound(std::vector &outWAV, const void *inData, size_t inSize int main(int argc, const char **argv) { - if (argc != 3) + if (argc != 4) { - fprintf(stderr, "Usage: gpr2gpa "); + fprintf(stderr, "Usage: gpr2gpa "); return -1; } std::string base = argv[1]; - std::string metadataPath = base + ".gpf"; - std::string resPath = base + ".gpr"; FILE *inF = nullptr; - if (fopen_s(&inF, resPath.c_str(), "rb")) + if (fopen_s(&inF, argv[1], "rb")) { fprintf(stderr, "Error opening input file"); return -1; } - FILE *metaF = nullptr; - if (fopen_s(&metaF, metadataPath.c_str(), "rb")) + FILE *timestampF = nullptr; + if (fopen_s(×tampF, argv[2], "rb")) { fprintf(stderr, "Error opening metadata file"); return -1; } - PortabilityLayer::MacFilePropertiesSerialized mfpSerialized; - if (fread(mfpSerialized.m_data, 1, PortabilityLayer::MacFilePropertiesSerialized::kSize, metaF) != PortabilityLayer::MacFilePropertiesSerialized::kSize) + PortabilityLayer::CombinedTimestamp ts; + if (fread(&ts, 1, sizeof(ts), timestampF) != sizeof(ts)) { - fprintf(stderr, "Error reading metadata"); + fprintf(stderr, "Error reading timestamp"); return -1; } - PortabilityLayer::MacFileProperties mfp; - mfpSerialized.Deserialize(mfp); - PortabilityLayer::CFileStream cfs(inF); PortabilityLayer::ResourceFile *resFile = PortabilityLayer::ResourceFile::Create(); @@ -856,7 +830,7 @@ int main(int argc, const char **argv) } } - ExportZipFile(argv[2], contents, mfp); + ExportZipFile(argv[3], contents, ts); resFile->Destroy();