// get the real offset of the data (without load address) int GetTrueOffset(QueuedFile file) { if (file.Settings.HasLoad || file.Settings.AddLoad) { return(file.Offset + 2); } return(file.Offset); }
// get the real length of the data (without load address) int GetTrueLength(QueuedFile file) { if (file.Settings.HasLoad || file.Settings.AddLoad) { return(file.Data.Length - 2); } return(file.Data.Length); }
// files write only in the empty space between patches public void WriteFile(FileSettings file, byte[] data) { QueuedFile qf = new QueuedFile(); qf.Checksum = Checksum(data); qf.Data = data; qf.Dir = false; qf.NoOverwrite = true; qf.Offset = GetOffset(file.Offset); qf.Settings = file; //if (qf.Offset < 0) // qf.Offset = 0; queue.Add(qf); }
// build directories separately public void WriteDir(FileSettings file) { QueuedFile qf = new QueuedFile(); qf.Data = new byte[256]; qf.Dir = true; qf.NoOverwrite = false; qf.Offset = GetOffset(file.Offset); qf.Settings = file; if (qf.Offset < 0) { qf.Offset = 0; } queue.Add(qf); }
// apply all queued files public void Update() { Encoding enc = Encoding.GetEncoding(437); List <QueuedFile> patches = new List <QueuedFile>(); List <QueuedFile> files = new List <QueuedFile>(); List <QueuedFile> dirs = new List <QueuedFile>(); List <QueuedFile> unsortedFiles = new List <QueuedFile>(); Dictionary <int, QueuedFile> fileHashTable = new Dictionary <int, QueuedFile>(); // separate patches from files and dirs foreach (QueuedFile file in queue) { if (file.NoOverwrite) { files.Add(file); } else if (file.Dir) { dirs.Add(file); } else { patches.Add(file); } } unsortedFiles.AddRange(files.ToArray()); // create directory map int fileCount = files.Count; int[] fileMap = new int[fileCount]; for (int i = 0; i < fileCount; i++) { fileMap[i] = i; } // sort files by size (decreasing) while (true) { bool finished = true; for (int i = 1; i < fileCount; i++) { if (files[i].Data.Length > files[i - 1].Data.Length) { QueuedFile temp = files[i]; files[i] = files[i - 1]; files[i - 1] = temp; int tempint = fileMap[i]; fileMap[i] = fileMap[i - 1]; fileMap[i - 1] = tempint; finished = false; } } if (finished) { break; } } // check for duplicates for (int i = 0; i < fileCount; i++) { QueuedFile file = files[i]; if (!file.Dir) { if (!fileHashTable.ContainsKey(file.Checksum)) { fileHashTable.Add(file.Checksum, file); } else { files[i] = fileHashTable[file.Checksum]; } } } // apply patches foreach (QueuedFile file in patches) { WriteRegion(file.Settings, file.Data, file.Offset, false); } // blank dirs are written the first time so files can navigate around them foreach (QueuedFile file in dirs) { if (file.Settings.Source.Contains(":") && file.Settings.Source.Split(':')[0].ToLowerInvariant() == "easyfs") { file.Data = new byte[0x18 * (fileCount + 1)]; } WriteRegion(file.Settings, file.Data, file.Offset, false); } // apply files List <QueuedFile> addedFiles = new List <QueuedFile>(); for (int i = 0; i < fileCount; i++) { if (fileMap[i] == 70) { } QueuedFile file = files[i]; if (!addedFiles.Contains(file)) { file.Offset = FitRegion(file.Offset, file.Data.Length, file.Settings.Lorom, file.Settings.Hirom); WriteRegion(file.Settings, file.Data, file.Offset, true); addedFiles.Add(file); } else { unsortedFiles[fileMap[i]].Offset = file.Offset; } } // process and write dirs now that we know where the files are foreach (QueuedFile file in dirs) { string dirKey; string dirName; if (file.Settings.Source.Contains(":")) { var keySections = file.Settings.Source.Split(':'); dirKey = keySections[0]; dirName = keySections[keySections.Length - 1].ToLowerInvariant(); fileCount = 0; foreach (QueuedFile f in files) { if (f.Settings.Section.ToLowerInvariant() == dirName) { fileCount++; } } } else { dirKey = file.Settings.Source; dirName = ""; fileCount = files.Count; } for (int i = 0; i < file.Data.Length; i++) { file.Data[i] = (byte)(settings.Fill & 0xFF); } int idx = 0; switch (dirKey.ToLowerInvariant()) { case "bankhigh": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)((GetTrueOffset(f) / settings.BankSize) >> 8); idx++; } } break; case "banklow": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)((GetTrueOffset(f) / settings.BankSize) & 0xFF); idx++; } } break; case "easyfs": for (idx = 0; idx < fileCount; idx++) { QueuedFile currentFile = unsortedFiles[idx]; byte[] byteName = enc.GetBytes(currentFile.Settings.Target); int offset = idx * 0x18; Array.Resize(ref byteName, 0x10); Array.Copy(byteName, 0, file.Data, offset + 0, 16); if (!currentFile.Settings.Lorom) { file.Data[offset + 16] = 0x03; } else if (!currentFile.Settings.Hirom) { file.Data[offset + 16] = 0x02; } else { file.Data[offset + 16] = 0x01; } if (currentFile.Settings.Hidden) { file.Data[offset + 16] |= 0x80; } file.Data[offset + 16] |= 0x60; file.Data[offset + 17] = (byte)(currentFile.Offset / settings.BankSize); file.Data[offset + 18] = 0; file.Data[offset + 19] = (byte)(currentFile.Offset & 0xFF); file.Data[offset + 20] = (byte)((currentFile.Offset & 0x3F00) >> 8); file.Data[offset + 21] = (byte)((currentFile.Data.Length) & 0xFF); file.Data[offset + 22] = (byte)((currentFile.Data.Length >> 8) & 0xFF); file.Data[offset + 23] = (byte)((currentFile.Data.Length >> 16) & 0xFF); } break; case "lengthhigh": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { int offset = GetTrueLength(f); if (settings.LengthTC) { offset = (offset ^ 0xFFFF) + 1; } file.Data[idx] = (byte)((offset >> 8) & 0xFF); idx++; } } break; case "lengthlow": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { int offset = GetTrueLength(f); if (settings.LengthTC) { offset = (offset ^ 0xFFFF) + 1; } file.Data[idx] = (byte)(offset & 0xFF); idx++; } } break; case "loadhigh": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)((f.Settings.Load >> 8) & 0xFF); idx++; } } break; case "loadlow": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)((f.Settings.Load) & 0xFF); idx++; } } break; case "offsethigh": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)(((GetTrueOffset(f) >> 8) & 0x3F) | 0x80); idx++; } } break; case "offsetlow": foreach (QueuedFile f in unsortedFiles) { if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { file.Data[idx] = (byte)(GetTrueOffset(f) & 0xFF); idx++; } } break; case "name": if (file.Settings.Source.Split(':').Length > 2) { int nameIndex = Convert.ToInt32(file.Settings.Source.Split(':')[1]); foreach (QueuedFile f in unsortedFiles) { byte[] byteName = enc.GetBytes(f.Settings.Target); if (dirName == "" || dirName == f.Settings.Section.ToLowerInvariant()) { if (nameIndex >= byteName.Length) { file.Data[idx] = 0; } else { file.Data[idx] = byteName[nameIndex]; } idx++; } } } break; default: Debug.Assert(false, "Unknown directory type"); break; } WriteRegion(file.Settings, file.Data, file.Offset, false); } queue.Clear(); }