FF8/WorldMap charaone

From Final Fantasy Inside
< FF8
Revision as of 18:38, 16 February 2019 by my_wiki>MaKi (section 12 (arg4 == 0x12))

by Maki file is different than field [ONE] Basically you have to read the whole file step-by-step as there are minimum pointers and no sizes at all. Therefore just to read one character on world map you have to actually read all content of chara.one. You have to read whole TIM textures first, calculate the global size of TIM texture and advance further. Textures are always 0x10000000 08000000. My approach is to read uint64 and check if it contains TIM header. If not, then geometry section (see Chara). I named the bones/animation section as "section 12" because game engine calls function for chara.one files with 0x11 for reading the header data (chara) and then again with argument 0x12 having pointer to data as in Wiki in EAX. It's like:

uint myPointer = charaOneFunction(pointer_to_Chara_section, 0x11...);
charaOneFunction(myPointer, 0x12...);


Header

Offset Length Description
0x00 uint EOF
0x04 (TIM_height * (TIM_width * 4) ) / 2 + 64 TIM texture
*(this) != x 10 00 00 00 08 00 00 00 Varies Chara

Chara

Offset Length Description
0x00 uint bone section? length (shift left 6)
0x04 uint unknown struct size. Multiply by 8
0x08 uint unknown size
?? uint 32 * *(dword_24FEE48[v24] + 36))
0x38 uint section 12 pointer

section 12 (arg4 == 0x12)

names are WIP, I'm not quite sure if it's animation, geometry or bones...

Offset Length Description
0x00 ushort countOfEntries
0x02 + ( entry.Length != 0 ? sizeof(entry[i-1] : 0) sizeof(this) Entry


Entry:

Offset Length Description
0x00 ushort count of entities
0x02 ushort count of bone rotations
0x04 + (entityID * (countOfBoneRot * 6) + 6) 6 + (countOfBoneRotations*6) entityObject

entityObject:

Offset Length Description
0x00 short X offset
0x02 short Z offset
0x04 short Y (up) offset
0x06 + (boneID*6) short Bone[boneID].RotX
0x08 + (boneID*6) short Bone[boneID].RotZ
0x0A + (boneID*6) short Bone[boneID].RotY

Contents

source

Example C# source of file parsing as in OpenVIII (commit from 16/02/1019): https://github.com/MaKiPL/OpenVIII/blob/db46297fbc7961852e89427927df2e40623266a5/FF8/module_world_debug.cs#L202

private static void ReadCharaOne(byte[] charaOneB)
       {
           using (MemoryStream ms = new MemoryStream(charaOneB))
           using (BinaryReader br = new BinaryReader(ms))
           {
               uint eof = br.ReadUInt32();
               TIM2 tim;
               while (ms.CanRead)
                   if (BitConverter.ToUInt16(charaOneB, (int)ms.Position) == 0)
                       ms.Seek(2, SeekOrigin.Current);
                   else if (br.ReadUInt64() == 0x0000000800000010)
                   {
                       ms.Seek(-8, SeekOrigin.Current);
                       tim = new TIM2(charaOneB, (uint)ms.Position);
                       ms.Seek(tim.GetHeight * tim.GetWidth / 2 + 64, SeekOrigin.Current); //i.e. 64*20=1280/2=640 + 64= 704 + eof
                       if (charaOneTextures == null)
                           charaOneTextures = new List<Texture2D[]>();
                       charaOneTextures.Add(new Texture2D[1] { new Texture2D(Memory.graphics.GraphicsDevice, tim.GetWidth, tim.GetHeight, false, SurfaceFormat.Color) });
                       charaOneTextures.Last()[0].SetData(tim.CreateImageBuffer(tim.GetClutColors(0), true));
                   }
                   else //is geometry structure
                   {
                       ms.Seek(-8, SeekOrigin.Current);
                       uint esi10h = BitConverter.ToUInt32(charaOneB, (int)ms.Position + 0x10);
                       uint esi14h = BitConverter.ToUInt32(charaOneB, (int)ms.Position + 0x14);
                       uint u45 = BitConverter.ToUInt32(charaOneB, (int)ms.Position + 56); //+2?
                       uint v29 = BitConverter.ToUInt32(charaOneB, (int)ms.Position) << 6; //bone section?
                       uint d250516c = BitConverter.ToUInt32(charaOneB, (int)ms.Position + 4) * 8; //unk?
                       uint v25 = BitConverter.ToUInt32(charaOneB, (int)ms.Position + 8); //unk size?
                       ms.Seek(u45, SeekOrigin.Current);
                       if (ms.Position > ms.Length)
                           break;
                       ushort countIs = br.ReadUInt16();
                       ushort cEntries = 0;
                       while (countIs != 0/*(cEntries = br.ReadUInt16()) != 0*/)
                       {
                           cEntries = br.ReadUInt16();
                           ushort cBones = br.ReadUInt16();
                           while (cEntries != 0)
                           {
                               Bone testBone = new Bone() { X = br.ReadInt16(), Y = br.ReadInt16(), Z = br.ReadInt16() };
                               Vector3[] myVec = new Vector3[cBones];
                               for (int i = 0; i < cBones; i++)
                                   myVec[i] = new Vector3() { X = br.ReadInt16(), Y = br.ReadUInt16(), Z = br.ReadUInt16() };
                               cEntries--;
                           }
                           countIs--;
                       }
                   }
               return;
           }
}