Download CommonLua/Dlc.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 28.9 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Dlc.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/Dlc.lua
-
curl -L -o Dlc.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Dlc.lua
28.9 kB
| --[==[ | |
| DLC OS-specific files have the following structure: | |
| DLC content is: | |
| |- autorun.lua | |
| |- revisions.lua | |
| |- Lua.hpk (if containing newer version of Lua, causes reload) | |
| |- Data.hkp (if containing newer version of Data) | |
| |- Data/... (additional data) | |
| |- Maps/... | |
| |- Sounds.hpk | |
| |- EntityTextures.hpk (additional entity textures) | |
| |- ... | |
| The Autorun returns a table with a description of the dlc: | |
| return { | |
| name = "Colossus", | |
| display_name = T{"Colossus"}, -- optional, must be a T with an id if present | |
| pre_load = function() end, | |
| post_load = function() end, | |
| required_lua_revision = 12345, -- skip loading this DLC below this revision | |
| } | |
| DLC mount steps: | |
| 1. Enumerate and mount OS-specific DLC packs (validating them on steam/pc) | |
| -- result is a list of folders containing autorun.lua files | |
| 2. Execute autorun.lua and revisions.lua; autorun.lua should set g_AvailableDlc[dlc.name] = true | |
| 3. Check required_lua_revision and call dlc:pre_load() for each dlc that passes the check | |
| 4. If necessary reload localization, lua and data from the lasest packs (it can update the follow up Dlc load steps) | |
| 5. If necessary reload the latest Dlc assets | |
| -- reload entities | |
| -- reload BinAssets | |
| -- reload sounds | |
| -- reload music | |
| 6. Call the dlc:post_load() for each dlc | |
| ]==] | |
| if FirstLoad then | |
| -- "" and false are "no dlc" values for the missions and achievements systems respectively | |
| g_AvailableDlc = {[""] = true, [false] = true} -- [achievement name] = true ; DLC code is expected to properly init this! | |
| g_DlcDisplayNames = {} -- [achievement name] = "translated string" | |
| DlcFolders = false | |
| DlcDefinitions = false | |
| DataLoaded = false | |
| end | |
| if FirstLoad and Platform.playstation then | |
| local err, list = AsyncPlayStationAddcontList(0) | |
| g_AddcontStatus = {} | |
| if not err then | |
| for _, addcont in ipairs(list) do | |
| g_AddcontStatus[addcont.label] = addcont.status | |
| end | |
| end | |
| end | |
| dlc_print = CreatePrint{ | |
| --"dlc" | |
| } | |
| --[[@@@ | |
| Returns if the player has a specific DLC installed. | |
| @function bool IsDlcAvailable(string dlc) | |
| @param dlc - The ID of a DLC. | |
| @result bool - If the DLC is available and loaded. | |
| ]] | |
| function IsDlcAvailable(dlc) | |
| dlc = dlc or false | |
| return g_AvailableDlc[dlc] | |
| end | |
| function IsDlcOwned(dlc) | |
| end | |
| function DLCPath(dlc) | |
| if not dlc or dlc == "" then return "" end | |
| return "DLC/" .. dlc | |
| end | |
| -- Use, for example, for marking savegames. In all other cases use IsDlcAvailable | |
| function GetAvailableDlcList() | |
| local dlcs = {} | |
| for dlc, v in pairs(g_AvailableDlc) do | |
| if v and dlc ~= "" and dlc ~= false then | |
| dlcs[ 1 + #dlcs ] = dlc | |
| end | |
| end | |
| table.sort(dlcs) | |
| return dlcs | |
| end | |
| function GetDeveloperDlcs() | |
| local dlcs = Platform.developer and IsFSUnpacked() and io.listfiles("svnProject/Dlc/", "*", "folders") or empty_table | |
| for i, folder in ipairs(dlcs) do | |
| dlcs[i] = string.gsub(folder, "svnProject/Dlc/", "") | |
| end | |
| table.sort(dlcs) | |
| return dlcs | |
| end | |
| DbgAllDlcs = false | |
| DbgAreDlcsMissing = return_true | |
| DbgIgnoreMissingDlcs = rawget(_G, "DbgIgnoreMissingDlcs") or {} | |
| if Platform.developer and IsFSUnpacked() then | |
| DbgAllDlcs = GetDeveloperDlcs() | |
| function DbgAreDlcsMissing() | |
| for _, dlc in ipairs(DbgAllDlcs or empty_table) do | |
| if not DbgIgnoreMissingDlcs[dlc] and not g_AvailableDlc[dlc] then | |
| return dlc | |
| end | |
| end | |
| end | |
| end | |
| -- Helper function for tying a savegame to a set of required DLCs | |
| -- metadata = FillDlcMetadata(metadata, dlcs) | |
| function FillDlcMetadata(metadata, dlcs) | |
| metadata = metadata or {} | |
| dlcs = dlcs or GetAvailableDlcList() | |
| local t = {} | |
| for _, dlc in ipairs(dlcs) do | |
| t[#t+1] = {id = dlc, name = g_DlcDisplayNames[dlc] or dlc} | |
| end | |
| metadata.dlcs = t | |
| return metadata | |
| end | |
| -- Step 1. Enumerate and mount OS-specific DLC packs (validating them on steam/pc) | |
| function DlcMountOsPacks() | |
| dlc_print("Mount Os Packs") | |
| local folders = {} | |
| local error = false | |
| if Platform.demo then | |
| dlc_print("Mount Os Packs early out: no DLCs in demo") | |
| return folders, error | |
| end | |
| if Platform.playstation then | |
| for label, status in pairs(g_AddcontStatus) do | |
| if status == const.PlaystationAddcontStatusInstalled then | |
| local addcont_error, mount_point = AsyncPlayStationAddcontMount(0, label) | |
| local pack_error = MountPack(label, mount_point .. "/content.hpk") | |
| error = error or addcont_error or pack_error | |
| table.insert(folders, label) | |
| end | |
| end | |
| dlc_print(string.format("PS4 Addcont: %d listed (%d mounted)", #g_AddcontStatus, #folders)) | |
| end | |
| if Platform.appstore then | |
| local content = AppStore.ListDownloadedContent() | |
| for i=1, #content do | |
| local folder = "Dlc" .. i | |
| local err = MountPack(folder, content[i].path) | |
| if not err then | |
| folders[#folders+1] = folder | |
| end | |
| end | |
| end | |
| if Platform.xbox then | |
| local list | |
| error, list = Xbox.EnumerateLocalDlcs() | |
| if not error then | |
| for idx = 1, #list do | |
| local folders_index = #folders+1 | |
| local err, mountDir = AsyncXboxMountDLC(list[idx][1]) | |
| if not err then | |
| err = MountPack("Dlc" .. folders_index, mountDir .. "/content.hpk") | |
| error = error or err | |
| if not err then | |
| folders[folders_index] = "Dlc" .. folders_index | |
| end | |
| end | |
| end | |
| end | |
| end | |
| if Platform.windows_store then | |
| local err, list = WindowsStore.MountDlcs() | |
| if not err then | |
| for i=1, #list do | |
| local folder = list[i] | |
| local folders_index = #folders+1 | |
| err = MountPack("Dlc" .. folders_index, folder .. "/content.hpk") | |
| if not err then | |
| folders[folders_index] = "Dlc" .. folders_index | |
| end | |
| end | |
| end | |
| end | |
| if not DlcFolders then -- Load the embedded DLCs only once | |
| if Platform.developer and IsFSUnpacked() then | |
| local dev_list = Platform.developer and IsFSUnpacked() and io.listfiles("svnProject/Dlc/", "*", "folders") or empty_table | |
| for _, folder in ipairs(dev_list) do | |
| local dlc = string.gsub(folder, "svnProject/Dlc/", "") | |
| if not (LocalStorage.DisableDLC and LocalStorage.DisableDLC[dlc]) then | |
| folders[#folders + 1] = folder | |
| end | |
| end | |
| else | |
| local files = io.listfiles("AppData/DLC/", "*.hpk", "non recursive") or {} | |
| table.iappend(files, io.listfiles("DLC/", "*.hpk", "non recursive")) | |
| if Platform.linux then | |
| table.iappend(files, io.listfiles("dlc/", "*.hpk", "non recursive")) | |
| end | |
| if Platform.pgo_train then | |
| table.iappend(files, io.listfiles("../win32-dlc", "*.hpk", "non recursive")) | |
| end | |
| dlc_print("Dlc os packs: ", files) | |
| for i=1,#files do | |
| local folder = "Dlc" .. tostring(#folders+1) | |
| local err = MountPack(folder, files[i]) | |
| if not err then | |
| table.insert(folders, folder) | |
| end | |
| end | |
| end | |
| end | |
| dlc_print("Dlc folders: ", folders) | |
| return folders, error | |
| end | |
| -- 2. Execute autorun.lua and revisions.lua | |
| function DlcAutoruns(folders) | |
| dlc_print("Dlc Autoruns") | |
| local dlcs = {} | |
| -- dlc.folder points to the autorun mount | |
| for i = 1, #folders do | |
| local folder = folders[i] | |
| local dlc = dofile(folder .. "/autorun.lua") | |
| if type(dlc) == "function" then | |
| dlc = dlc(folder) | |
| end | |
| if type(dlc) == "table" then | |
| dlc_print("Autorun executed for", dlc.name) | |
| dlc.folder = folder | |
| if Platform.developer and folder:starts_with("svnProject/Dlc") then | |
| dlc.lua_revision, dlc.assets_revision = LuaRevision, AssetsRevision | |
| else | |
| dlc.lua_revision, dlc.assets_revision = dofile(folder .. "/revisions.lua") | |
| end | |
| table.insert(dlcs, dlc) | |
| DebugPrint(string.format("DLC %s loaded, lua revision %d, assets revision %d\n", tostring(dlc.name), dlc.lua_revision or 0, dlc.assets_revision or 0)) | |
| else | |
| print("Autorun failed:", folder) | |
| end | |
| end | |
| return dlcs | |
| end | |
| -- 3. Call the dlc:pre_load() to all dlcs. Let a DLC decide that it doesn't want to be installed | |
| function DlcPreLoad(dlcs) | |
| local revision | |
| for i = #dlcs, 1, -1 do | |
| local required_lua_revision = dlcs[i].required_lua_revision | |
| if required_lua_revision and required_lua_revision <= LuaRevision then | |
| required_lua_revision = nil -- the required revision is lower, ignore condition | |
| end | |
| revision = Max(revision, required_lua_revision) | |
| local pre_load = dlcs[i].pre_load or empty_func | |
| if required_lua_revision or pre_load(dlcs[i]) == "remove" then | |
| dlc_print("Dlc removed:", dlcs[i].name, required_lua_revision or "") | |
| table.remove_value(DlcFolders, dlcs[i].folder) | |
| table.remove(dlcs, i) | |
| end | |
| end | |
| return revision | |
| end | |
| function GetDlcRequiresTitleUpdateMessage() | |
| local id = TGetID(MessageText.DlcRequiresUpdate) | |
| if id and TranslationTable[id] then | |
| return TranslationTable[id] | |
| end | |
| -- fallback | |
| local language, strMessage = GetLanguage(), nil | |
| if language == "French" then | |
| strMessage = "Certains contenus téléchargeables nécessitent l'installation d'une mise à jour du jeu pour fonctionner." | |
| elseif language == "Italian" then | |
| strMessage = "Alcuni contenuti scaricabili richiedono un aggiornamento del titolo per essere utilizzati." | |
| elseif language == "German" then | |
| strMessage = "Bei einigen Inhalten zum Herunterladen ist ein Update notwendig, damit sie funktionieren." | |
| elseif language == "Spanish" or language == "Latam" then | |
| strMessage = "Ciertos contenidos descargables requieren una actualización para funcionar." | |
| elseif language == "Polish" then | |
| strMessage = "Część zawartości do pobrania wymaga aktualizacji gry." | |
| elseif language == "Russian" then | |
| strMessage = "Загружаемый контент требует обновления игры." | |
| else | |
| strMessage = "Some downloadable content requires a title update in order to work." | |
| end | |
| return strMessage | |
| end | |
| local function find(dlcs, path, rev, rev_name) | |
| local found | |
| for i = #dlcs, 1, -1 do | |
| local dlc = dlcs[i] | |
| if dlc[rev_name] > rev and io.exists(dlc.folder .. path) then | |
| rev = dlc[rev_name] | |
| found = dlc | |
| end | |
| end | |
| if found then | |
| return found.folder .. path, found | |
| end | |
| end | |
| -- 4. If necessary reload localization, lua and data from the lasest packs (it can update the follow up Dlc load steps) | |
| function DlcReloadLua(dlcs, late_dlc_reload) | |
| local lang_reload | |
| local reload = late_dlc_reload | |
| -- mount latest localization | |
| local lang_pack = find(dlcs, "/Local/" .. GetLanguage() .. ".hpk", LuaRevision, "lua_revision") | |
| if lang_pack then | |
| dlc_print(" - localization:", lang_pack) | |
| MountPack("", lang_pack, "", "CurrentLanguage") | |
| lang_reload = true | |
| end | |
| -- English language for e.g. the Mod Editor on PC | |
| if config.GedLanguageEnglish then | |
| local engl_pack = find(dlcs, "/Local/English.hpk", LuaRevision, "lua_revision") | |
| if engl_pack then | |
| MountPack("", engl_pack, "", "EnglishLanguage") | |
| end | |
| end | |
| -- reload entities | |
| local binassets_path = "/BinAssets.hpk" | |
| local binassets_pack = find(dlcs, binassets_path, AssetsRevision, "assets_revision") | |
| if binassets_pack then | |
| dlc_print(" - BinAssets:", binassets_pack) | |
| UnmountByPath("BinAssets") | |
| local err = MountPack("BinAssets", binassets_pack) | |
| dlc_print(" - BinAssets:", binassets_pack, "ERROR", err) | |
| ReloadEntities("BinAssets/entities.dat") | |
| ReloadTextureHeaders() | |
| reload = true | |
| end | |
| -- reload Lua | |
| if late_dlc_reload then -- clean the global tables to prevent duplication | |
| Presets = {} | |
| ClassDescendants("Preset", function(name, class) | |
| if class.GlobalMap then | |
| _G[class.GlobalMap] = {} | |
| end | |
| end) | |
| end | |
| local lua_pack, dlc = find(dlcs, "/Lua.hpk", LuaRevision, "lua_revision") | |
| if lua_pack then | |
| dlc_print(" - lua:", dlc.folder .. "/Lua.hpk") | |
| assert(not config.RunUnpacked) | |
| UnmountByLabel("Lua") | |
| LuaPackfile = lua_pack | |
| reload = true | |
| end | |
| local data_pack, dlc = find(dlcs, "/Data.hpk", LuaRevision, "lua_revision") | |
| if data_pack then | |
| assert(io.exists(dlc.folder .. "/Data.hpk")) | |
| UnmountByLabel("Data") | |
| DataPackfile = data_pack | |
| end | |
| for i = 1, #dlcs do | |
| if io.exists(dlcs[i].folder .. "/Code/") then | |
| reload = true | |
| break | |
| end | |
| end | |
| reload = reload or config.Mods and next(ModsLoaded) | |
| if reload then | |
| ReloadLua(true) | |
| end | |
| if lang_reload and not reload then | |
| LoadTranslationTables() | |
| end | |
| end | |
| function DlcMountVoices(dlcs, skip_sort) | |
| UnmountByLabel("DlcVoices") | |
| if not dlcs then return end | |
| -- Mount all available voices packs in the multi (order by assets revision in case we want to fix a voice from one DLC from a later one) | |
| local sorted_dlcs | |
| if not skip_sort then | |
| sorted_dlcs = table.copy(dlcs) | |
| table.stable_sort(sorted_dlcs, function (a, b) return a.assets_revision < b.assets_revision end) | |
| end | |
| for i, dlc in ipairs(sorted_dlcs or dlcs) do | |
| local voice_pack = string.format("%s/Local/Voices/%s.hpk", dlc.folder, GetVoiceLanguage()) | |
| if MountPack("CurrentLanguage/Voices", voice_pack, "seethrough,label:DlcVoices") then | |
| dlc_print(" - localization voice: ", voice_pack) | |
| end | |
| end | |
| end | |
| function DlcMountMapPacks(dlcs) | |
| for _, dlc in ipairs(dlcs) do | |
| for _, map_pack in ipairs(io.listfiles(dlc.folder .. "/Maps", "*.hpk")) do | |
| local map_name = string.match(map_pack, ".*/Maps/([^/]*).hpk") | |
| if map_name then | |
| MapPackfile[map_name] = map_pack | |
| end | |
| end | |
| end | |
| end | |
| function DlcMountUI(dlcs) | |
| local asset_path = dlcs.folder .. "/UI/" | |
| if io.exists(asset_path) then | |
| local err = MountFolder("UI", asset_path, "seethrough") | |
| dlc_print(" - UI:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcMountNonEntityTextures(dlcs) | |
| local asset_path = find(dlcs, "/AdditionalNETextures.hpk", AssetsRevision, "assets_revision") | |
| if asset_path then | |
| UnmountByLabel("AdditionalNETextures") | |
| local err = MountPack("", asset_path, "priority:high,seethrough,label:AdditionalNETextures") | |
| dlc_print(" - non-entity textures:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcMountAdditionalEntityTextures(dlcs) | |
| local asset_path = find(dlcs, "/AdditionalTextures.hpk", AssetsRevision, "assets_revision") | |
| if asset_path then | |
| UnmountByLabel("AdditionalTextures") | |
| local err = MountPack("", asset_path, "priority:high,seethrough,label:AdditionalTextures") | |
| dlc_print(" - entity textures:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcMountSounds(dlcs) | |
| local asset_path = dlcs.folder .. "/Sounds/" | |
| if io.exists(asset_path) then | |
| local err = MountFolder("Sounds", asset_path, "seethrough") | |
| dlc_print(" - Sounds:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcMountMeshesAndAnimations(dlcs) | |
| local meshes_pack = find(dlcs, "/Meshes.hpk", AssetsRevision, "assets_revision") | |
| if meshes_pack then | |
| dlc_print(" - Meshes:", meshes_pack) | |
| UnmountByPath("Meshes") | |
| MountPack("Meshes", meshes_pack) | |
| else | |
| -- If we reload DLCs in packed mode, make sure to have the original meshes first | |
| if MountsByPath("Meshes") == 0 and not IsFSUnpacked() then | |
| MountPack("Meshes", "Packs/Meshes.hpk") | |
| end | |
| end | |
| local animations_pack = find(dlcs, "/Animations.hpk", AssetsRevision, "assets_revision") | |
| if animations_pack then | |
| dlc_print(" - Animations:", animations_pack) | |
| UnmountByPath("Animations") | |
| MountPack("Animations", animations_pack) | |
| else | |
| if MountsByPath("Animations") == 0 and not IsFSUnpacked() then | |
| MountPack("Animations", "Packs/Animations.hpk") | |
| end | |
| end | |
| -- mount additional meshes and animations for each DLC | |
| for i, dlc in ipairs(dlcs) do | |
| MountPack("", dlc.folder .. "/DlcMeshes.hpk", "seethrough,label:DlcMeshes") | |
| MountPack("", dlc.folder .. "/DlcAnimations.hpk", "seethrough,label:DlcAnimations") | |
| MountPack("", dlc.folder .. "/DlcSkeletons.hpk", "seethrough,label:DlcSkeletons") | |
| MountPack("BinAssets", dlc.folder .. "/DlcBinAssets.hpk", "seethrough,label:DlcBinAssets") | |
| end | |
| --common assets should be processed before the rest | |
| UnmountByLabel("CommonAssets") | |
| MountPack("", "Packs/CommonAssets.hpk", "seethrough,label:CommonAssets") | |
| end | |
| function DlcReloadShaders(dlcs) | |
| -- box DX9 and DX11 shader packs should be provided or missing | |
| local asset_path, dlc = find(dlcs, "/ShaderCache" .. config.GraphicsApi .. ".hpk", AssetsRevision, "assets_revision") | |
| if asset_path then | |
| dlc_print(" - ShaderCache:", asset_path) | |
| UnmountByPath("ShaderCache") | |
| MountPack("ShaderCache", asset_path, "seethrough,in_mem,priority:high") | |
| -- NOTE: new shader cache will be reloaded not on start up(main menu) but on next map/savegame load | |
| hr.ForceShaderCacheReload = true | |
| end | |
| end | |
| function DlcAddMusic(dlcs) | |
| local asset_path = dlcs.folder .. "/Music/" | |
| if io.exists(asset_path) then | |
| local err = MountFolder("Music/" .. dlc.name, asset_path) | |
| dlc_print(" - Music:", asset_path, "ERROR", err) | |
| Playlists[dlc.name] = PlaylistCreate("Music/" .. dlc.name) | |
| end | |
| end | |
| function DlcAddCubemaps(dlcs) | |
| local asset_path = dlcs.folder .. "/Cubemaps/" | |
| if io.exists(asset_path) then | |
| local err = MountFolder("Cubemaps", asset_path, "seethrough") | |
| dlc_print(" - Cubemaps:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcAddBillboards(dlcs) | |
| local asset_path = dlcs.folder .. "/Textures/Billboards/" | |
| if io.exists(asset_path) then | |
| local err = MountFolder("Textures/Billboards", asset_path, "seethrough") | |
| dlc_print(" - Billboards:", asset_path, "ERROR", err) | |
| end | |
| end | |
| function DlcMountMovies(dlcs) | |
| if IsFSUnpacked() then return end | |
| for _, dlc in ipairs(dlcs) do | |
| local path = dlc.folder .. "/Movies/" | |
| if io.exists(path) then | |
| local err = MountFolder("Movies/", path, "seethrough") | |
| dlc_print(" - DlcMovies:", path, err and "ERROR", err) | |
| end | |
| end | |
| end | |
| function DlcMountBinAssets(dlcs) | |
| if IsFSUnpacked() then return end | |
| for _, dlc in ipairs(dlcs) do | |
| local path = dlc.folder .. "/BinAssets/" | |
| if io.exists(path) then | |
| local err = MountFolder("BinAssets/", path, "seethrough") | |
| dlc_print(" - DlcBinAssets:", path, err and "ERROR", err) | |
| end | |
| end | |
| end | |
| function DlcMountMisc(dlcs) | |
| UnmountByLabel("DlcMisc") | |
| for _, dlc in ipairs(dlcs) do | |
| local path = dlc.folder .. "/Misc/" | |
| if io.exists(path) then | |
| local err = MountFolder("Misc/", path, "seethrough,label:DlcMisc") | |
| dlc_print(" - DlcMisc:", path, err and "ERROR", err) | |
| end | |
| end | |
| end | |
| -- 5. If necessary reload the latest Dlc assets | |
| function DlcReloadAssets(dlcs) | |
| dlcs = table.copy(dlcs) | |
| table.stable_sort(dlcs, function (a, b) return a.assets_revision < b.assets_revision end) | |
| -- mount map packs found in Maps/ | |
| DlcMountMapPacks(dlcs) | |
| for _, dlc in pairs(dlcs) do | |
| -- mount the dlc UI | |
| DlcMountUI(dlc) | |
| -- mount the dlc sounds | |
| DlcMountSounds(dlc) | |
| -- mount the dlc music to the default playlist | |
| DlcAddMusic(dlc) | |
| -- mount the dlc cubemaps | |
| DlcAddCubemaps(dlc) | |
| -- mount the dlc billboards | |
| DlcAddBillboards(dlc) | |
| end | |
| -- mount the most recent additional non-entity textures | |
| DlcMountNonEntityTextures(dlcs) | |
| -- mount the most recent additional entity textures | |
| DlcMountAdditionalEntityTextures(dlcs) | |
| -- mount latest meshes and animations plus additional ones in Dlcs | |
| DlcMountMeshesAndAnimations(dlcs) | |
| -- find latest shaders; OpenGL shaders are not reloaded | |
| DlcReloadShaders(dlcs) | |
| -- mount movies | |
| DlcMountMovies(dlcs) | |
| -- | |
| DlcMountBinAssets(dlcs) | |
| -- mount Misc | |
| DlcMountMisc(dlcs) | |
| -- mount voices | |
| DlcMountVoices(dlcs, true) | |
| end | |
| -- 6. Call the dlc.post_load() for each dlc | |
| function DlcPostLoad(dlcs) | |
| for _, dlc in ipairs(dlcs) do | |
| if dlc.post_load then dlc:post_load() end | |
| end | |
| end | |
| function DlcErrorHandler(err) | |
| print("DlcErrorHandler", err, GetStack()) | |
| end | |
| function WaitInitialDlcLoad() -- does nothing on reloads (like what happens on Xbox) | |
| if not DlcFolders then | |
| WaitMsg("DlcsLoaded") | |
| end | |
| end | |
| function LoadDlcs(force_reload) | |
| if Platform.developer and (LuaRevision == 0 or AssetsRevision == 0) then | |
| for i=1, 50 do | |
| Sleep(50) | |
| if LuaRevision ~= 0 and AssetsRevision ~= 0 then break end | |
| end | |
| if LuaRevision == 0 or AssetsRevision == 0 then | |
| print("Couldn't get LuaRevision or AssetsRevision, DLC loading may be off") | |
| end | |
| end | |
| if DlcFolders and not force_reload then | |
| return | |
| end | |
| if force_reload then | |
| ForceReloadBinAssets() | |
| DlcFolders = false | |
| end | |
| if Platform.appstore then | |
| local err = CopyDownloadedDLCs() | |
| if err then | |
| print("Failed to copy downloaded DLCs", err) | |
| end | |
| end | |
| LoadingScreenOpen("idDlcLoading", "dlc loading", T(808151841545, "Checking for downloadable content... Please wait.")) | |
| -- 1. Mount OS packs | |
| local bCorrupt = false | |
| local folders, err = DlcMountOsPacks() | |
| if err == "File is corrupt" then | |
| bCorrupt = true | |
| err = false | |
| end | |
| if err then | |
| DlcErrorHandler(err) | |
| end | |
| local dlcs = DlcAutoruns(folders) | |
| table.stable_sort(dlcs, function (a, b) return a.lua_revision < b.lua_revision end) | |
| UnmountByLabel("Dlc") | |
| local seen_dlcs = {} | |
| for i, dlc in ripairs(dlcs) do | |
| if seen_dlcs[dlc.name] then | |
| table.remove(dlcs, i) | |
| else | |
| seen_dlcs[dlc.name] = true | |
| dlc.title = dlc.title or dlc.display_name and _InternalTranslate(dlc.display_name) or dlc.name | |
| if not dlc.folder:starts_with("svnProject/Dlc/") then | |
| local org_folder = dlc.folder | |
| dlc.folder = "Dlc/" .. dlc.name | |
| MountFolder(dlc.folder, org_folder, "priority:high,label:Dlc") | |
| end | |
| end | |
| end | |
| DlcFolders = table.map(dlcs, "folder") | |
| DlcDefinitions = table.copy(dlcs) | |
| local bRevision = DlcPreLoad(dlcs) | |
| dlc_print("Dlc tables after preload:\n", table.map(dlcs, "name")) | |
| if config.Mods then | |
| -- load mod items in the same loading screen | |
| ModsReloadDefs() | |
| ModsReloadItems(nil, nil, true) | |
| end | |
| DlcReloadLua(dlcs, force_reload) | |
| DlcReloadAssets(dlcs) | |
| local metaCheck = const.PrecacheDontCheck | |
| if Platform.test then | |
| metaCheck = Platform.pc and const.PrecacheCheckUpToDate or const.PrecacheCheckExists | |
| end | |
| for _, dlc in ipairs(dlcs) do | |
| ResourceManager.LoadPrecacheMetadata("BinAssets/resources-" .. dlc.name .. ".meta", metaCheck) | |
| end | |
| DlcPostLoad(dlcs) | |
| -- Collect and translate the DLC display names. | |
| -- We are just after the step that would reload the localization, so this would handle the case | |
| -- where a DLC display name is translated in the new DLC-provided localization | |
| local dlc_names = GetAvailableDlcList() | |
| for i=1, #dlc_names do | |
| local dlc_metadata = table.find_value(dlcs, "name", dlc_names[i]) | |
| assert(dlc_metadata) | |
| local display_name = dlc_metadata.display_name | |
| if display_name then | |
| if not IsT(display_name) or not TGetID(display_name) then | |
| print("DLC", dlc_names[i], "display_name must be a localized T!") | |
| end | |
| display_name = _InternalTranslate(display_name) | |
| assert(type(display_name)=="string") | |
| g_DlcDisplayNames[ dlc_names[i] ] = display_name | |
| end | |
| end | |
| local dlc_names = GetAvailableDlcList() | |
| if next(dlc_names) then | |
| local infos = {} | |
| for i=1, #dlc_names do | |
| local dlcname = dlc_names[i] | |
| infos[i] = string.format("%s(%s)", dlcname, g_DlcDisplayNames[dlcname] or "") | |
| end | |
| print("Available DLCs:", table.concat(infos, ",")) | |
| end | |
| Msg("DlcsLoaded") | |
| LoadData(dlcs) | |
| if config.Mods and next(ModsLoaded) then | |
| ContinueModsReloadItems() | |
| end | |
| if not Platform.developer then | |
| if not config.Mods or not Platform.pc then -- the mod creation (available on PC only) needs the data and lua sources to be able to copy functions | |
| UnmountByLabel("Lua") | |
| UnmountByLabel("Data") | |
| end | |
| end | |
| -- Messages should be shown after LoadData(), as there are UI presets that need to be present | |
| local interactive = not (Platform.developer and GetIgnoreDebugErrors()) | |
| if interactive then | |
| if bCorrupt then | |
| WaitMessage(GetLoadingScreenDialog() or terminal.desktop, "", T(619878690503, --[[error_message]] "A downloadable content file appears to be damaged and cannot be loaded. Please delete it from the Memory section of the Dashboard and download it again."), nil, terminal.desktop) | |
| end | |
| if bRevision then | |
| local message = Untranslated(GetDlcRequiresTitleUpdateMessage()) | |
| WaitMessage(GetLoadingScreenDialog() or terminal.desktop, "", message) | |
| end | |
| end | |
| LoadingScreenClose("idDlcLoading", "dlc loading") | |
| UIL.Invalidate() | |
| end | |
| function LoadData(dlcs) | |
| PauseInfiniteLoopDetection("LoadData") | |
| collectgarbage("collect") | |
| collectgarbage("stop") | |
| Msg("DataLoading") | |
| MsgClear("DataLoading") | |
| LoadPresetFiles("CommonLua/Data") | |
| LoadPresetFolders("CommonLua/Data") | |
| ForEachLib("Data", function (lib, path) | |
| LoadPresetFiles(path) | |
| LoadPresetFolders(path) | |
| end) | |
| LoadPresetFolder("Data") | |
| for _, dlc in ipairs(dlcs or empty_table) do | |
| LoadPresetFolder(dlc.folder .. "/Presets") | |
| end | |
| Msg("DataPreprocess") | |
| MsgClear("DataPreprocess") | |
| Msg("DataPostprocess") | |
| MsgClear("DataPostprocess") | |
| Msg("DataLoaded") | |
| MsgClear("DataLoaded") | |
| DataLoaded = true | |
| local mem = collectgarbage("count") | |
| collectgarbage("collect") | |
| collectgarbage("restart") | |
| -- printf("Load Data mem %dk, peak %dk", collectgarbage("count"), mem) | |
| ResumeInfiniteLoopDetection("LoadData") | |
| end | |
| function WaitDataLoaded() | |
| if not DataLoaded then | |
| WaitMsg("DataLoaded") | |
| end | |
| end | |
| if Platform.xbox then | |
| local oldLoadDlcs = LoadDlcs | |
| function LoadDlcs(...) | |
| SuspendSigninChecks("load dlcs") | |
| SuspendInviteChecks("load dlcs") | |
| sprocall(oldLoadDlcs, ...) | |
| ResumeSigninChecks("load dlcs") | |
| ResumeInviteChecks("load dlcs") | |
| end | |
| end | |
| function OnMsg.BugReportStart(print_func) | |
| local list = GetAvailableDlcList() | |
| table.sort(list) | |
| print_func("Dlcs: " .. table.concat(list, ", ")) | |
| end | |
| function DlcComboItems(additional_item) | |
| local items = {{ text = "", value = ""}} | |
| for _, def in ipairs(DlcDefinitions) do | |
| if not def.deprecated then | |
| local name, title = def.name, def.title | |
| if name ~= title then | |
| title = name .. " (" .. title .. ")" | |
| end | |
| items[#items + 1] = {text = title, value = name} | |
| end | |
| end | |
| if additional_item then | |
| table.insert(items, 2, additional_item) | |
| end | |
| return items | |
| end | |
| function DlcCombo(additional_item) | |
| return function() | |
| return DlcComboItems(additional_item) | |
| end | |
| end | |
| function RedownloadContent(list, progress) | |
| if not NetIsConnected() then return "disconnected" end | |
| progress(0) | |
| AsyncCreatePath("AppData/DLC") | |
| AsyncCreatePath("AppData/DownloadedDLC") | |
| for i = 1, #list do | |
| local dlc_name = list[i] | |
| local name = dlc_name .. ".hpk" | |
| local download_file = string.format("AppData/DownloadedDLC/%s.download", dlc_name) | |
| local dlc_file = string.format("AppData/DLC/%s.hpk", dlc_name) | |
| local err, def = NetCall("rfnGetContentDef", name) | |
| if not err and def then | |
| local err, local_def = CreateContentDef(download_file, def.chunk_size) | |
| if err == "Path Not Found" or err == "File Not Found" then | |
| err, local_def = CreateContentDef(dlc_file, def.chunk_size) | |
| end | |
| if local_def then local_def.name = name end | |
| local start_progress = 100 * (i - 1) / #list | |
| local file_progress = 100 * i / #list - start_progress | |
| start_progress = start_progress + file_progress / 10 | |
| progress(start_progress) | |
| err = NetDownloadContent(download_file, def, | |
| function (x, y) | |
| progress(start_progress + MulDivRound(file_progress * 9 / 10, x, y)) | |
| end, | |
| local_def) | |
| if not err then | |
| local downloaded_dlc_file = string.format("AppData/DownloadedDLC/%s.hpk", dlc_name) | |
| os.remove(downloaded_dlc_file) | |
| os.rename(download_file, downloaded_dlc_file) | |
| end | |
| end | |
| progress(100 * i / #list) | |
| end | |
| end | |
| function CopyDownloadedDLCs() | |
| AsyncCreatePath("AppData/DLC") | |
| AsyncCreatePath("AppData/DownloadedDLC") | |
| local err, new_dlcs = AsyncListFiles("AppData/DownloadedDLC", "*.hpk", "relative") | |
| if err then return err end | |
| for i = 1, #new_dlcs do | |
| local src = "AppData/DownloadedDLC/" .. new_dlcs[i] | |
| if not AsyncCopyFile(src, "AppData/DLC/" .. new_dlcs[i], "raw") then | |
| AsyncFileDelete(src) | |
| end | |
| end | |
| end | |
| function DlcsLoadCode() | |
| for i = 1, #(DlcFolders or "") do | |
| dofolder(DlcFolders[i] .. "/Code/") | |
| end | |
| end | |
| function ReloadDevDlcs() | |
| CreateRealTimeThread(function() | |
| OpenPreGameMainMenu() | |
| DlcFolders = false | |
| for dlc in pairs(LocalStorage.DisableDLC or empty_table) do | |
| g_AvailableDlc[dlc] = nil | |
| end | |
| SaveLocalStorage() | |
| ClassDescendants("Preset", function(name, preset, Presets) | |
| --purge presets, which are saved in Data, we are reloading it | |
| if preset:GetSaveFolder() == "Data" then | |
| if preset.GlobalMap then | |
| _G[preset.GlobalMap] = {} | |
| end | |
| Presets[preset.PresetClass or name] = {} | |
| end | |
| end, Presets) | |
| LoadDlcs("force reload") | |
| end) | |
| end | |
| function SetAllDevDlcs(enable) | |
| local disabled = not enable | |
| LocalStorage.DisableDLC = LocalStorage.DisableDLC or {} | |
| for _, file in ipairs(io.listfiles("svnProject/Dlc/", "*", "folders")) do | |
| local dlc = string.gsub(file, "svnProject/Dlc/", "") | |
| if (LocalStorage.DisableDLC[dlc] or false) ~= disabled then | |
| LocalStorage.DisableDLC[dlc] = disabled | |
| DelayedCall(0, ReloadDevDlcs) | |
| end | |
| end | |
| end | |
| function SaveDLCOwnershipDataToDisk(data, file_path) | |
| local machine_id = GetMachineID() | |
| if (machine_id or "") ~= "" then -- don't save to disk without machine id | |
| data.machine_id = machine_id | |
| --encrypt data and machine id and save to disk | |
| SaveLuaTableToDisk(data, file_path, g_encryption_key) | |
| end | |
| end | |
| function LoadDLCOwnershipDataFromDisk(file_path) | |
| if io.exists(file_path) then | |
| --decrypt the file | |
| local data, err = LoadLuaTableFromDisk(file_path, nil, g_encryption_key) | |
| if not err then | |
| if data and (data.machine_id or "") == GetMachineID() then -- check against current machine id | |
| data.machine_id = nil -- remove the machine_id from the data, no need for it | |
| return data | |
| end | |
| end | |
| end | |
| return {} | |
| end | |