// 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); //} }
public static void RebuildAudioSeq(List <SequenceInfo> SequenceList) { 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) { entry.Addr = 2; entry.Size = 0; OldSeq.Add(entry); continue; } int entryaddr = Addresses.SeqTable + (i * 16); entry.Addr = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, entryaddr - basea); entry.Size = (int)ReadWriteUtils.Arr_ReadU32(RomData.MMFileList[f].Data, (entryaddr - basea) + 4); if (entry.Size > 0) { entry.Data = new byte[entry.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) { SequenceList[j].Replaces = entry.Addr; } else { entry.Data = OldSeq[0x18].Data; entry.Size = OldSeq[0x18].Size; } } } } 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; } int j = SequenceList.FindIndex(u => u.Replaces == i); if (j != -1) { if (SequenceList[j].MM_seq != -1) { newentry.Size = OldSeq[SequenceList[j].MM_seq].Size; newentry.Data = OldSeq[SequenceList[j].MM_seq].Data; } else { BinaryReader sequence = new BinaryReader(File.Open(SequenceList[j].Name, FileMode.Open)); int len = (int)sequence.BaseStream.Length; byte[] data = new byte[len]; sequence.Read(data, 0, len); sequence.Close(); if (data[1] != 0x20) { data[1] = 0x20; } newentry.Size = len; newentry.Data = data; } } else { newentry.Size = OldSeq[i].Size; newentry.Data = OldSeq[i].Data; } NewSeq.Add(newentry); if (newentry.Data != null) { NewAudioSeq = NewAudioSeq.Concat(newentry.Data).ToArray(); } addr += newentry.Size; } if (addr > (RomData.MMFileList[4].End - RomData.MMFileList[4].Addr)) { MMFile newa = new MMFile(); newa.Addr = RomData.MMFileList[RomData.MMFileList.Count - 1].End; newa.End = newa.Addr + addr; newa.IsCompressed = false; newa.Data = NewAudioSeq; RomData.MMFileList.Add(newa); ResourceUtils.ApplyHack(Values.ModsDirectory + "reloc-audio"); RelocateSeq(RomData.MMFileList.Count - 1); RomData.MMFileList[4].Data = new byte[0]; RomData.MMFileList[4].Cmp_Addr = -1; RomData.MMFileList[4].Cmp_End = -1; } else { RomData.MMFileList[4].Data = NewAudioSeq; } //update 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 f = RomUtils.GetFileIndexForWriting(Addresses.InstSetMap); basea = RomData.MMFileList[f].Addr; for (int i = 0; i < 128; i++) { int paddr = (Addresses.InstSetMap - basea) + (i * 2) + 2; int j = -1; if (NewSeq[i].Size == 0) { 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; } } }