public static void GetMaps() { for (int i = 0; i < RomData.SceneList.Count; i++) { int f = RomData.SceneList[i].File; RomUtils.CheckCompressed(f); int j = 0; while (true) { byte cmd = RomData.MMFileList[f].Data[j]; if (cmd == 0x04) { byte mapcount = RomData.MMFileList[f].Data[j + 1]; int mapsaddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; for (int k = 0; k < mapcount; k++) { Map m = new Map(); m.File = RomUtils.AddrToFile((int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, mapsaddr)); RomData.SceneList[i].Maps.Add(m); mapsaddr += 8; } break; } if (cmd == 0x14) { break; } j += 8; } } }
public static void InsertObj(byte[] obj, int replace) { int f = RomUtils.GetFileIndexForWriting(OBJECT_TABLE); int basea = OBJECT_TABLE - RomData.MMFileList[f].Addr; uint replaceaddr = ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, basea + (replace * 8)); int objf = RomData.MMFileList.FindIndex(u => u.Addr == replaceaddr); if (objf == -1) { return; } ; if (obj.Length > (RomData.MMFileList[objf].End - RomData.MMFileList[objf].Addr)) { MMFile newfile = new MMFile(); newfile.Addr = RomData.MMFileList[RomData.MMFileList.Count - 1].End; newfile.End = newfile.Addr + obj.Length; newfile.IsCompressed = true; newfile.WasEdited = true; newfile.Data = obj; RomData.MMFileList[objf].Cmp_Addr = -1; RomData.MMFileList[objf].Cmp_End = -1; RomData.MMFileList[objf].Data = null; RomData.MMFileList[objf].IsCompressed = false; RomData.MMFileList.Add(newfile); ReadWriteUtils.Arr_WriteU32(RomData.MMFileList[f].Data, basea + (replace * 8), (uint)newfile.Addr); ReadWriteUtils.Arr_WriteU32(RomData.MMFileList[f].Data, basea + (replace * 8) + 4, (uint)newfile.End); } else { RomData.MMFileList[objf].Data = obj; RomData.MMFileList[objf].WasEdited = true; } }
public static byte[] Decompress(byte[] CmpFile) { if (ReadWriteUtils.Arr_ReadU32(CmpFile, 0) != 0x59617A30) { return(null); } int dest_len = (int)ReadWriteUtils.Arr_ReadU32(CmpFile, 4); byte[] dest = new byte[dest_len]; int src_pos = 16; int src_len = CmpFile.Length; int dest_pos = 0; while ((src_pos < src_len) && (dest_pos < dest_len)) { byte code = CmpFile[src_pos]; src_pos++; for (int i = 0; i < 8; i++) { if ((src_pos >= src_len) || (dest_pos >= dest_len)) { break; } ; if ((code & 0x80) > 0) { dest[dest_pos] = CmpFile[src_pos]; dest_pos++; src_pos++; } else { byte b1 = CmpFile[src_pos]; src_pos++; byte b2 = CmpFile[src_pos]; src_pos++; int copy_from = dest_pos - (((b1 & 0xF) << 8) | b2) - 1; int n = b1 >> 4; if (n == 0) { n = CmpFile[src_pos] + 0x12; src_pos++; } else { n += 2; } for (int j = 0; j < n; j++) { dest[dest_pos] = dest[copy_from]; dest_pos++; copy_from++; } } code <<= 1; } } return(dest); }
public static void GetActors() { for (int i = 0; i < RomData.SceneList.Count; i++) { for (int j = 0; j < RomData.SceneList[i].Maps.Count; j++) { int f = RomData.SceneList[i].Maps[j].File; RomUtils.CheckCompressed(f); int k = RomData.SceneList[i].Maps[j].Header; while (true) { byte cmd = RomData.MMFileList[f].Data[k]; if (cmd == 0x01) { byte ActorCount = RomData.MMFileList[f].Data[k + 1]; int ActorAddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, k + 4) & 0xFFFFFF; RomData.SceneList[i].Maps[j].ActorAddr = ActorAddr; RomData.SceneList[i].Maps[j].Actors = ReadMapActors(RomData.MMFileList[f].Data, ActorAddr, ActorCount); } if (cmd == 0x0B) { byte ObjectCount = RomData.MMFileList[f].Data[k + 1]; int ObjectAddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, k + 4) & 0xFFFFFF; RomData.SceneList[i].Maps[j].ObjAddr = ObjectAddr; RomData.SceneList[i].Maps[j].Objects = ReadMapObjects(RomData.MMFileList[f].Data, ObjectAddr, ObjectCount); } if (cmd == 0x14) { break; } k += 8; } } } }
public static List <int[]> GetAddresses(string path, string name) { List <int[]> Addrs = new List <int[]>(); byte[] a; using (BinaryReader AddrFile = new BinaryReader(File.Open(Path.Combine(path, name), FileMode.Open, FileAccess.Read))) { a = new byte[AddrFile.BaseStream.Length]; AddrFile.Read(a, 0, a.Length); } int i = 0; while (a[i] != 0xFF) { int count = (int)ReadWriteUtils.Arr_ReadU32(a, i); int[] alist = new int[count]; i += 4; for (int j = 0; j < count; j++) { alist[j] = (int)ReadWriteUtils.Arr_ReadU32(a, i); i += 4; } Addrs.Add(alist); } return(Addrs); }
public static int GetObjSize(int obj) { int f = RomUtils.GetFileIndexForWriting(OBJECT_TABLE); int basea = OBJECT_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; return((int)(ReadWriteUtils.Arr_ReadU32(fileData, basea + (obj * 8) + 4) - ReadWriteUtils.Arr_ReadU32(fileData, basea + (obj * 8)))); }
public static void GetMapHeaders() { for (int i = 0; i < RomData.SceneList.Count; i++) { int maps = RomData.SceneList[i].Maps.Count; for (int j = 0; j < maps; j++) { int f = RomData.SceneList[i].Maps[j].File; RomUtils.CheckCompressed(f); int k = 0; int setupsaddr = -1; int nextlowest = -1; while (true) { byte cmd = RomData.MMFileList[f].Data[k]; if (cmd == 0x18) { setupsaddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, k + 4) & 0xFFFFFF; } else if (cmd == 0x14) { break; } else { if (RomData.MMFileList[f].Data[k + 4] == 0x03) { int p = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, k + 4) & 0xFFFFFF; if (((p < nextlowest) || (nextlowest == -1)) && ((p > setupsaddr) && (setupsaddr != -1))) { nextlowest = p; } } } k += 8; } if ((setupsaddr == -1) || (nextlowest == -1)) { continue; } for (k = setupsaddr; k < nextlowest; k += 4) { byte s = RomData.MMFileList[f].Data[k]; if (s != 0x03) { break; } int p = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, k) & 0xFFFFFF; Map m = new Map(); m.File = f; m.Header = p; RomData.SceneList[i].Maps.Add(m); } } } }
private static void CheckHeaderForExits(int f, int headeraddr, Scene scene) { int j = headeraddr; int setupsaddr = -1; int nextlowest = -1; byte s; var setup = new SceneSetup(); scene.Setups.Add(setup); while (true) { byte cmd = RomData.MMFileList[f].Data[j]; if (cmd == 0x13) { setup.ExitListAddress = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; } else if (cmd == 0x17) { setup.CutsceneListAddress = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; } else if (cmd == 0x18) { setupsaddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; } else if (cmd == 0x14) { break; } else { if (RomData.MMFileList[f].Data[j + 4] == 0x02) { int p = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; if (((p < nextlowest) || (nextlowest == -1)) && ((p > setupsaddr) && (setupsaddr != -1))) { nextlowest = p; } } } j += 8; } if ((setupsaddr != -1) && nextlowest != -1) { j = setupsaddr; s = RomData.MMFileList[f].Data[j]; while (s == 0x02) { int p = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j) & 0xFFFFFF; CheckHeaderForExits(f, p, scene); j += 4; s = RomData.MMFileList[f].Data[j]; } } }
public static void ApplyHack(string path, string name) { BinaryReader hack_file = new BinaryReader(File.Open(Path.Combine(path, name), FileMode.Open)); int hack_len = (int)hack_file.BaseStream.Length; byte[] hack_content = new byte[hack_len]; hack_file.Read(hack_content, 0, hack_len); hack_file.Close(); if (name.EndsWith("title-screen")) { Random R = new Random(); int rot = R.Next(360); Color l; float h; for (int i = 0; i < 144 * 64; i++) { int p = (i * 4) + 8; l = Color.FromArgb(hack_content[p + 3], hack_content[p], hack_content[p + 1], hack_content[p + 2]); h = l.GetHue(); h += rot; h %= 360f; l = ColorUtils.FromAHSB(l.A, h, l.GetSaturation(), l.GetBrightness()); hack_content[p] = l.R; hack_content[p + 1] = l.G; hack_content[p + 2] = l.B; hack_content[p + 3] = l.A; } l = Color.FromArgb(hack_content[0x1FE72], hack_content[0x1FE73], hack_content[0x1FE76]); h = l.GetHue(); h += rot; h %= 360f; l = ColorUtils.FromAHSB(255, h, l.GetSaturation(), l.GetBrightness()); hack_content[0x1FE72] = l.R; hack_content[0x1FE73] = l.G; hack_content[0x1FE76] = l.B; } int addr = 0; while (hack_content[addr] != 0xFF) { //Debug.WriteLine(addr.ToString("X4")); uint dest = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; uint len = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; int f = RomUtils.GetFileIndexForWriting((int)dest); dest -= (uint)RomData.MMFileList[f].Addr; ReadWriteUtils.Arr_Insert(hack_content, addr, (int)len, RomData.MMFileList[f].Data, (int)dest); addr += (int)len; } }
public static byte[] GetObjectData(int objectIndex) { var objectTableFileIndex = RomUtils.GetFileIndexForWriting(OBJECT_TABLE); var baseAddress = OBJECT_TABLE - RomData.MMFileList[objectTableFileIndex].Addr; var objectAddress = ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[objectTableFileIndex].Data, baseAddress + (objectIndex * 8)); var objectFileIndex = RomData.MMFileList.FindIndex(f => f.Addr == objectAddress); if (objectFileIndex == -1) { return(null); } RomUtils.CheckCompressed(objectFileIndex); return(RomData.MMFileList[objectFileIndex].Data); }
public static void ApplyHack(byte[] hack_content) { int addr = 0; while (hack_content[addr] != 0xFF) { //Debug.WriteLine(addr.ToString("X4")); uint dest = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; uint len = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; int f = RomUtils.GetFileIndexForWriting((int)dest); dest -= (uint)RomData.MMFileList[f].Addr; ReadWriteUtils.Arr_Insert(hack_content, addr, (int)len, RomData.MMFileList[f].Data, (int)dest); addr += (int)len; } }
public static List <int[]> GetAddresses(byte[] addresses_content) { List <int[]> Addrs = new List <int[]>(); int i = 0; while (addresses_content[i] != 0xFF) { int count = (int)ReadWriteUtils.Arr_ReadU32(addresses_content, i); int[] alist = new int[count]; i += 4; for (int j = 0; j < count; j++) { alist[j] = (int)ReadWriteUtils.Arr_ReadU32(addresses_content, i); i += 4; } Addrs.Add(alist); } return(Addrs); }
public static void ApplyHack_File(string name, byte[] data) { BinaryReader hack_file = new BinaryReader(File.Open(name, FileMode.Open)); int hack_len = (int)hack_file.BaseStream.Length; byte[] hack_content = new byte[hack_len]; hack_file.Read(hack_content, 0, hack_len); hack_file.Close(); int addr = 0; while (hack_content[addr] != 0xFF) { //Debug.WriteLine(addr.ToString("X4")); uint dest = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; uint len = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; ReadWriteUtils.Arr_Insert(hack_content, addr, (int)len, data, (int)dest); addr += (int)len; } }
public static void GetMaps() { foreach (var scene in RomData.SceneList) { int f = scene.File; RomUtils.CheckCompressed(f); int j = 0; while (true) { byte cmd = RomData.MMFileList[f].Data[j]; if (cmd == 0x04) { byte mapcount = RomData.MMFileList[f].Data[j + 1]; int mapsaddr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, j + 4) & 0xFFFFFF; for (int k = 0; k < mapcount; k++) { Map m = new Map(); m.File = RomUtils.AddrToFile((int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, mapsaddr)); scene.Maps.Add(m); mapsaddr += 8; } break; } if (cmd == 0x14) { break; } j += 8; } CheckHeaderForExits(f, 0, scene); if (scene.Number == 108) // avoid modifying unused setup in East Clock Town. doesn't seem to actually affect anything in-game, but best not to touch it. { scene.Setups.RemoveAt(2); } } }
private static void FixCRC(byte[] ROM) { // reference: http://n64dev.org/n64crc.html uint[] CRC = new uint[2]; uint seed = 0xDF26F436; uint t1, t2, t3, t4, t5, t6, r, d; int i = 0x1000; t1 = t2 = t3 = t4 = t5 = t6 = seed; while (i < 0x101000) { d = ReadWriteUtils.Arr_ReadU32(ROM, i); if ((t6 + d) < t6) { t4++; } t6 += d; t3 ^= d; r = (d << (byte)(d & 0x1F)) | (d >> (byte)(32 - (d & 0x1F))); t5 += r; if (t2 < d) { t2 ^= (t6 ^ d); } else { t2 ^= r; } t1 += (ReadWriteUtils.Arr_ReadU32(ROM, 0x750 + (i & 0xFF)) ^ d); i += 4; } CRC[0] = t6 ^ t4 ^ t3; CRC[1] = t5 ^ t2 ^ t1; ReadWriteUtils.Arr_WriteU32(ROM, 16, CRC[0]); ReadWriteUtils.Arr_WriteU32(ROM, 20, CRC[1]); }
public static void ReadSceneTable() { RomData.SceneList = new List <Scene>(); int f = RomUtils.GetFileIndexForWriting(SCENE_TABLE); int _SceneTable = SCENE_TABLE - RomData.MMFileList[f].Addr; int i = 0; while (true) { Scene s = new Scene(); uint saddr = ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, _SceneTable + i); if (saddr > 0x4000000) { break; } if (saddr != 0) { s.File = RomUtils.AddrToFile((int)saddr); s.Number = i >> 4; RomData.SceneList.Add(s); } i += 16; } }
// gets passed RomData.SequenceList in Builder.cs::WriteAudioSeq public static void RebuildAudioSeq(List <SequenceInfo> SequenceList, OutputSettings _settings) { // spoiler log output DEBUG StringBuilder log = new StringBuilder(); void WriteOutput(string str) { Debug.WriteLine(str); // we still want debug output though log.AppendLine(str); } List <MMSequence> OldSeq = new List <MMSequence>(); int f = RomUtils.GetFileIndexForWriting(Addresses.SeqTable); int basea = RomData.MMFileList[f].Addr; for (int i = 0; i < 128; i++) { MMSequence entry = new MMSequence(); if (i == 0x1E) // intro music when link gets ambushed { entry.Addr = 2; OldSeq.Add(entry); continue; } int entryaddr = Addresses.SeqTable + (i * 16); entry.Addr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, entryaddr - basea); var size = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, (entryaddr - basea) + 4); if (size > 0) { entry.Data = new byte[size]; Array.Copy(RomData.MMFileList[4].Data, entry.Addr, entry.Data, 0, entry.Size); } else { int j = SequenceList.FindIndex(u => u.Replaces == i); if (j != -1) { if ((entry.Addr > 0) && (entry.Addr < 128)) { if (SequenceList[j].Replaces != 0x28) // 28 (fairy fountain) { SequenceList[j].Replaces = entry.Addr; } else { entry.Data = OldSeq[0x18].Data; } } } } OldSeq.Add(entry); } List <MMSequence> NewSeq = new List <MMSequence>(); int addr = 0; byte[] NewAudioSeq = new byte[0]; for (int i = 0; i < 128; i++) { MMSequence newentry = new MMSequence(); if (OldSeq[i].Size == 0) { newentry.Addr = OldSeq[i].Addr; } else { newentry.Addr = addr; } if (SequenceList.FindAll(u => u.Replaces == i).Count > 1) { WriteOutput("Error: Slot " + i.ToString("X") + " has multiple songs pointing at it!"); } int p = RomData.PointerizedSequences.FindIndex(u => u.PreviousSlot == i); int j = SequenceList.FindIndex(u => u.Replaces == i); if (p != -1) { // found song we want to pointerize newentry.Addr = RomData.PointerizedSequences[p].Replaces; } else if (j != -1) { // new song to replace old slot found if (SequenceList[j].MM_seq != -1) { newentry.Data = OldSeq[SequenceList[j].MM_seq].Data; WriteOutput("Slot " + i.ToString("X2") + " -> " + SequenceList[j].Name); } else if (SequenceList[j].SequenceBinaryList != null && SequenceList[j].SequenceBinaryList.Count > 0) { if (SequenceList[j].SequenceBinaryList.Count > 1) { WriteOutput("Warning: writing song with multiple sequence/bank combos, selecting first available"); } newentry.Data = SequenceList[j].SequenceBinaryList[0].SequenceBinary; WriteOutput("Slot " + i.ToString("X2") + " := " + SequenceList[j].Name + " *"); } else // non mm, load file and add { byte[] data; if (File.Exists(SequenceList[j].Filename)) { using (var reader = new BinaryReader(File.OpenRead(SequenceList[j].Filename))) { data = new byte[(int)reader.BaseStream.Length]; reader.Read(data, 0, data.Length); } } else if (SequenceList[j].Name == nameof(Properties.Resources.mmr_f_sot)) { data = Properties.Resources.mmr_f_sot; } else { throw new Exception("Music not found as file or built-in resource." + SequenceList[j].Filename); } // I think this checks if the sequence type is correct for MM // because DB ripped sequences from SF64/SM64/MK64 without modifying them if (data[1] != 0x20) { data[1] = 0x20; } newentry.Data = data; WriteOutput("Slot " + i.ToString("X2") + " := " + SequenceList[j].Name); } } else // not found, song wasn't touched by rando, just transfer over { newentry.Data = OldSeq[i].Data; } // if the sequence is not padded to 16 bytes, the DMA fails // music can stop from playing and on hardware it will just straight crash var Padding = 0x10 - newentry.Size % 0x10; if (Padding != 0x10) { newentry.Data = newentry.Data.Concat(new byte[Padding]).ToArray(); } NewSeq.Add(newentry); // TODO is there not a better way to write this? if (newentry.Data != null) { NewAudioSeq = NewAudioSeq.Concat(newentry.Data).ToArray(); } addr += newentry.Size; } // discovered when MM-only music was fixed, if the audioseq is left in it's old spot // audio quality is garbage, sounds like static //if (addr > (RomData.MMFileList[4].End - RomData.MMFileList[4].Addr)) //else //RomData.MMFileList[4].Data = NewAudioSeq; int index = RomUtils.AppendFile(NewAudioSeq); ResourceUtils.ApplyHack(Values.ModsDirectory, "reloc-audio"); RelocateSeq(index); RomData.MMFileList[4].Data = new byte[0]; RomData.MMFileList[4].Cmp_Addr = -1; RomData.MMFileList[4].Cmp_End = -1; //update sequence index pointer table f = RomUtils.GetFileIndexForWriting(Addresses.SeqTable); for (int i = 0; i < 128; i++) { ReadWriteUtils.Arr_WriteU32(RomData.MMFileList[f].Data, (Addresses.SeqTable + (i * 16)) - basea, (uint)NewSeq[i].Addr); ReadWriteUtils.Arr_WriteU32(RomData.MMFileList[f].Data, 4 + (Addresses.SeqTable + (i * 16)) - basea, (uint)NewSeq[i].Size); } //update inst sets used by each new seq // this is NOT the audiobank, its the complementary instrument set value for each sequence // IE, sequence 7 uses instrument set "10", we replaced it with sequnece ae which needs bank "23" f = RomUtils.GetFileIndexForWriting(Addresses.InstSetMap); basea = RomData.MMFileList[f].Addr; for (int i = 0; i < 128; i++) { // huh? paddr? pointer? padding? int paddr = (Addresses.InstSetMap - basea) + (i * 2) + 2; int j = -1; if (NewSeq[i].Size == 0) // pointer, we need to copy the instrumnet set from the destination { j = SequenceList.FindIndex(u => u.Replaces == NewSeq[i].Addr); } else { j = SequenceList.FindIndex(u => u.Replaces == i); } if (j != -1) { RomData.MMFileList[f].Data[paddr] = (byte)SequenceList[j].Instrument; } } //// DEBUG spoiler log output //String dir = Path.GetDirectoryName(_settings.OutputROMFilename); //String path = $"{Path.GetFileNameWithoutExtension(_settings.OutputROMFilename)}"; //// spoiler log should already be written by the time we reach this far //if (File.Exists(Path.Combine(dir, path + "_SpoilerLog.txt"))) // path += "_SpoilerLog.txt"; //else // TODO add HTML log compatibility // path += "_SongLog.txt"; //using (StreamWriter sw = new StreamWriter(Path.Combine(dir, path), append: true)) //{ // sw.WriteLine(""); // spacer // sw.Write(log); //} }