Difference between revisions of "FF8/FileFormat magfiles"
my_wiki>MaKiPL m (→Environment objects) |
my_wiki>MaKiPL (GF update) |
||
Line 43: | Line 43: | ||
}; | }; | ||
Example: 09 00 9f 01 means there's 9f 01 (415) polygons of type 0x9. Therefore: 28*415=11620 bytes to next polygon type or vertices if FF FF FF FF. | Example: 09 00 9f 01 means there's 9f 01 (415) polygons of type 0x9. Therefore: 28*415=11620 bytes to next polygon type or vertices if FF FF FF FF. | ||
+ | |||
+ | ==Magic.fs MAG environment GF objects== | ||
+ | GF Environment (stages like Cerberus gate; Alexander backdrop and etc.) geometry contains many polygon types (sic!). Every single polygon type is different than other. | ||
+ | |||
+ | private int[] _knownPolygons = new int[] | ||
+ | { | ||
+ | 0x2, | ||
+ | 0x6, | ||
+ | 0x7, | ||
+ | 0x8, | ||
+ | 0x9, | ||
+ | 0xC, | ||
+ | 0x10, | ||
+ | 0x12, | ||
+ | 0x11, | ||
+ | 0x13, | ||
+ | }; | ||
+ | |||
+ | |||
+ | 0xC points to geometry section. If *0xC (pointers to geometries) is == 0, then file has no geometry (it is possible. Some files are only texture, some only geometry, some are only animation and some are everything). There should be no single case where the number of models in file exceed 12. | ||
+ | |||
+ | 0xC: | ||
+ | PointersToModel: | ||
+ | Example object (let's call it modOffset) | ||
+ | |||
+ | uint _objCount = *modOffset; | ||
+ | ushort _vertexCount = *(modOffset + 24); | ||
+ | ushort _verticesOffset = *(modOffset + 20); | ||
+ | |||
+ | Polygon parsing based on types (C#): | ||
+ | |||
+ | int relativeJump = *(modOffset + 8); | ||
+ | int *localoffset = modOffset + _relativeJump + 4; | ||
+ | |||
+ | if (polygonType == 0x02) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 20; i += 20) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)} {GetPolygon(localoffset + i + 0x10)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 20; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x06) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 12; i += 12) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 0x4)} {GetPolygon(localoffset + i + 0x6)} {GetPolygon(localoffset + i + 0x08)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 12; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x07) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 20; i += 20) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)} {GetPolygon(localoffset + i + 0x10)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 20; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x09) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 28; i += 28) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 20)} {GetPolygon(localoffset + i + 22)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 28; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x08) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 20; i += 20) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 0xA)} {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 20; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 12) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 28; i += 28) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 20)} {GetPolygon(localoffset + i + 22)} {GetPolygon(localoffset + i + 26)} {GetPolygon(localoffset + i + 24)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 28; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x12) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 24; i += 24) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 12)} {GetPolygon(localoffset + i + 14)} {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 16)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 24; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x13) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 36; i += 36) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 24)} {GetPolygon(localoffset + i + 26)} {GetPolygon(localoffset + i + 30)} {GetPolygon(localoffset + i + 28)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 36; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x11) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 24; i += 24) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 16)} {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 22)} {GetPolygon(localoffset + i + 20)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 24; | ||
+ | } | ||
+ | |||
+ | if (polygonType == 0x10) | ||
+ | { | ||
+ | for (int i = 0; i < polygons * 12; i += 12) | ||
+ | { | ||
+ | f += $"f {GetPolygon(localoffset + i + 4)} {GetPolygon(localoffset + i + 6)} {GetPolygon(localoffset + i + 10)} {GetPolygon(localoffset + i + 8)}\n"; | ||
+ | } | ||
+ | safeHandle = polygons * 12; | ||
+ | } | ||
+ | |||
+ | uint isNext = *(localoffset + safeHandle); | ||
+ | if (isNext == 0xFFFFFFFF) //FFFFFFFF breaks the polygons reading | ||
+ | break; | ||
+ | Vertex: | ||
+ | short x | ||
+ | short z | ||
+ | short y | ||
+ | short w //rarely used, for skeleton weighting |
Revision as of 18:57, 10 July 2016
By MaKiPL
There is no unified format for MAG files. Every file has their own naming, even extension. You can't really distinguish them.
Files starting with 10 00 00 00 09 00 00 00 are obviously TIM texture. What we know so far is how to extract GF sequence and GF environment geometry (ifrit ball, Leviathan's rock, Cerberus gate...)
G.F. Sequence
http://forums.qhimm.com/index.php?topic=15056.0
Environment objects
I'm aware it's chaotic, like no tables and etc. but it's up-to-date information and I'll update it after I crack the face indices (triangles/quads) First, recognize file:
1.Header must be 8 bytes null!!
2.Jump to pointer at 0xC [Global]
3.This int indicates pointer count. Example: 10 00 00 00 is 16 pointers
3a. If pointer is 00 00 00 00, just ignore it and move forward
4.After noting all pointers that are not 00 00 00 00 (therefore inspecting Count*4 bytes) just jump to selected one [Relative jump from count int]
5.See third int (8th byte) for UNKNOWN(POLYGON TYPES see below) [Relative jump from 03 00 00 00]
6.Read int at 20th byte for VERTICES offset [Relative jump from 03 00 00 00]
7.Read int at 24th byte for VERTICES COUNT [Vertex is 8 bytes! X Z Y W, where W is probably weight byte [as in OBJ specification] and is normally unused (as usual, even in casual OBJ files)]
The polygons are weird? So far I think there are more polygon types. If you work with this dictionary you're going to be fine:
private Dictionary<UInt16,int> _polygonType = new Dictionary<ushort, int> { { 0x7, 20}, //BAD? { 0x8, 20}, //OK { 0x9, 28}, //OK {0x10, 20}, //BAD? {0x12, 24}, //OK {0x13, 36}, //OK {0x18, 0x18} //24 BAD? };
Example: 09 00 9f 01 means there's 9f 01 (415) polygons of type 0x9. Therefore: 28*415=11620 bytes to next polygon type or vertices if FF FF FF FF.
Magic.fs MAG environment GF objects
GF Environment (stages like Cerberus gate; Alexander backdrop and etc.) geometry contains many polygon types (sic!). Every single polygon type is different than other.
private int[] _knownPolygons = new int[] { 0x2, 0x6, 0x7, 0x8, 0x9, 0xC, 0x10, 0x12, 0x11, 0x13, };
0xC points to geometry section. If *0xC (pointers to geometries) is == 0, then file has no geometry (it is possible. Some files are only texture, some only geometry, some are only animation and some are everything). There should be no single case where the number of models in file exceed 12.
0xC: PointersToModel: Example object (let's call it modOffset)
uint _objCount = *modOffset; ushort _vertexCount = *(modOffset + 24); ushort _verticesOffset = *(modOffset + 20);
Polygon parsing based on types (C#):
int relativeJump = *(modOffset + 8); int *localoffset = modOffset + _relativeJump + 4;
if (polygonType == 0x02) { for (int i = 0; i < polygons * 20; i += 20) { f += $"f {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)} {GetPolygon(localoffset + i + 0x10)}\n"; } safeHandle = polygons * 20; }
if (polygonType == 0x06) { for (int i = 0; i < polygons * 12; i += 12) { f += $"f {GetPolygon(localoffset + i + 0x4)} {GetPolygon(localoffset + i + 0x6)} {GetPolygon(localoffset + i + 0x08)}\n"; } safeHandle = polygons * 12; }
if (polygonType == 0x07) { for (int i = 0; i < polygons * 20; i += 20) { f += $"f {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)} {GetPolygon(localoffset + i + 0x10)}\n"; } safeHandle = polygons * 20; }
if (polygonType == 0x09) { for (int i = 0; i < polygons * 28; i += 28) { f += $"f {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 20)} {GetPolygon(localoffset + i + 22)}\n"; } safeHandle = polygons * 28; }
if (polygonType == 0x08) { for (int i = 0; i < polygons * 20; i += 20) { f += $"f {GetPolygon(localoffset + i + 0xA)} {GetPolygon(localoffset + i + 0xC)} {GetPolygon(localoffset + i + 0xE)}\n"; } safeHandle = polygons * 20; }
if (polygonType == 12) { for (int i = 0; i < polygons * 28; i += 28) { f += $"f {GetPolygon(localoffset + i + 20)} {GetPolygon(localoffset + i + 22)} {GetPolygon(localoffset + i + 26)} {GetPolygon(localoffset + i + 24)}\n"; } safeHandle = polygons * 28; }
if (polygonType == 0x12) { for (int i = 0; i < polygons * 24; i += 24) { f += $"f {GetPolygon(localoffset + i + 12)} {GetPolygon(localoffset + i + 14)} {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 16)}\n"; } safeHandle = polygons * 24; }
if (polygonType == 0x13) { for (int i = 0; i < polygons * 36; i += 36) { f += $"f {GetPolygon(localoffset + i + 24)} {GetPolygon(localoffset + i + 26)} {GetPolygon(localoffset + i + 30)} {GetPolygon(localoffset + i + 28)}\n"; } safeHandle = polygons * 36; }
if (polygonType == 0x11) { for (int i = 0; i < polygons * 24; i += 24) { f += $"f {GetPolygon(localoffset + i + 16)} {GetPolygon(localoffset + i + 18)} {GetPolygon(localoffset + i + 22)} {GetPolygon(localoffset + i + 20)}\n"; } safeHandle = polygons * 24; }
if (polygonType == 0x10) { for (int i = 0; i < polygons * 12; i += 12) { f += $"f {GetPolygon(localoffset + i + 4)} {GetPolygon(localoffset + i + 6)} {GetPolygon(localoffset + i + 10)} {GetPolygon(localoffset + i + 8)}\n"; } safeHandle = polygons * 12; }
uint isNext = *(localoffset + safeHandle); if (isNext == 0xFFFFFFFF) //FFFFFFFF breaks the polygons reading break;
Vertex:
short x short z short y short w //rarely used, for skeleton weighting