/// <summary> /// Reads a generic Pokémon container from a Stream. /// Those containers are the ones that starts with "GR", "MM", "AD" and so on... /// </summary> /// <param name="data">Stream with container data</param> /// <returns></returns> public static OContainer load(Stream data) { BinaryReader input = new BinaryReader(data); OContainer output = new OContainer(); IOUtils.readString(input, 0, 2); //Magic ushort sectionCount = input.ReadUInt16(); for (int i = 0; i < sectionCount; i++) { OContainer.fileEntry entry = new OContainer.fileEntry(); data.Seek(4 + (i * 4), SeekOrigin.Begin); uint startOffset = input.ReadUInt32(); uint endOffset = input.ReadUInt32(); uint length = endOffset - startOffset; data.Seek(startOffset, SeekOrigin.Begin); byte[] buffer = new byte[length]; input.Read(buffer, 0, (int)length); entry.data = buffer; output.content.Add(entry); } data.Close(); return output; }
private void BtnExport_Click(object sender, System.EventArgs e) { if (FileList.SelectedIndex == -1) { return; } using (SaveFileDialog saveDlg = new SaveFileDialog()) { saveDlg.Title = "Export file"; saveDlg.FileName = container.content[FileList.SelectedIndex].name; saveDlg.Filter = "All files|*.*"; if (saveDlg.ShowDialog() == DialogResult.OK) { OContainer.fileEntry file = container.content[FileList.SelectedIndex]; if (file.loadFromDisk) { byte[] buffer = new byte[file.fileLength]; container.data.Seek(file.fileOffset, SeekOrigin.Begin); container.data.Read(buffer, 0, buffer.Length); File.WriteAllBytes(saveDlg.FileName, buffer); } else { File.WriteAllBytes(saveDlg.FileName, file.data); } } } }
/// <summary> /// Reads a DARC archive. /// </summary> /// <param name="data">Stream of the data</param> /// <returns>The container data</returns> public static OContainer load(Stream data) { OContainer output = new OContainer(); BinaryReader input = new BinaryReader(data); string darcMagic = IOUtils.readStringWithLength(input, 4); ushort endian = input.ReadUInt16(); ushort headerLength = input.ReadUInt16(); uint version = input.ReadUInt32(); uint fileSize = input.ReadUInt32(); uint tableOffset = input.ReadUInt32(); uint tableLength = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); data.Seek(tableOffset, SeekOrigin.Begin); fileEntry root = getEntry(input); int baseOffset = (int)data.Position; int namesOffset = (int)(tableOffset + root.length * 0xc); string currDir = null; for (int i = 0; i < root.length - 1; i++) { data.Seek(baseOffset + i * 0xc, SeekOrigin.Begin); fileEntry entry = getEntry(input); if ((entry.flags & 1) > 0) { //Folder int index = i; currDir = null; for (;;) { uint parentIndex = entry.offset; currDir = getName(input, entry.nameOffset + namesOffset) + "/" + currDir; if (parentIndex == 0 || parentIndex == index) break; data.Seek(baseOffset + parentIndex * 0xc, SeekOrigin.Begin); entry = getEntry(input); index = (int)parentIndex; } continue; } data.Seek(entry.offset, SeekOrigin.Begin); byte[] buffer = new byte[entry.length]; data.Read(buffer, 0, buffer.Length); OContainer.fileEntry file = new OContainer.fileEntry(); file.name = currDir + getName(input, entry.nameOffset + namesOffset); file.data = buffer; output.content.Add(file); } data.Close(); return output; }
/// <summary> /// Reads a GARC archive. /// </summary> /// <param name="data">Stream of the data</param> /// <returns>The container data</returns> public static OContainer load(Stream data) { OContainer output = new OContainer(); BinaryReader input = new BinaryReader(data); output.data = data; string garcMagic = IOUtils.readStringWithLength(input, 4); uint garcHeaderLength = input.ReadUInt32(); ushort endian = input.ReadUInt16(); input.ReadUInt16(); //0x400 uint sectionCount = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); uint decompressedLength = input.ReadUInt32(); uint compressedLength = input.ReadUInt32(); //File Allocation Table Offsets string fatoMagic = IOUtils.readStringWithLength(input, 4); uint fatoHeaderLength = input.ReadUInt32(); ushort fatoEntries = input.ReadUInt16(); input.ReadUInt16(); //0xffff = Padding? data.Seek(fatoEntries * 4, SeekOrigin.Current); //We don't need this string fatbMagic = IOUtils.readStringWithLength(input, 4); uint fatbHeaderLength = input.ReadUInt32(); uint entries = input.ReadUInt32(); long baseOffset = data.Position; for (int i = 0; i < entries; i++) { data.Seek(baseOffset + i * 0x10, SeekOrigin.Begin); uint flags = input.ReadUInt32(); uint startOffset = input.ReadUInt32(); uint endOffset = input.ReadUInt32(); uint length = input.ReadUInt32(); input.BaseStream.Seek(startOffset + dataOffset, SeekOrigin.Begin); byte[] buffer = new byte[Math.Min(0x10, length)]; input.Read(buffer, 0, buffer.Length); bool isCompressed = buffer.Length > 0 ? buffer[0] == 0x11 : false; string extension = FileIO.getExtension(buffer, isCompressed ? 5 : 0); string name = string.Format("file_{0:D5}{1}", i, extension); //And add the file to the container list OContainer.fileEntry entry = new OContainer.fileEntry(); entry.name = name; entry.loadFromDisk = true; entry.fileOffset = startOffset + dataOffset; entry.fileLength = length; output.content.Add(entry); } return output; }
/// <summary> /// Reads FPT0 containers from Dragon Quest VII. /// </summary> /// <param name="data">Stream with container data</param> /// <returns></returns> public static OContainer load(Stream data) { BinaryReader input = new BinaryReader(data); OContainer output = new OContainer(); data.Seek(8, SeekOrigin.Begin); uint entries = input.ReadUInt32(); uint baseAddress = 0x10 + (entries * 0x20) + 0x80; input.ReadUInt32(); List<sectionEntry> files = new List<sectionEntry>(); for (int i = 0; i < entries; i++) { sectionEntry entry = new sectionEntry(); entry.name = IOUtils.readString(input, (uint)(0x10 + (i * 0x20))); data.Seek(0x20 + (i * 0x20), SeekOrigin.Begin); input.ReadUInt32(); //Memory address? entry.offset = input.ReadUInt32() + baseAddress; entry.length = input.ReadUInt32(); input.ReadUInt32(); //Padding? files.Add(entry); } foreach (sectionEntry file in files) { OContainer.fileEntry entry = new OContainer.fileEntry(); data.Seek(file.offset, SeekOrigin.Begin); byte[] buffer = new byte[file.length]; input.Read(buffer, 0, buffer.Length); entry.data = buffer; entry.name = file.name; output.content.Add(entry); } data.Close(); return output; }
/// <summary> /// Reads a SARC archive. /// </summary> /// <param name="data">Stream of the data</param> /// <returns>The container data</returns> public static OContainer load(Stream data) { OContainer output = new OContainer(); BinaryReader input = new BinaryReader(data); string sarcMagic = IOUtils.readStringWithLength(input, 4); ushort sarcHeaderLength = input.ReadUInt16(); ushort endian = input.ReadUInt16(); uint fileLength = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); uint dataPadding = input.ReadUInt32(); string sfatMagic = IOUtils.readStringWithLength(input, 4); ushort sfatHeaderLength = input.ReadUInt16(); ushort entries = input.ReadUInt16(); uint hashMultiplier = input.ReadUInt32(); int sfntOffset = 0x20 + entries * 0x10 + 8; for (int i = 0; i < entries; i++) { data.Seek(0x20 + i * 0x10, SeekOrigin.Begin); uint nameHash = input.ReadUInt32(); uint nameOffset = (input.ReadUInt32() & 0xffffff) << 2; uint offset = input.ReadUInt32(); uint length = input.ReadUInt32() - offset; string name = IOUtils.readString(input, (uint)(sfntOffset + nameOffset)); data.Seek(offset + dataOffset, SeekOrigin.Begin); byte[] buffer = new byte[length]; data.Read(buffer, 0, buffer.Length); if (name == "") name = string.Format("file_{0:D5}{1}", i, FileIO.getExtension(buffer)); OContainer.fileEntry entry = new OContainer.fileEntry(); entry.name = name; entry.data = buffer; output.content.Add(entry); } data.Close(); return output; }
/// <summary> /// Reads the Model PACKage from Dragon Quest VII. /// </summary> /// <param name="data">Stream of the data</param> /// <returns></returns> public static OContainer load(Stream data) { BinaryReader input = new BinaryReader(data); OContainer output = new OContainer(); List<sectionEntry> mainSection = getSection(input); //World nodes section data.Seek(mainSection[0].offset, SeekOrigin.Begin); List<node> nodes = new List<node>(); List<sectionEntry> worldNodesSection = getSection(input); foreach (sectionEntry entry in worldNodesSection) { data.Seek(entry.offset, SeekOrigin.Begin); node n = new node(); //Geometry node input.ReadUInt32(); //GNOD magic number input.ReadUInt32(); input.ReadUInt32(); n.parentId = input.ReadInt32(); n.name = IOUtils.readString(input, (uint)data.Position); data.Seek(entry.offset + 0x20, SeekOrigin.Begin); n.transform = new RenderBase.OMatrix(); RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z)); n.transform *= RenderBase.OMatrix.rotateX(r.x); n.transform *= RenderBase.OMatrix.rotateY(r.y); n.transform *= RenderBase.OMatrix.rotateZ(r.z); n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z)); nodes.Add(n); } RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count]; for (int i = 0; i < nodes.Count; i++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformNode(nodes, i, ref transform); nodesTransform[i] = transform; } //Models section data.Seek(mainSection[1].offset, SeekOrigin.Begin); List<sectionEntry> modelsSection = getSection(input); foreach (sectionEntry entry in modelsSection) { data.Seek(entry.offset, SeekOrigin.Begin); //Field Data section /* * Usually have 3 entries. * 1st entry: Model CGFX * 2nd entry: Unknow CGFX, possibly animations * 3rd entry: Another FieldData section, possibly child object */ List<sectionEntry> fieldDataSection = getSection(input); data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin); uint length = fieldDataSection[0].length; while ((length & 0x7f) != 0) length++; //Align byte[] buffer = new byte[length]; input.Read(buffer, 0, buffer.Length); OContainer.fileEntry file = new OContainer.fileEntry(); file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl"; file.data = buffer; output.content.Add(file); } //FILE section data.Seek(mainSection[2].offset, SeekOrigin.Begin); //TODO //Collision section data.Seek(mainSection[3].offset, SeekOrigin.Begin); //TODO //PARM(???) section data.Seek(mainSection[4].offset, SeekOrigin.Begin); //TODO //Textures CGFX data.Seek(mainSection[5].offset, SeekOrigin.Begin); byte[] texBuffer = new byte[mainSection[5].length]; input.Read(texBuffer, 0, texBuffer.Length); OContainer.fileEntry texFile = new OContainer.fileEntry(); texFile.name = "textures.bctex"; texFile.data = texBuffer; output.content.Add(texFile); data.Close(); return output; }