using System; using System.IO; using System.Windows.Forms; namespace NarcAPI { public class Narc { public String Name { get; set; } private MemoryStream[] Elements; private int FatbOffset, FntbOffset, FimgOffset; private Narc(String name) { this.Name = name; } public static Narc NewEmpty(String name = "NewNarc") { Narc narc = new Narc(name); return narc; } public static Narc Open(String filePath) { FileStream file = File.OpenRead(filePath); Narc narc = new Narc(Path.GetFileNameWithoutExtension(filePath)); BinaryReader br = new BinaryReader(file); narc.ReadOffsets(br); narc.ReadElements(br); br.Close(); return narc; } public static Narc FromFolder(String dirPath) { Narc narc = new Narc(Path.GetDirectoryName(dirPath)); String[] fileNames = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories); uint numberOfElements = (uint)fileNames.Length; narc.Elements = new MemoryStream[numberOfElements]; for (int i = 0; i < numberOfElements; i++) { FileStream fs = File.OpenRead(fileNames[i]); MemoryStream ms = new MemoryStream(); byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, (int)fs.Length); ms.Write(buffer, 0, (int)fs.Length); narc.Elements[i] = ms; fs.Close(); } return narc; } public void Save(String filePath) { uint fileSizeOffset, fimgSizeOffset, curOffset; FileStream file = File.Create(filePath); BinaryWriter bw = new BinaryWriter(file); // Write NARC Section bw.Write(0x4352414E); // "NARC" bw.Write(0x0100FFFE); // Constant fileSizeOffset = (uint)bw.BaseStream.Position; bw.Write((UInt32)0x0); bw.Write((UInt16)16); bw.Write((UInt16)3); // Write FATB Section bw.Write(0x46415442); // "BTAF" bw.Write((UInt32)(0xC + Elements.Length * 8)); // FATB Size bw.Write((UInt32)Elements.Length); // Number of elements curOffset = 0; for (int i = 0; i < Elements.Length; i++) { while (curOffset % 4 != 0) curOffset++; // Force offsets to be a multiple of 4 bw.Write(curOffset); curOffset += (uint)Elements[i].Length; bw.Write(curOffset); } // Write FNTB Section (No names, sorry =( ) bw.Write(0x464E5442); // "BTNF" bw.Write(0x10); // FNTB Size bw.Write(0x4); // <- bw.Write(0x10000); // |-- Pointless data // Write FIMG Section bw.Write(0x46494D47); // "GMIF" fimgSizeOffset = (uint)bw.BaseStream.Position; bw.Write((UInt32)0x0); curOffset = 0; byte[] buffer; for (int i = 0; i < Elements.Length; i++) { while (curOffset % 4 != 0) { bw.Write((Byte)0xFF); curOffset++; } // Force offsets to be a multiple of 4 // Data writin' buffer = new byte[Elements[i].Length]; Elements[i].Seek(0, SeekOrigin.Begin); Elements[i].Read(buffer, 0, (int)Elements[i].Length); bw.Write(buffer, 0, (int)Elements[i].Length); curOffset += (uint)Elements[i].Length; } // Writes sizes int fileSize = (int)bw.BaseStream.Position; bw.Seek((int)fileSizeOffset, SeekOrigin.Begin); // File size bw.Write((UInt32)fileSize); bw.Seek((int)fimgSizeOffset, SeekOrigin.Begin); // FIMG size bw.Write((UInt32)curOffset + 0x8); // FIMG size == Last end offset + 0x8 bw.Close(); } public void ExtractToFolder(String dirPath) { Console.WriteLine(dirPath); if (Directory.Exists(dirPath)) { try { Directory.Delete(dirPath, true); } catch (IOException) { MessageBox.Show("Can't access temp directory: \n" + dirPath +"\nThis might be a temporary issue.\nMake sure no other process is using it and try again.", "Open Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } try { Directory.CreateDirectory(dirPath); } catch (ArgumentNullException) { MessageBox.Show("Dir path is null.", "Can't create directory", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } FileStream file; byte[] buffer; for (int i = 0; i < Elements.Length; i++) { file = File.Create(Path.Combine(dirPath, i.ToString("D4"))); buffer = new byte[Elements[i].Length]; Elements[i].Seek(0, SeekOrigin.Begin); Elements[i].Read(buffer, 0, (int)Elements[i].Length); file.Write(buffer, 0, (int)Elements[i].Length); file.Close(); } } public void Free() // Libera todos los recursos de memoria asociados (cierra los streams) { for (int i = 0; i < Elements.Length; i++) { Elements[i].Close(); } } public MemoryStream this[int elemIndex] { get { return Elements[elemIndex]; } set { Elements[elemIndex] = value; } } public int GetElementsLength() { return Elements.Length; } private void ReadOffsets(BinaryReader br) { FatbOffset = 0x10; br.BaseStream.Position = 0x18; // Number of elements FntbOffset = (int)br.ReadUInt32() * 8 + FatbOffset + 12; br.BaseStream.Position = FntbOffset + 0x4; // FNTB Size FimgOffset = (int)br.ReadUInt32() + FntbOffset; } private void ReadElements(BinaryReader br) { uint numberOfElements; uint[] startOffsets, endOffsets; // Create array of elements br.BaseStream.Position = 0x18; Elements = new MemoryStream[numberOfElements = br.ReadUInt32()]; // Read offsets of each element startOffsets = new uint[numberOfElements]; endOffsets = new uint[numberOfElements]; br.BaseStream.Position = FatbOffset + 0xC; for (int i = 0; i < numberOfElements; i++) { startOffsets[i] = br.ReadUInt32(); endOffsets[i] = br.ReadUInt32(); } // Read elements for (int i = 0; i < numberOfElements; i++) { MemoryStream ms; byte[] buffer; br.BaseStream.Position = FimgOffset + startOffsets[i] + 0x8; buffer = new byte[endOffsets[i] - startOffsets[i]]; br.Read(buffer, 0, (int)(endOffsets[i] - startOffsets[i])); ms = new MemoryStream(buffer); Elements[i] = ms; } } } }