public void PatchDirArchive(ModData mod, string archivepath) { string archive_dir = mod.Path + archivepath; // Check if the mod has a directory for wad resources if (Directory.Exists(archive_dir)) { Archive archive = new Archive(TempPath + archivepath + ".wad"); archive.Load(); int i, j; int count = 0; // Go through the groups in the archive file. for (i = 0; i < archive.Groups.Count; i++) { var group = archive.Groups[i]; //Check for matching subdirectory and archive group. if (Directory.Exists(archive_dir + "/" + group.Name)) { //Go through the entries in the archive file for (j = 0; j < archive.Groups[i].Entries.Count; j++) { var entry = archive.Groups[i].Entries[j]; //Check for matching file and archive entry. string filepath = archive_dir + "/" + group.Name + "/" + entry.Name; if (File.Exists(filepath)) { Byte[] data = File.ReadAllBytes(filepath); var new_entry = new Entry(entry.Name, data); //Replace entry in the archive.wad with file in mod archive folder. archive.Groups[i].Entries[j] = new_entry; //Msg.Log("Patching '" + group.Name + "/" + entry.Name + "' from " + mod.Name); count++; } } } } archive.Save(); if (count > 0) Msg.Log("Patched " + count + " " + archivepath + " resources for " + mod.Name); } }
public void PatchZipArchive(ModData mod, string archivepath) { ZipFile zip = new ZipFile(mod.Path); // Check if the mod has a directory for wad resources if (zip.ContainsEntry(archivepath + "/")) { Archive archive = new Archive(TempPath + archivepath + ".wad"); archive.Load(); int i, j; int count = 0; // Go through the groups in the archive file. for (i = 0; i < archive.Groups.Count; i++) { var group = archive.Groups[i]; //Check for matching subdirectory and archive group. string grouppath = archivepath + "/" + group.Name + "/"; if (zip.ContainsEntry(grouppath)) { //Go through the entries in the archive file for (j = 0; j < archive.Groups[i].Entries.Count; j++) { var entry = archive.Groups[i].Entries[j]; //Check for matching file and archive entry. string entrypath = grouppath + entry.Name; if (zip.ContainsEntry(entrypath)) { //Msg.Log("zip: " + mod.Name + ", matching res-entry: " + entrypath); MemoryStream stream = new MemoryStream(); zip[entrypath].Extract(stream); Byte[] data = stream.GetBuffer(); var new_entry = new Entry(entry.Name, data); //Replace entry in the archive.wad with file in mod archive folder. archive.Groups[i].Entries[j] = new_entry; //Msg.Log("Patching '" + group.Name + "/" + entry.Name + "' from zip " + mod.Name); count++; } } } } archive.Save(); if (count > 0) Msg.Log("Patched " + count + " " + archivepath + " resources for " + mod.Name); } }
//Extracts textures and sounds to a directory (for modders) private void btnExtractArchives_Click(object sender, EventArgs e) { if (Setup.CheckBackups() == false) { Msg.Log("Failed to extract the game archives."); string message = "Patchlunky is missing Spelunky backups!" + Environment.NewLine + Environment.NewLine + "Patchlunky cannot extract the game archives without backups."; Msg.MsgBox(message, "Patchlunky"); return; //Backups are missing, can't extract. } Archive alltex = new Archive(Setup.BackupPath + "Textures/alltex.wad"); alltex.Load(); foreach (var group in alltex.Groups) { string dirpath = Path.Combine(Setup.TempPath + "alltex/", group.Name); Directory.CreateDirectory(dirpath); foreach (var entry in group.Entries) { string filepath = Path.Combine(dirpath, entry.Name); File.WriteAllBytes(filepath, entry.Data); } } Msg.Log("Extracted alltex.wad to ./Patchlunky_Temp/alltex/"); Archive allsounds = new Archive(Setup.BackupPath + "Sounds/allsounds.wad"); allsounds.Load(); foreach (var group in allsounds.Groups) { string dirpath = Path.Combine(Setup.TempPath + "allsounds/", group.Name); Directory.CreateDirectory(dirpath); foreach (var entry in group.Entries) { string filepath = Path.Combine(dirpath, entry.Name); File.WriteAllBytes(filepath, entry.Data); } } Msg.Log("Extracted allsounds.wad to ./Patchlunky_Temp/allsounds/"); }
// Patch the game files public bool PatchGame(bool reset_only) { Settings settings = Program.mainForm.Settings; ModManager modMan = Program.mainForm.ModMan; SkinManager skinMan = Program.mainForm.SkinMan; string message; if (CheckGameDir() == false) { message = "The Spelunky directory is not set!" + Environment.NewLine + Environment.NewLine + "Patchlunky cannot patch the game until the directory is set."; Msg.MsgBox(message, "Patchlunky Setup"); return false; //Path to game has not been set, can't patch. } if (CheckBackups() == false) { message = "Patchlunky is missing Spelunky backups!" + Environment.NewLine + Environment.NewLine + "Patchlunky cannot patch the game without backups."; Msg.MsgBox(message, "Patchlunky Setup"); return false; //Backups are missing, can't patch. } Program.mainForm.SetProgress(0); // STEP 1 OF 4 - Copy default data files to temp folder foreach (GameFile gmfile in DefaultFiles) { try { string srcfile = this.BackupPath + gmfile.FilePath; string dstfile = this.TempPath + gmfile.FilePath; Directory.CreateDirectory(Path.GetDirectoryName(dstfile)); File.Copy(srcfile, dstfile, true); } catch (Exception ex) { Msg.MsgBox("Error trying to copy file: " + ex.Message, "Patchlunky Setup"); return false; //Stop patching } } Program.mainForm.SetProgress(33); // STEP 2 OF 4 - Copy mod files to temp folder if ((reset_only == false) && (modMan.Mods.Count > 0)) { foreach (ModData mod in modMan.Mods) { if (mod.Enabled == false) continue; //Patch game files in the mod. if (PatchFiles(mod) == false) return false; //Stop patching //Patch wad resources in the mod. PatchArchive(mod, "Textures/alltex"); PatchArchive(mod, "Sounds/allsounds"); } } // STEP 3 OF 4 - Patch alltex.wad in temp folder with skin files if ((reset_only == false) && (skinMan.SkinConfig.Count > 0)) { Archive archive = new Archive(TempPath + "Textures/alltex" + ".wad"); archive.Load(); int i = archive.Groups.FindIndex(o => o.Name.Equals("PLAYERS", StringComparison.OrdinalIgnoreCase)); if (i == -1) return false; //Missing "PLAYERS" group in archive, stop patching int count = 0; foreach(KeyValuePair<string, string> kvp in skinMan.SkinConfig) { SkinData oldSkin = skinMan.GetSkin(kvp.Key); SkinData newSkin = skinMan.GetSkin(kvp.Value); bool skinsMatch = kvp.Key.Equals(kvp.Value); if (skinsMatch && settings.Check("ModsReplaceDefaultSkins", "True")) continue; //Skip default skins int j = archive.Groups[i].Entries.FindIndex(o => o.Name.Equals(oldSkin.Name + ".png", StringComparison.OrdinalIgnoreCase)); if (j == -1) continue; //Skip missing character entry Byte[] data = File.ReadAllBytes(newSkin.Path); var new_entry = new Entry(oldSkin.Name + ".png", data); //Replace entry in the archive.wad archive.Groups[i].Entries[j] = new_entry; //Msg.Log("Patching skin '" + oldSkin.Name + " to '" + newSkin.Name + "'."); if (skinsMatch == false) count++; } if (count > 0) Msg.Log("Patched " + count + " character skins"); archive.Save(); } Program.mainForm.SetProgress(66); // STEP 4 OF 4 - Copy files to Spelunky data folders. foreach (GameFile gmfile in DefaultFiles) { try { string srcfile = this.TempPath + gmfile.FilePath; string dstfile = this.GamePath + "Data/" + gmfile.FilePath; File.Copy(srcfile, dstfile, true); } catch (Exception ex) { Msg.MsgBox("Error patching file: " + ex.Message, "Patchlunky Setup"); return false; //Stop patching } } Program.mainForm.SetProgress(100); return true; }
//Extracts the original skins from the backups private void ExtractDefaultSkins() { Setup setup = Program.mainForm.Setup; if (setup.CheckBackups() == false) { Msg.Log("Failed to extract default skins."); string message = "Patchlunky is missing Spelunky backups!" + Environment.NewLine + Environment.NewLine + "Patchlunky character skins will not work without backups."; Msg.MsgBox(message, "Patchlunky"); return; //Backups are missing, can't extract. } Archive alltex = new Archive(setup.BackupPath + "Textures/alltex.wad"); alltex.Load(); var group = alltex.Groups.Find(o => o.Name.Equals("PLAYERS")); if (group.Equals(default(SpelunkyWad.Group))) return; //Group not found string dirpath = setup.BackupPath + "Characters/"; Directory.CreateDirectory(dirpath); foreach (var entry in group.Entries) { string filepath = Path.Combine(dirpath, entry.Name); File.WriteAllBytes(filepath, entry.Data); } }