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); } }
// 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; }
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); } }
/// <summary> /// Loads this WAD archive from file. /// </summary> public void Load() { //context var groups = new List<Group>(); Group group = null; using (FileStream wadStream = File.Open(this.WadFile, FileMode.Open)) using (StreamReader wixStreamReader = new StreamReader(File.Open(this.WixFile, FileMode.Open))) { while (!wixStreamReader.EndOfStream) { //read var line = wixStreamReader.ReadLine(); var parts = line.Split(' '); var identifier = parts[0]; switch (identifier) { case "!group": { if (group != null) { //add groups.Add(group); } //group var name = parts[1]; //create group = new Group(name); break; } default: { //entry var name = parts[0]; var offset = int.Parse(parts[1]); var length = int.Parse(parts[2]); //data var data = new byte[length]; wadStream.Seek(offset, SeekOrigin.Begin); wadStream.Read(data, 0, length); //create var entry = new Entry(name, data); group.Entries.Add(entry); break; } } } if (group != null) { //add last groups.Add(group); } } //set this.Groups = groups; }