private byte[] GetFile(NitroFile sdat, uint fileID) { uint fatOffset = sdat.Read32(0x20); return(sdat.ReadBlock(sdat.Read32(fatOffset + 12 + 16 * fileID), sdat.Read32(fatOffset + 16 + 16 * fileID))); }
public SDAT_File(string name, NitroFile file, uint fileID) { m_Name = name; uint fatOffset = file.Read32(0x20); m_Data = new INitroROMBlock(); m_Data.m_Data = file.ReadBlock(file.Read32(fatOffset + 12 + 16 * fileID), file.Read32(fatOffset + 16 + 16 * fileID)); }
public void loadSections() { if (isCompressed) { throw new Exception("Can't load sections of compressed arm9"); } sections = new List <Arm9BinSection>(); uint copyTableBegin = (uint)(f.Read32(getCodeSettingsOffs() + 0x00) - Program.m_ROM.ARM9RAMAddress); int copyTableEnd = (int)(f.Read32(getCodeSettingsOffs() + 0x04) - Program.m_ROM.ARM9RAMAddress); int dataBegin = (int)(f.Read32(getCodeSettingsOffs() + 0x08) - Program.m_ROM.ARM9RAMAddress); newSection((int)Program.m_ROM.ARM9RAMAddress, dataBegin, 0x0, 0); sections[0].real = false; while (copyTableBegin < copyTableEnd) { int start = (int)f.Read32(copyTableBegin); copyTableBegin += 4; int size = (int)f.Read32(copyTableBegin); copyTableBegin += 4; int bsssize = (int)f.Read32(copyTableBegin); copyTableBegin += 4; newSection(start, size, dataBegin, bsssize); dataBegin += size; } }
public BTP(NitroFile file, BMD model) { this.m_File = file; this.m_FileName = file.m_Name; this.m_Model = model; m_NumFrames = m_File.Read16(0x00); m_NumTextures = m_File.Read16(0x02); m_TextureHeadersOffset = m_File.Read32(0x04); m_TextureNames = new List<string>(); m_NumPalettes = m_File.Read16(0x08); m_PaletteHeadersOffset = m_File.Read32(0x0C); m_PaletteNames = new List<string>(); m_FrameChangesOffset = m_File.Read32(0x10); m_FrameTextureIDsOffset = m_File.Read32(0x14); m_FramePaletteIDsOffset = m_File.Read32(0x18); m_NumMaterials = m_File.Read16(0x1C); m_MaterialHeadersOffset = m_File.Read32(0x20); m_MaterialNames = new List<string>(); for (int i = 0; i < m_NumTextures; i++) { m_TextureNames.Add(m_File.ReadString(m_File.Read32(m_TextureHeadersOffset + 0x04 + (uint)(i * 8)), 0)); } for (int i = 0; i < m_NumPalettes; i++) { m_PaletteNames.Add(m_File.ReadString(m_File.Read32(m_PaletteHeadersOffset + 0x04 + (uint)(i * 8)), 0)); } // Read in the frames for each material m_MaterialData = new Dictionary<string, BTPMaterialData>(); m_Frames = new List<BTPFrameData>(); for (int i = 0; i < m_NumMaterials; i++) { m_MaterialNames.Add(m_File.ReadString(m_File.Read32(m_MaterialHeadersOffset + 0x04 + (uint)(i * 12)), 0)); ushort matFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x08 + (uint)(i * 12)); ushort startOffsetFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x0A + (uint)(i * 12)); int matNumFrames = 0; for (int j = 0; j < matFrameChanges; j++) { ushort frameNum = m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + (j * 2))); // If not the last frame in the sequence, length = (next frame number - current frame number) else, // length = (number of frames in sequence - current frame number) int nextFrameNum = (j != (matFrameChanges - 1)) ? (int)m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + ((j + 1) * 2))) : -1; int length = (nextFrameNum != -1) ? (nextFrameNum - frameNum) : (m_NumFrames - frameNum); ushort texID = m_File.Read16((uint)(m_FrameTextureIDsOffset + (startOffsetFrameChanges * 2) + (j * 2))); ushort palID = m_File.Read16((uint)(m_FramePaletteIDsOffset + (startOffsetFrameChanges * 2) + (j * 2))); AddFrame(matNumFrames, m_Frames.Count, texID, palID, length); matNumFrames += length; } BTPMaterialData matData = new BTPMaterialData(m_MaterialNames[i], matFrameChanges, startOffsetFrameChanges, matNumFrames); m_MaterialData.Add(m_MaterialNames[i], matData); } }
//the last 2 record types don't exist in SM64DS. public SDAT(NitroFile file) { m_File = file; m_Filename = file.m_Name; uint symbolOffset = file.Read32(0x10); uint infoOffset = file.Read32(0x18); INitroROMBlock symbolBlock = new INitroROMBlock(file.ReadBlock(file.Read32(0x10), file.Read32(0x14))); INitroROMBlock infoBlock = new INitroROMBlock(file.ReadBlock(file.Read32(0x18), file.Read32(0x1c))); INitroROMBlock fatBlock = new INitroROMBlock(file.ReadBlock(file.Read32(0x20), file.Read32(0x24))); fatBlock.m_Data = fatBlock.ReadBlock(0x0c, fatBlock.Read32(0x08) * 0x10); //strip the header m_Sequences = new Sequence [symbolBlock.Read32(symbolBlock.Read32(0x08))]; m_SeqArcs = new SequenceArchive[symbolBlock.Read32(symbolBlock.Read32(0x0c))]; m_Banks = new Bank [symbolBlock.Read32(symbolBlock.Read32(0x10))]; m_WaveArcs = new SWAR [symbolBlock.Read32(symbolBlock.Read32(0x14))]; m_Players = new Player [symbolBlock.Read32(symbolBlock.Read32(0x18))]; m_Groups = new Group [symbolBlock.Read32(symbolBlock.Read32(0x1c))]; INitroROMBlock symbSeqBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x08) + 4, (uint)m_Sequences.Length * 4)); INitroROMBlock symbSeqArcBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x0c) + 4, (uint)m_SeqArcs.Length * 8)); INitroROMBlock symbBankBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x10) + 4, (uint)m_Banks.Length * 4)); INitroROMBlock symbWaveBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x14) + 4, (uint)m_WaveArcs.Length * 4)); INitroROMBlock symbPlayerBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x18) + 4, (uint)m_Players.Length * 4)); INitroROMBlock symbGroupBlock = new INitroROMBlock( symbolBlock.ReadBlock(symbolBlock.Read32(0x1c) + 4, (uint)m_Groups.Length * 4)); INitroROMBlock infoSeqBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x08) + 4, (uint)m_Sequences.Length * 4)); INitroROMBlock infoSeqArcBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x0c) + 4, (uint)m_SeqArcs.Length * 4)); INitroROMBlock infoBankBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x10) + 4, (uint)m_Banks.Length * 4)); INitroROMBlock infoWaveBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x14) + 4, (uint)m_WaveArcs.Length * 4)); INitroROMBlock infoPlayerBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x18) + 4, (uint)m_Players.Length * 4)); INitroROMBlock infoGroupBlock = new INitroROMBlock( infoBlock.ReadBlock(infoBlock.Read32(0x1c) + 4, (uint)m_Groups.Length * 4)); INitroROMBlock buffer = new INitroROMBlock(); for (uint i = 0; i < m_WaveArcs.Length; ++i) { if (infoWaveBlock.Read32(4 * i) == 0) //An offset of 0 means "Does Not Exist". { continue; } uint fileID = infoBlock.Read32(infoWaveBlock.Read32(4 * i)); SWAR waveArc = new SWAR(symbolBlock.ReadString(symbWaveBlock.Read32(4 * i), -1), new INitroROMBlock(GetFile(file, fileID))); m_WaveArcs[i] = waveArc; } for (uint i = 0; i < m_Banks.Length; ++i) { if (infoBankBlock.Read32(4 * i) == 0) { continue; } buffer.m_Data = infoBlock.ReadBlock(infoBankBlock.Read32(4 * i), 12); Bank bank = new Bank(); bank.m_File = new SDAT_File(symbolBlock.ReadString(symbBankBlock.Read32(4 * i), -1), file, buffer.Read32(0)); bank.m_WaveArcs = new SWAR[4]; for (uint j = 0; j < 4; ++j) { uint waveArcID = buffer.Read16(4 + 2 * j); bank.m_WaveArcs[j] = waveArcID != 0xffff ? m_WaveArcs[waveArcID] : null; } m_Banks[i] = bank; } for (uint i = 0; i < m_Players.Length; ++i) { buffer.m_Data = infoBlock.ReadBlock(infoPlayerBlock.Read32(4 * i), 8); Player player = new Player(); player.m_Filename = symbolBlock.ReadString(symbPlayerBlock.Read32(4 * i), -1); player.m_MaxNumSeqs = buffer.Read8(0); player.m_HeapSize = buffer.Read32(4); m_Players[i] = player; } for (uint i = 0; i < m_Sequences.Length; ++i) { if (infoSeqBlock.Read32(4 * i) == 0) { continue; } buffer.m_Data = infoBlock.ReadBlock(infoSeqBlock.Read32(4 * i), 12); Sequence sequence = new Sequence(); sequence.m_File = new SDAT_File(symbolBlock.ReadString(symbSeqBlock.Read32(4 * i), -1), file, buffer.Read32(0)); sequence.m_Bank = m_Banks[buffer.Read16(4)]; sequence.m_Volume = buffer.Read8(6); sequence.m_ChannelPriority = buffer.Read8(7); sequence.m_PlayerPriority = buffer.Read8(8); sequence.m_Player = m_Players[buffer.Read8(9)]; m_Sequences[i] = sequence; } for (uint i = 0; i < m_SeqArcs.Length; ++i) { if (infoSeqArcBlock.Read32(4 * i) == 0) { continue; } uint fileID = infoBlock.Read32(infoSeqArcBlock.Read32(4 * i)); SequenceArchive seqArc = new SequenceArchive(); seqArc.m_Filename = symbolBlock.ReadString(symbSeqArcBlock.Read32(8 * i), -1); seqArc.m_Sequences = new SequenceArchive.Sequence[ symbolBlock.Read32(symbSeqArcBlock.Read32(8 * i + 4))]; INitroROMBlock symbSeqArcSeqBlock = new INitroROMBlock(symbolBlock.ReadBlock( symbSeqArcBlock.Read32(8 * i + 4) + 4, (uint)seqArc.m_Sequences.Length * 4)); INitroROMBlock ssar = new INitroROMBlock(GetFile(file, fileID)); INitroROMBlock ssarInfo = new INitroROMBlock( ssar.ReadBlock(0x20, (uint)seqArc.m_Sequences.Length * 12)); seqArc.m_Data = new INitroROMBlock( ssar.ReadBlock(ssar.Read32(0x18), (uint)ssar.m_Data.Length - ssar.Read32(0x18))); for (uint j = 0; j < seqArc.m_Sequences.Length; ++j) { buffer.m_Data = ssarInfo.ReadBlock(12 * j, 12); SequenceArchive.Sequence sequence = new SequenceArchive.Sequence(); sequence.m_Filename = symbolBlock.ReadString(symbSeqArcSeqBlock.Read32(4 * j), -1); sequence.m_SeqOffset = buffer.Read32(0); sequence.m_Bank = m_Banks[buffer.Read16(4)]; sequence.m_Volume = buffer.Read8(6); sequence.m_ChannelPriority = buffer.Read8(7); sequence.m_PlayerPriority = buffer.Read8(8); sequence.m_Player = m_Players[buffer.Read8(9)]; seqArc.m_Sequences[j] = sequence; } m_SeqArcs[i] = seqArc; } for (uint i = 0; i < m_Groups.Length; ++i) { if (infoGroupBlock.Read32(4 * i) == 0) { continue; } Group group = new Group(); group.m_Entries = new Group.Entry[ infoBlock.Read32(infoGroupBlock.Read32(4 * i))]; buffer.m_Data = infoBlock.ReadBlock(infoGroupBlock.Read32(4 * i) + 4, (uint)group.m_Entries.Length * 8); group.m_Filename = symbolBlock.ReadString(symbGroupBlock.Read32(4 * i), -1); for (uint j = 0; j < group.m_Entries.Length; ++j) { uint index = buffer.Read32(8 * j + 4); Group.Entry entry = new Group.Entry(); entry.m_LoadFlag = buffer.Read8(8 * j + 1); switch (buffer.Read8(8 * j)) { case 0: entry.m_Record = m_Sequences[index]; break; case 3: entry.m_Record = m_SeqArcs[index]; break; case 1: entry.m_Record = m_Banks[index]; break; case 2: entry.m_Record = m_WaveArcs[index]; break; default: throw new Exception("Group " + i + ", Record " + j + " has an unexpected record type: " + buffer.Read8(8 * j)); } group.m_Entries[j] = entry; } m_Groups[i] = group; } }
public void Optimise(NitroFile bca) { uint numBones = bca.Read16(0x00); uint numFrames = bca.Read16(0x02); BCAOffsetData offs = new BCAOffsetData { scaleOffset = bca.Read32(0x08), rotOffset = bca.Read32(0x0c), posOffset = bca.Read32(0x10), animOffset = bca.Read32(0x14) }; //Pass 1: The constant value pass if (numFrames > 1) { for (uint i = 0; i < numBones; ++i) { for (uint j = 0; j < 9; ++j) { if (bca.Read8(offs.animOffset + 0x24 * i + 4 * j + 1) == 0) { continue; } uint dataOffset, unitSize; offs.GetDataOffsetAndSize(j / 3, out dataOffset, out unitSize); bool interped = bca.Read8(offs.animOffset + 0x24 * i + 4 * j + 0) != 0; uint firstValID = bca.Read16(offs.animOffset + 0x24 * i + 4 * j + 2); uint numVals = interped ? numFrames / 2 + 1 : numFrames; uint[] vals = new uint[numVals]; for (uint k = 0; k < numVals; ++k) { vals[k] = bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize); } if (!Array.Exists(vals, x => x != vals[0])) //They're all the same. { uint addr = dataOffset + (firstValID + numVals) * unitSize; BackSpace(bca, addr, (numVals - 1) * unitSize, ref offs); bca.Write16(offs.animOffset + 0x24 * i + 4 * j + 0, 0x0000); continue; } if (interped) { continue; } //Pass 2: The interpolation pass bool shouldInterp = true; int[] valInts; if (unitSize == 2) { valInts = Array.ConvertAll(vals, x => x >= 0x8000 ? (int)(x | 0xffff0000) : (int)x); } else { valInts = Array.ConvertAll(vals, x => (int)x); } for (int k = 0; k < numVals - 2; k += 2) { if (Math.Abs(valInts[k] - 2 * valInts[k + 1] + valInts[k + 2]) > 1) { shouldInterp = false; } } if (!shouldInterp) { continue; } for (uint k = 0; k < numVals; k += 2) { bca.WriteVar(dataOffset + (firstValID + k / 2) * unitSize, unitSize, bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize)); } if (numVals % 2 == 0) //can't interpolate on last frame even if that frame is odd { bca.WriteVar(dataOffset + (firstValID + numVals / 2) * unitSize, unitSize, bca.ReadVar(dataOffset + (firstValID + numVals - 1) * unitSize, unitSize)); } bca.Write8(offs.animOffset + 0x24 * i + 4 * j + 0, 1); BackSpace(bca, dataOffset + (firstValID + numVals) * unitSize, (numVals - 1) / 2 * unitSize, ref offs); } } } //Pass 3: The share equal data pass. for (uint trID = 0; trID < 3; ++trID) { uint dataOffset, unitSize; offs.GetDataOffsetAndSize(trID, out dataOffset, out unitSize); BCAEntryToSort[] bcaEntries = new BCAEntryToSort[3 * numBones]; for (uint i = 0; i < numBones; ++i) { for (uint j = 0; j < 3; ++j) { bool constant = bca.Read8(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 1) == 0; bool interped = bca.Read8(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 0) != 0; uint firstValID = bca.Read16(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 2); uint numVals = constant ? 1 : interped ? numFrames / 2 + 1 : numFrames; bcaEntries[3 * i + j].boneID = i; bcaEntries[3 * i + j].axisID = j; bcaEntries[3 * i + j].data = new uint[numVals]; for (uint k = 0; k < numVals; ++k) { bcaEntries[3 * i + j].data[k] = bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize); } } } BCAEntryToSort[][] sortedEnts = bcaEntries.GroupBy(x => x.data, new UintArrEqualityComparer()). Select(g => g.ToArray()). ToArray(); foreach (BCAEntryToSort[] entArr in sortedEnts) { uint boneID = entArr[0].boneID; uint axisID = entArr[0].axisID; for (int i = 1; i < entArr.Length; ++i) //Using the 1st for reference; skip it { bool constant = bca.Read8(offs.animOffset + 0x24 * entArr[i].boneID + 4 * (3 * trID + entArr[i].axisID) + 1) == 0; bool interped = bca.Read8(offs.animOffset + 0x24 * entArr[i].boneID + 4 * (3 * trID + entArr[i].axisID) + 0) != 0; uint firstValID = bca.Read16(offs.animOffset + 0x24 * entArr[i].boneID + 4 * (3 * trID + entArr[i].axisID) + 2); uint numVals = constant ? 1 : interped ? numFrames / 2 + 1 : numFrames; uint sameValID = bca.Read16(offs.animOffset + 0x24 * boneID + 4 * (3 * trID + axisID) + 2); if (firstValID != sameValID) //Avoid deleting entries when they are already shared { bca.Write16(offs.animOffset + 0x24 * entArr[i].boneID + 4 * (3 * trID + entArr[i].axisID) + 2, (ushort)sameValID); BackSpace(bca, dataOffset + (firstValID + numVals) * unitSize, numVals * unitSize, ref offs); } } } } if ((offs.posOffset - offs.rotOffset) % 4 != 0) { BackSpace(bca, offs.posOffset, 0xfffffffe, ref offs, true); } }
public BTP(NitroFile file, BMD model) { this.m_File = file; this.m_FileName = file.m_Name; this.m_Model = model; m_NumFrames = m_File.Read16(0x00); m_NumTextures = m_File.Read16(0x02); m_TextureHeadersOffset = m_File.Read32(0x04); m_TextureNames = new List <string>(); m_NumPalettes = m_File.Read16(0x08); m_PaletteHeadersOffset = m_File.Read32(0x0C); m_PaletteNames = new List <string>(); m_FrameChangesOffset = m_File.Read32(0x10); m_FrameTextureIDsOffset = m_File.Read32(0x14); m_FramePaletteIDsOffset = m_File.Read32(0x18); m_NumMaterials = m_File.Read16(0x1C); m_MaterialHeadersOffset = m_File.Read32(0x20); m_MaterialNames = new List <string>(); for (int i = 0; i < m_NumTextures; i++) { m_TextureNames.Add(m_File.ReadString(m_File.Read32(m_TextureHeadersOffset + 0x04 + (uint)(i * 8)), 0)); } for (int i = 0; i < m_NumPalettes; i++) { m_PaletteNames.Add(m_File.ReadString(m_File.Read32(m_PaletteHeadersOffset + 0x04 + (uint)(i * 8)), 0)); } // Read in the frames for each material m_MaterialData = new Dictionary <string, BTPMaterialData>(); m_Frames = new List <BTPFrameData>(); for (int i = 0; i < m_NumMaterials; i++) { m_MaterialNames.Add(m_File.ReadString(m_File.Read32(m_MaterialHeadersOffset + 0x04 + (uint)(i * 12)), 0)); ushort matFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x08 + (uint)(i * 12)); ushort startOffsetFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x0A + (uint)(i * 12)); int matNumFrames = 0; for (int j = 0; j < matFrameChanges; j++) { ushort frameNum = m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + (j * 2))); // If not the last frame in the sequence, length = (next frame number - current frame number) else, // length = (number of frames in sequence - current frame number) int nextFrameNum = (j != (matFrameChanges - 1)) ? (int)m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + ((j + 1) * 2))) : -1; int length = (nextFrameNum != -1) ? (nextFrameNum - frameNum) : (m_NumFrames - frameNum); ushort texID = m_File.Read16((uint)(m_FrameTextureIDsOffset + (startOffsetFrameChanges * 2) + (j * 2))); ushort palID = m_File.Read16((uint)(m_FramePaletteIDsOffset + (startOffsetFrameChanges * 2) + (j * 2))); AddFrame(matNumFrames, m_Frames.Count, texID, palID, length); matNumFrames += length; } BTPMaterialData matData = new BTPMaterialData(m_MaterialNames[i], matFrameChanges, startOffsetFrameChanges, matNumFrames); m_MaterialData.Add(m_MaterialNames[i], matData); } }