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 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 void ReenableNightBGM() { // summary: there is a scene header which has a single byte that determines what plays at night, setting to 13 re-enables BGM at night // since scene table is only read previously on enemizer, if not loaded we have to load now if (RomData.SceneList == null) { ReadSceneTable(); } // TODO since this is static, it can be moved var TargetSceneEnums = new GameObjects.Scene[] { GameObjects.Scene.TerminaField, GameObjects.Scene.RoadToSouthernSwamp, GameObjects.Scene.SouthernSwamp, GameObjects.Scene.SouthernSwampClear, GameObjects.Scene.PathToMountainVillage, GameObjects.Scene.MountainVillage, GameObjects.Scene.MountainVillageSpring, GameObjects.Scene.TwinIslands, GameObjects.Scene.TwinIslandsSpring, GameObjects.Scene.GoronRacetrack, GameObjects.Scene.GoronVillage, GameObjects.Scene.GoronVillageSpring, GameObjects.Scene.PathToSnowhead, GameObjects.Scene.Snowhead, GameObjects.Scene.MilkRoad, GameObjects.Scene.GreatBayCoast, GameObjects.Scene.PinnacleRock, GameObjects.Scene.ZoraCape, GameObjects.Scene.WaterfallRapids, GameObjects.Scene.RoadToIkana, GameObjects.Scene.IkanaCanyon, GameObjects.Scene.EastClockTown, GameObjects.Scene.WestClockTown, GameObjects.Scene.NorthClockTown, GameObjects.Scene.SouthClockTown, GameObjects.Scene.LaundryPool, GameObjects.Scene.Woodfall, }.ToList(); foreach (var SceneEnum in TargetSceneEnums) { ReenableNightBGMSingle(RomData.SceneList.Find(u => u.Number == SceneEnum.Id()).File); } // Kamaro the dancing ghost in Termina Field breaks night music // he calls a function that sets an unknown actor flag unk39 & 20, he calls this function per frame from multiple places // if we nop it his music never plays, and might music is never interupted by him var kamaroFID = 593; //GameObjects.Actor.En_Yb.FileListIndex(); RomUtils.CheckCompressed(kamaroFID); var kamaroData = RomData.MMFileList[kamaroFID].Data; // null function call to func_800B9084 -> NOP ReadWriteUtils.Arr_WriteU32(kamaroData, 0x618, 0x00000000); }
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); } } } }
public static void ReenableNightBGMSingle(int SceneFileID, byte NewMusicByte = 0x13) { // search for the bgm music header in the scene headers and replace the night sfx with a value that plays day BGM RomUtils.CheckCompressed(SceneFileID); for (int Byte = 0; Byte < 0x10 * 70; Byte += 8) { if (RomData.MMFileList[SceneFileID].Data[Byte] == 0x15) // header command starts with 0x15 { RomData.MMFileList[SceneFileID].Data[Byte + 0x6] = NewMusicByte; // 6th/8 byte is night BGM behavior, 0x13 is daytime BGM return; } } }
public static void ReassignSkulltulaHousesMusic(byte replacement_slot = 0x75) { // changes the skulltulla house BGM to a separate slot so it plays a new music that isn't generic cave music (overused) // the BGM for a scene is specified by a single byte in the scene headers // to modify the scene header, which is in the scene, we need the scene as a file // we can get this from the Romdata.SceneList but this only gets populated on enemizer // and we don't NEED to populate it since vanilla scenes are static, we can just hard code it here // at re-encode, we'll have fewer decoded files to re-encode too int swamp_spider_house_fid = 1284; // taken from ultimate MM spreadsheet (US File list -> A column) // scan the files for the header that contains scene music (0x15 first byte) // 15xx0000 0000yyzz where zz is the sequence pointer byte RomUtils.CheckCompressed(swamp_spider_house_fid); for (int b = 0; b < 0x10 * 70; b += 8) { if (RomData.MMFileList[swamp_spider_house_fid].Data[b] == 0x15 && RomData.MMFileList[swamp_spider_house_fid].Data[b + 0x7] == 0x3B) { RomData.MMFileList[swamp_spider_house_fid].Data[b + 0x7] = replacement_slot; break; } } int ocean_spider_house_fid = 1291; // taken from ultimate MM spreadsheet RomUtils.CheckCompressed(ocean_spider_house_fid); for (int b = 0; b < 0x10 * 70; b += 8) { if (RomData.MMFileList[ocean_spider_house_fid].Data[b] == 0x15 && RomData.MMFileList[ocean_spider_house_fid].Data[b + 0x7] == 0x3B) { RomData.MMFileList[ocean_spider_house_fid].Data[b + 0x7] = replacement_slot; break; } } SequenceInfo new_music_slot = new SequenceInfo { Name = "mm-spiderhouse-replacement", MM_seq = replacement_slot, Replaces = replacement_slot, Type = new List <int> { 2 }, Instrument = 3 }; RomData.TargetSequences.Add(new_music_slot); }
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); }
/// <summary> /// Get image data for the 4 stray fairy icons. /// </summary> /// <returns>Array of image bytes.</returns> public static byte[][] GetStrayFairyIcons() { // Stray Fairy icons start at: 0xA0A000 + 0x1B80 RomUtils.CheckCompressed(11); var fileData = RomData.MMFileList[11].Data; // Extract HUD icon for Stray Fairy icons. var fairies = new List <byte[]>(4); for (int i = 0; i < 4; i++) { var imageData = new byte[0xC00]; var offset = 0x1B80 + (imageData.Length * i); Buffer.BlockCopy(fileData, offset, imageData, 0, imageData.Length); fairies.Add(imageData); } return(fairies.ToArray()); }
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); } } }
/// <summary> /// Get the <see cref="MMFile"/> at a <see cref="FileIndex"/>. /// </summary> /// <param name="index">File index.</param> /// <returns></returns> static MMFile GetFile(FileIndex index) { RomUtils.CheckCompressed((int)index); return(RomData.MMFileList[(int)index]); }