diff --git a/Core/NativeClient/C_Resources.lua b/Core/NativeClient/C_Resources.lua index 374e4b16..e69ba9f4 100644 --- a/Core/NativeClient/C_Resources.lua +++ b/Core/NativeClient/C_Resources.lua @@ -1,3 +1,4 @@ +local CompiledGRF = require("Core.FileFormats.Optimized.CompiledGRF") local FogParameters = require("Core.FileFormats.FogParameters") local RagnarokGRF = require("Core.FileFormats.RagnarokGRF") @@ -8,13 +9,33 @@ local C_Resources = { ["data/sprite/cursors.spr"] = false, ["data/fogparametertable.txt"] = FogParameters, }, + ENABLE_CGRF_CACHING = true, } local self = C_Resources function C_Resources.PreloadPersistentResources() + local grfFilePath = self.GRF_FILE_PATH + + local hasUpdatedCacheEntry = self.ENABLE_CGRF_CACHING and CompiledGRF:IsCacheUpdated(grfFilePath) or false + local grfName = path.basename(grfFilePath, path.extname(grfFilePath)) + local cgrfFilePath = path.join(CompiledGRF.CGRF_CACHE_DIRECTORY, grfName .. ".cgrf") + + local cgrfFileContents + if hasUpdatedCacheEntry then + printf("CGRF_CACHE_HIT: %s (Restoring table of contents)", cgrfFilePath) + cgrfFileContents = C_FileSystem.ReadFile(cgrfFilePath) + end + local grf = RagnarokGRF() - grf:Open(self.GRF_FILE_PATH) + grf:Open(self.GRF_FILE_PATH, cgrfFileContents) + + local needsCacheWrite = self.ENABLE_CGRF_CACHING and not hasUpdatedCacheEntry + if needsCacheWrite then + cgrfFileContents = CompiledGRF:CompileTableOfContents(grf) + printf("CGRF_CACHE_WRITE: %s (%s)", cgrfFilePath, string.filesize(#cgrfFileContents)) + C_FileSystem.WriteFile(cgrfFilePath, cgrfFileContents) + end printf("Preloading %d persistent resources from %s", table.count(self.PERSISTENT_RESOURCES), self.GRF_FILE_PATH) for filePath, decoder in pairs(self.PERSISTENT_RESOURCES) do diff --git a/Tests/NativeClient/C_Resources.spec.lua b/Tests/NativeClient/C_Resources.spec.lua index 7bfd16cc..eb41b8f5 100644 --- a/Tests/NativeClient/C_Resources.spec.lua +++ b/Tests/NativeClient/C_Resources.spec.lua @@ -1,7 +1,10 @@ +local CompiledGRF = require("Core.FileFormats.Optimized.CompiledGRF") local RagnarokGRF = require("Core.FileFormats.RagnarokGRF") local C_Resources = require("Core.NativeClient.C_Resources") +local TEST_GRF_PATH = path.join("Tests", "Fixtures", "test.grf") + describe("C_Resources", function() describe("PreloadPersistentResources", function() local DEFAULT_GRF_PATH = C_Resources.GRF_FILE_PATH @@ -14,6 +17,7 @@ describe("C_Resources", function() it("should throw if the configured asset container doesn't exist", function() local function preloadFromNonExistingGRF() + C_Resources.ENABLE_CGRF_CACHING = false -- Cache access would throw C_Resources.GRF_FILE_PATH = "invalid.grf" C_Resources.PreloadPersistentResources() end @@ -32,7 +36,7 @@ describe("C_Resources", function() end) it("should load and store all persistent resources from the configured asset container", function() - C_Resources.GRF_FILE_PATH = path.join("Tests", "Fixtures", "test.grf") + C_Resources.GRF_FILE_PATH = TEST_GRF_PATH C_Resources.PERSISTENT_RESOURCES = { ["hello-grf.txt"] = false, ["subdirectory/hello.txt"] = false, @@ -98,5 +102,44 @@ describe("C_Resources", function() local preloadedAssetFiles = C_Resources.PERSISTENT_RESOURCES assertEquals(preloadedAssetFiles["uppercase.png"], expectedFileContents["uppercase.png"]) end) + + it("should generate a CGRF cache entry after loading the configured asset container", function() + C_Resources.ENABLE_CGRF_CACHING = true + C_Resources.GRF_FILE_PATH = TEST_GRF_PATH + C_Resources.PERSISTENT_RESOURCES = {} + C_Resources.PreloadPersistentResources() + + local cgrfFilePath = path.join("Tests", "Fixtures", "test.cgrf") + local expectedFileContents = C_FileSystem.ReadFile(cgrfFilePath) + + -- Creating a new cache entry manually should ensure it's considered up-to-date + local cacheEntryPath = path.join(CompiledGRF.CGRF_CACHE_DIRECTORY, "test.cgrf") + local cgrfFileContents = C_FileSystem.ReadFile(cacheEntryPath) + + C_FileSystem.Delete(cacheEntryPath) + + assertEquals(cgrfFileContents, expectedFileContents) + end) + + it("should use an existing CGRF cache entry if it exists and is still up-to-date", function() + -- Assumes that the fixture's CGRF is always more recent than the GRF itself + -- This should always be the case as it's regenerated from the GRF after each format change + local cgrfFilePath = path.join("Tests", "Fixtures", "test.cgrf") + local cgrfFileContents = C_FileSystem.ReadFile(cgrfFilePath) + + -- Creating a new cache entry manually should ensure it's considered up-to-date + local cacheEntryPath = path.join(CompiledGRF.CGRF_CACHE_DIRECTORY, "test.cgrf") + C_FileSystem.WriteFile(cacheEntryPath, cgrfFileContents) + + C_Resources.ENABLE_CGRF_CACHING = true + C_Resources.GRF_FILE_PATH = TEST_GRF_PATH + C_Resources.PERSISTENT_RESOURCES = {} + C_Resources.PreloadPersistentResources() + + C_FileSystem.Delete(cacheEntryPath) + + assertEquals(C_Resources.grf.fileTable.compressedSizeInBytes, 0) + assertEquals(C_Resources.grf.fileTable.decompressedSizeInBytes, 0) + end) end) end)