using System; using System.Collections.Generic; using System.Linq; using System.Text; using LibEveryFileExplorer.IO; using System.IO; namespace NDS.Nitro { public class ARM9 { private UInt32 RamAddress; private byte[] StaticData; private UInt32 _start_ModuleParamsOffset; private CRT0.ModuleParams _start_ModuleParams; private List AutoLoadList; public ARM9(byte[] Data, UInt32 RamAddress) : this(Data, RamAddress, FindModuleParams(Data)) { } public ARM9(byte[] Data, UInt32 RamAddress, UInt32 _start_ModuleParamsOffset) { //Unimportant static footer! Use it for _start_ModuleParamsOffset and remove it. if (IOUtil.ReadU32LE(Data, Data.Length - 12) == 0xDEC00621) { _start_ModuleParamsOffset = IOUtil.ReadU32LE(Data, Data.Length - 8); byte[] data_tmp = new byte[Data.Length - 12]; Array.Copy(Data, data_tmp, Data.Length - 12); Data = data_tmp; } this.RamAddress = RamAddress; this._start_ModuleParamsOffset = _start_ModuleParamsOffset; _start_ModuleParams = new CRT0.ModuleParams(Data, _start_ModuleParamsOffset); if (_start_ModuleParams.CompressedStaticEnd != 0) { Data = Decompress(Data, _start_ModuleParamsOffset); _start_ModuleParams = new CRT0.ModuleParams(Data, _start_ModuleParamsOffset); } StaticData = new byte[_start_ModuleParams.AutoLoadStart - RamAddress]; Array.Copy(Data, StaticData, _start_ModuleParams.AutoLoadStart - RamAddress); AutoLoadList = new List(); uint nr = (_start_ModuleParams.AutoLoadListEnd - _start_ModuleParams.AutoLoadListOffset) / 0xC; uint Offset = _start_ModuleParams.AutoLoadStart - RamAddress; for (int i = 0; i < nr; i++) { var entry = new CRT0.AutoLoadEntry(Data, _start_ModuleParams.AutoLoadListOffset - RamAddress + (uint)i * 0xC); entry.Data = new byte[entry.Size]; Array.Copy(Data, Offset, entry.Data, 0, entry.Size); AutoLoadList.Add(entry); Offset += entry.Size; } } public byte[] Write() { MemoryStream m = new MemoryStream(); EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian); er.Write(StaticData, 0, StaticData.Length); _start_ModuleParams.AutoLoadStart = (uint)er.BaseStream.Position + RamAddress; foreach (var v in AutoLoadList) er.Write(v.Data, 0, v.Data.Length); _start_ModuleParams.AutoLoadListOffset = (uint)er.BaseStream.Position + RamAddress; foreach (var v in AutoLoadList) v.Write(er); _start_ModuleParams.AutoLoadListEnd = (uint)er.BaseStream.Position + RamAddress; long curpos = er.BaseStream.Position; er.BaseStream.Position = _start_ModuleParamsOffset; _start_ModuleParams.Write(er); er.BaseStream.Position = curpos; byte[] data = m.ToArray(); er.Close(); return data; } public void AddAutoLoadEntry(UInt32 Address, byte[] Data) { AutoLoadList.Add(new CRT0.AutoLoadEntry(Address, Data)); } public bool WriteU16LE(UInt32 Address, UInt16 Value) { if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart) { IOUtil.WriteU16LE(StaticData, (int)(Address - RamAddress), Value); return true; } foreach (var v in AutoLoadList) { if (Address > v.Address && Address < (v.Address + v.Size)) { IOUtil.WriteU16LE(v.Data, (int)(Address - v.Address), Value); return true; } } return false; } public UInt32 ReadU32LE(UInt32 Address) { if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart) { return IOUtil.ReadU32LE(StaticData, (int)(Address - RamAddress)); } foreach (var v in AutoLoadList) { if (Address > v.Address && Address < (v.Address + v.Size)) { return IOUtil.ReadU32LE(v.Data, (int)(Address - v.Address)); } } return 0xFFFFFFFF; } public bool WriteU32LE(UInt32 Address, UInt32 Value) { if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart) { IOUtil.WriteU32LE(StaticData, (int)(Address - RamAddress), Value); return true; } foreach (var v in AutoLoadList) { if (Address > v.Address && Address < (v.Address + v.Size)) { IOUtil.WriteU32LE(v.Data, (int)(Address - v.Address), Value); return true; } } return false; } public static byte[] Decompress(byte[] Data) { uint offset = FindModuleParams(Data); if (offset == 0xffffffe3) return Data;//no moduleparams, so it must be uncompressed return Decompress(Data, offset); } public static byte[] Decompress(byte[] Data, UInt32 _start_ModuleParamsOffset) { if (IOUtil.ReadU32LE(Data, (int)_start_ModuleParamsOffset + 0x14) == 0) return Data;//Not Compressed! byte[] Result = CRT0.MIi_UncompressBackward(Data); IOUtil.WriteU32LE(Result, (int)_start_ModuleParamsOffset + 0x14, 0); return Result; } private static uint FindModuleParams(byte[] Data) { return (uint)IndexOf(Data, new byte[] { 0x21, 0x06, 0xC0, 0xDE, 0xDE, 0xC0, 0x06, 0x21 }) - 0x1C; } private static unsafe long IndexOf(byte[] Data, byte[] Search) { fixed (byte* H = Data) fixed (byte* N = Search) { long i = 0; for (byte* hNext = H, hEnd = H + Data.LongLength; hNext < hEnd; i++, hNext++) { bool Found = true; for (byte* hInc = hNext, nInc = N, nEnd = N + Search.LongLength; Found && nInc < nEnd; Found = *nInc == *hInc, nInc++, hInc++) ; if (Found) return i; } return -1; } } } }