Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.

[PS2] Medal of Honor: Frontline

Featured Replies

I'm digging more and more into figuring out how to extract these meshes from the game Medal of Honor: Frontline (and Rising Sun) for the PS2. I've already been able to get the sfx/music so I have a little experience in using a hex editor. I would really appreciate anyone able to help me make sense of what I am seeing for mesh data, or at least get a fresh set of eyes to explain things better. I've looked through a few of the tutorials and will continue to research but I'd like to get this out there in case someone wants to take a jab at it. I have manually extracted the asset below from the parent archive (which seems to be some sort of BIG file? 0xC0FB magic), and it is quite a bit bigger in size from a previous attempt using a tool that was made for those types of files, but I don't think it's useful for this game. I can see floats and other groupings of things but I just can't make sense of them with the knowledge I have at this point. To me, it looks inconsistent. I also have Model Researcher and Noesis on-hand. Thank you for your time.

Vehicle_Jeep_Wheel.zip

  • Author
55 minutes ago, shak-otay said:

I'd guess for VIF tags but the resulting point clouds are ugly. Maybe upload a bigger part of the jeep?

Thanks for taking a look. I've also attached some pics for the header of the archive, and of the offset/size/name of the files produced, if it helps. The second result in the search is just the names.

JeepOffset.png

LevelHdr.png

Vehicle_Jeep_Body.zip

  • Supporter

Thanks! I gathered 7 sub meshes, it looks like something but would require much fiddling for a better result:

 

jeepPS2.png

edit: when interpreted as uv data it looks smarter. (But times where I spent hours with PS2 VIF blocks are over...)

Edited by shak-otay

  • Supporter

There is xbox version too. I think it will be way easier...

Try this one... It's from xbox

Vehicle_Jeep_Body.7z

Also bms

endian big
get BaseFileName basename

idstring "BIGF"
getDString DUMMY 0x4
get Resources long
get TOCSize long

for i = 0 < Resources
	get ResourceOffset long
	get ResourceSize long
	get ResourceName string
	string Name p "%s/%s" BaseFileName ResourceName
	log Name ResourceOffset ResourceSize
next i

image.thumb.png.014a4e51d2dc3ae6c556f9f9da7ddd00.png

Edited by h3x3r

  • Author
14 hours ago, shak-otay said:

Thanks! I gathered 7 sub meshes, it looks like something but would require much fiddling for a better result:

edit: when interpreted as uv data it looks smarter. (But times where I spent hours with PS2 VIF blocks are over...)

12 hours ago, h3x3r said:

There is xbox version too. I think it will be way easier...

Thanks 🙂 You guys are awesome. PS2 must be doing a lot of interpretation type stuff then? I'll take a look at the Xbox and Gamecube versions.

  • Supporter

image.thumb.png.12901e097c28c815eb98af21e70fe46c.png

Also not sure if there are any submeshes. There's uv overlaping

image.png.3063a4a4b2f8bf681089dd2b997e84ed.png

Textures are inside too.

image.thumb.png.54e2cc877d30dbcc78250a7a49e2a0f7.png

Edited by h3x3r

  • Author
On 1/27/2026 at 5:31 AM, h3x3r said:

Textures are inside too.

Hey nice! I have almost zero experience with textures so this is cool to learn.

Here's a side-by-side. Pic on right is dumped from the emulator 🙂

WheelComp.png

  • Supporter

So the palette format is BGRA8888 instead of RGBA8888. Thanks for confirming.

BTW what is your goal? You want whole level or just models?

Well this is everything I know so far about *.msh format. But don't know how to assign material to the mesh. Mesh has offset to the material.

*.mpk also include meshes + textures. Looks similar to the *.msh struct

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i;
uint32 Sign;
uint32 ResourceSize;
uint32 MaterialTableOffset;
uint32 MaterialIndex;
uint32 Unknown_2_Offset;
uint32 Unknown_2_Index;
uint32 MeshTableOffset;
uint32 MeshIndex;
uint32 Unknown_3_Offset;
uint32 Unknown_3_Index;
uint32 TransformOffset;
uint32 TransformIndex;

FSeek(MaterialTableOffset);
struct
{
    uint32 Null[5];
    uint32 Unknown_0;
    uint32 Unknown_1;
    uint32 Unknown_2;
    uint32 RawDataOffset;
    uint32 PaletteDataOffset;
    uint32 Unknown_3;
    uint32 Unknown_4;
    uint32 Unknown_5;
    ubyte Unknown_6;
    ubyte Unknown_7;
    ubyte Unknown_8;
    ubyte Unknown_9;
    uint32 Unknown_10;
    uint32 Unknown_11;
    uint32 Unknown_12;
    uint32 Unknown_13;
    uint32 Unknown_14;
    uint32 Unknown_15;
    uint32 Unknown_16;
    uint32 Unknown_17;
    uint32 Unknown_18;
    uint32 Unknown_19;
    uint32 Unknown_20;
    uint32 TextureWidth;
    uint32 TextureHeight;
    uint32 Unknown_21;
    uint32 HeaderOffset;
    uint32 Unknown_22;
    uint32 Unknown_23;
    uint32 Unknown_24;
}MaterialTable[MaterialIndex]<optimize=false>;


FSeek(MeshTableOffset);
struct
{
    uint32 Unknown_0;
    uint32 MaterialOffset;
    uint32 MeshOffset;
    uint32 Unknown_1;
    uint32 Null[4];
    local uint32 cPos=FTell();
    
    FSeek(MeshOffset);
    struct
    {
        uint32 Null[3];
        uint32 VertexBufferOffset;
        uint32 IndexBufferOffset;
        uint16 VertexCount;
        uint16 IndexCount;
        FSeek(VertexBufferOffset);
        struct
        {
            float VPosX,VPosY,VPosZ;
            uint32 Unknown_0,Unknown_1;
            float UVPosX,UVPosY;
        }VertexBuffer[VertexCount];
        
        FSeek(IndexBufferOffset);
        uint16 IndexBuffer[IndexCount];
    }Mesh;
    FSeek(cPos);
}MeshTable[MeshIndex]<optimize=false>;

And here's Noesis script. UV's are flipped after export. Not sure why Noesis does this, when I preview uv in noesis they are correct. So if you want export check the Flip UV's in export options.

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Medal of Honor Frontline - Mesh", ".msh")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadModel(handle, noepyLoadModel)
   noesis.logPopup()
   return 1
        
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   return 1
        
def noepyLoadModel(data, mdlList):
	bs = NoeBitStream(data)
	baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
	ctx = rapi.rpgCreateContext()
	Underline = "_"

	bs.read(24)
	MeshTableOffset = bs.readUInt()
	MeshIndex = bs.readUInt()

	bs.seek(MeshTableOffset, NOESEEK_ABS)
	for i in range(0, MeshIndex):
	    bs.read(8)
	    MeshOffset = bs.readUInt()
	    bs.read(20)
	    cPos = bs.tell()
        
	    bs.seek(MeshOffset, NOESEEK_ABS)
	    bs.read(12)
	    VertexBufferOffset = bs.readUInt()
	    IndexBufferOffset = bs.readUInt()
	    VertexCount = bs.readUShort()
	    IndexCount = bs.readUShort()
	    MeshIndex = "{:04d}".format(i)

	    bs.seek(VertexBufferOffset, NOESEEK_ABS)
	    VertexBuffer = bs.readBytes(VertexCount * 28)

	    rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 28, 0)
	    rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 28, 20)
                            
	    rapi.rpgSetName(baseName + Underline + MeshIndex)
	    bs.seek(IndexBufferOffset, NOESEEK_ABS)
	    IndexBuffer = bs.readBytes(IndexCount * 2)
	    rapi.rpgCommitTriangles(IndexBuffer, noesis.RPGEODATA_USHORT, IndexCount, noesis.RPGEO_TRIANGLE_STRIP)
	    bs.seek(cPos, NOESEEK_ABS)
	mdl = rapi.rpgConstructModel()
	mdlList.append(mdl)
	return 1

 

 

Edited by h3x3r

  • Author
14 hours ago, h3x3r said:

BTW what is your goal? You want whole level or just models?

Well this is everything I know so far about *.msh format. But don't know how to assign material to the mesh. Mesh has offset to the material.

Thank you so much! That's more than I was expecting. Also saves me from figuring out why my script wasn't working right, lol.
 

Ideally, I'd like to get models (props + characters), maps, animations, textures... sfx and music is easy enough. There are a couple reasons: nostalgia of course, I played this game to death on the ps2 and it's what makes me want to do this. Preservation of the game and its assets. And also modding or possibly fan projects in the future.

Ah, the Dutch town mission 😉

  • Supporter

Well i figured materials for level meshes. I noticed they are using external texture id but still one thing remains, some of them use internal textures and don't know yet where is texture index for them. Like i know it is in material but don't know which value it could be.

  • Author
25 minutes ago, h3x3r said:

Well i figured materials for level meshes. I noticed they are using external texture id but still one thing remains, some of them use internal textures and don't know yet where is texture index for them. Like i know it is in material but don't know which value it could be.

So is everything for the levels in the .cpt? I've taken a peek into the .cdb but not a lot stands out to me at the moment. 

Was able to get textures onto the vehicle in Noesis, just gotta figure out why the glass texture is still not right.

TexTest1.png

  • Supporter

Yes, cpt is level. Anyway good work with assigning textures. Can you show me how you did it?

Here's Noesis for any kind of *.xsh. There may be some more pixel formats but I didn't came across them yet.

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Medal of Honor Frontline - OG XBox Texture", ".xsh")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadRGBA(handle, noepyLoadRGBA)
   noesis.logPopup()
   return 1
        
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   return 1
   
def noepyLoadRGBA(data, texList):
	bs = NoeBitStream(data)
	baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
	bs.readBytes(112)
	PixelFormat = bs.readUByte()
	bs.read(3)
	TextureWidth = bs.readUShort()
	TextureHeight = bs.readUShort()
	bs.read(4)
	MortonFlag = bs.readUInt()
    
	if PixelFormat == 96:
		RawDataBufferSize = TextureWidth * TextureHeight //2
		RawDataBuffer = bs.read(RawDataBufferSize)
		data = rapi.imageDecodeDXT(RawDataBuffer, TextureWidth, TextureHeight, noesis.NOESISTEX_DXT1)
		texFmt = noesis.NOESISTEX_RGBA32
        
	elif PixelFormat == 97:
		RawDataBufferSize = TextureWidth * TextureHeight
		RawDataBuffer = bs.read(RawDataBufferSize)
		data = rapi.imageDecodeDXT(RawDataBuffer, TextureWidth, TextureHeight, noesis.NOESISTEX_DXT3)
		texFmt = noesis.NOESISTEX_RGBA32
                    
	elif PixelFormat == 123:
		RawDataBufferSize = TextureWidth * TextureHeight
		RawDataBuffer = bs.read(RawDataBufferSize)
		bs.seek(data.find(b'\x00\x00\x00\x00\x2A', bs.tell()) + 12) # thanks durik for this function
		PaletteBufferSize = bs.readUInt() * 4
		bs.read(4)
		PaletteBuffer = bs.read(PaletteBufferSize)
		data = rapi.imageDecodeRawPal(RawDataBuffer, PaletteBuffer, TextureWidth, TextureHeight, 8, "b8 g8 r8 a8")
		data = rapi.imageFromMortonOrder(data, TextureWidth, TextureHeight, 4)
		texFmt = noesis.NOESISTEX_RGBA32

	elif PixelFormat == 125:
		RawDataBufferSize = TextureWidth * TextureHeight * 4
		RawDataBuffer = bs.read(RawDataBufferSize)
		data = rapi.imageDecodeRaw(RawDataBuffer, TextureWidth, TextureHeight, "b8 g8 r8 a8")
		if MortonFlag != 0:
		    data = rapi.imageFromMortonOrder(data, TextureWidth, TextureHeight, 4)
		texFmt = noesis.NOESISTEX_RGBA32

	texList.append(NoeTexture(rapi.getInputName(), TextureWidth, TextureHeight, data, texFmt))
	return 1

Also the external textures are in level file in cpt. May be confusing but these are not meshes but textures.

So here's bms to unpack them.

endian little
get BaseFileName basename

get Sign uint32
get ResourceSize uint32
get Unknown_0 uint32
get MaterialSet0_Offset uint32
get MaterialSet0_Index uint32
get MaterialSet1_Offset uint32
get MaterialSet1_Index uint32
get Unknown_5 uint32
get Unknown_6 uint32
get Unknown_7 uint32
get Unknown_8 uint32
get Unknown_9 uint32
get Unknown_10 uint32
get Unknown_11 uint32
get Unknown_12 uint32
get Unknown_13 uint32


goto MaterialSet0_Offset

for i = 0 < MaterialSet0_Index
	getdstring Dummy 0x14
	get Unknown_0 uint32
	get Unknown_1 uint32
	get Unknown_2 uint32
	get RawDataOffset uint32
	get PaletteDataOffset uint32
	get Unknown_3 uint32
	get Unknown_4 uint32
	get Unknown_5 uint32
	get Unknown_6 ubyte
	get Unknown_7 ubyte
	get Unknown_8 ubyte
	get Unknown_9 ubyte
	get Unknown_10 uint32
	get Unknown_11 uint32
	get Unknown_12 uint32
	get Unknown_13 uint32
	get Unknown_14 uint32
	get Unknown_15 uint32
	get Unknown_16 uint32
	get Unknown_17 uint32
	get Unknown_18 uint32
	get Unknown_19 uint32
	get Unknown_20 uint32
	get TextureWidth uint32
	get TextureHeight uint32
	get Unknown_21 uint32
	get HeaderOffset uint32
	get Unknown_22 uint32
	get Unknown_23 uint32
	get ExternalTextureId uint32
	savepos cPos
	goto HeaderOffset
	getdstring Dummy 0x4
	get ResourceSize uint32
	goto cPos
	
	string Name p "%s/MaterialSet0/%04u.xsh" BaseFileName i
	log Name HeaderOffset ResourceSize
next i

goto MaterialSet1_Offset

for i = 0 < MaterialSet1_Index
	getdstring Dummy 0x14
	get Unknown_0 uint32
	get Unknown_1 uint32
	get Unknown_2 uint32
	get RawDataOffset uint32
	get PaletteDataOffset uint32
	get Unknown_3 uint32
	get Unknown_4 uint32
	get Unknown_5 uint32
	get Unknown_6 ubyte
	get Unknown_7 ubyte
	get Unknown_8 ubyte
	get Unknown_9 ubyte
	get Unknown_10 uint32
	get Unknown_11 uint32
	get Unknown_12 uint32
	get Unknown_13 uint32
	get Unknown_14 uint32
	get Unknown_15 uint32
	get Unknown_16 uint32
	get Unknown_17 uint32
	get Unknown_18 uint32
	get Unknown_19 uint32
	get Unknown_20 uint32
	get TextureWidth uint32
	get TextureHeight uint32
	get Unknown_21 uint32
	get HeaderOffset uint32
	get Unknown_22 uint32
	get Unknown_23 uint32
	get ExternalTextureId uint32
	savepos cPos
	goto HeaderOffset
	getdstring Dummy 0x4
	get ResourceSize uint32
	goto cPos
	
	string Name p "%s/MaterialSet1/%04u.xsh" BaseFileName i
	log Name HeaderOffset ResourceSize
next i

 

Edited by h3x3r

  • Author

Still figuring out how to handle that last texture with the transparency. The texture before it is empty so it skips it. But I think they are connected somehow. The jeep model only has 16 textures/materials while having 17 meshes.

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Medal of Honor Frontline - Mesh", ".msh")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadModel(handle, noepyLoadModel)
   noesis.logPopup()
   return 1
   
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   return 1
   
def noepyLoadModel(data, mdlList):
   bs = NoeBitStream(data)
   baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
   ctx = rapi.rpgCreateContext()
   Underline = "_"
   
   matList = []
   texList = []
   
   bs.read(8)
   MatTableOffset = bs.readUInt()
   MatIndex = bs.readUInt()
   bs.read(8)
   MeshTableOffset = bs.readUInt()
   MeshIndex = bs.readUInt()

   bs.seek(MeshTableOffset, NOESEEK_ABS)
   for i in range(0, MeshIndex):
      bs.read(8)
      MeshOffset = bs.readUInt()
      bs.read(20)
      cPos = bs.tell()
      
      bs.seek(MeshOffset, NOESEEK_ABS)
      bs.read(12)
      VertexBufferOffset = bs.readUInt()
      IndexBufferOffset = bs.readUInt()
      VertexCount = bs.readUShort()
      IndexCount = bs.readUShort()
      MeshIndex = "{:04d}".format(i)

      bs.seek(VertexBufferOffset, NOESEEK_ABS)
      VertexBuffer = bs.readBytes(VertexCount * 28)
      
      rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 28, 0)
      rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 28, 20)
      rapi.rpgSetName(baseName + Underline + MeshIndex)
      
      bs.seek(MatTableOffset, NOESEEK_ABS)
      bs.read(32)
      pixelOffset = bs.readUInt()
      paletteOffset = bs.readUInt()
      bs.read(60)
      width = bs.readUInt()
      height = bs.readUInt()
      print(width, height, width * height)
      bs.read(20)
      MatTableOffset = bs.tell()
      
      if width > 0 and height > 0:
         bs.seek(paletteOffset, NOESEEK_ABS)
         print("Palette Offset: " + hex(bs.tell()))
         bs.read(68)
         paletteSize = bs.readUInt()
         bs.seek(paletteOffset, NOESEEK_ABS)
         rawPal = bs.readBytes(paletteSize)
         
         bs.seek(pixelOffset, NOESEEK_ABS)
         pixelData = bs.readBytes(width * height)
         pixelData = rapi.imageFromMortonOrder(pixelData, width, height, 1)
         texData = rapi.imageDecodeRawPal(pixelData, rawPal, width, height, 8, "b8g8r8a8")
         
         texName = "tex_{}".format(i)
         texture = NoeTexture(texName, width, height, texData, noesis.NOESISTEX_RGBA32)
         texList.append(texture)
         
         mat = NoeMaterial(texName, texName)
         matList.append(mat)
         rapi.rpgSetMaterial(texName)
      else:
         print("Skipping empty texture slot at index", i)
         print("Offset: " + hex(bs.tell()))
         rapi.rpgSetMaterial("") # set blank material
      
      bs.seek(IndexBufferOffset, NOESEEK_ABS)
      IndexBuffer = bs.readBytes(IndexCount * 2)
      rapi.rpgCommitTriangles(IndexBuffer, noesis.RPGEODATA_USHORT, IndexCount, noesis.RPGEO_TRIANGLE_STRIP)
      bs.seek(cPos, NOESEEK_ABS)
   mdl = rapi.rpgConstructModel()
   
   mdl.setModelMaterials(NoeModelMaterials(texList, matList))
   
   mdlList.append(mdl)
   return 1

 

  • Supporter

BTW did you figure out what's goin on with comp.viv which has C0 FB signature? Can't see any value representing offset/size. Maybe it's converted. Normaly when signature is BIGF there is uint32 Offset and uint32 Size and string Filename. But this one has only 6 bytes and then string as Filename.

EDiT: Well i found something. The data type is uint16 and is multiplied by 256 but for the first file is wrong file strats at 384 bytes and I get 256.

Damn when file count change the value too I am lost here.... At least file size is written right after 4 byte sign. Maybe use findloc function solve this crap.

image.thumb.png.6348caed9cde2591b8317fc4f2f3b90a.png

Here's 2 samples if anyone want to take a look. I bet DKDave would crack this...

 

viv.7z

The cdb = most likely collision database

image.thumb.png.0107564a09d9592cf3de56a5e242a541.png

Edited by h3x3r

  • Author
4 hours ago, h3x3r said:

BTW did you figure out what's goin on with comp.viv which has C0 FB signature? Can't see any value representing offset/size. Maybe it's converted. Normaly when signature is BIGF there is uint32 Offset and uint32 Size and string Filename. But this one has only 6 bytes and then string as Filename.

EDiT: Well i found something. The data type is uint16 and is multiplied by 256 but for the first file is wrong file strats at 384 bytes and I get 256.

Hmm, isn't it just absolute offsets from file start? Apologies if I misunderstand. I'll post a pic of what seems to work for me.

A while ago when I first saw C0FB, I thought it was some type of compression; now I think it's just a different way Electronic Arts packed some of their game archives. There is a modding program for some of the SSX (snowboarding) games that can open BIG/BIGF/C0FB files (it doesn't seem to unpack these well, however). It is strange that it is 6 bytes + string for each file... So big endian, offset 0x06 = 0x000180, go to offset 0x180 for file start, offset 0x09 = 0x063064 for 405,604 bytes in length. Then some alignment padding before the next file starts at 0x63200, etc.

Looking at it now I see that 0x000E is indeed the file count, lol. I have it as "unknown" in my earlier pattern in the pic.

bW5I8iY.png

 

Edited by MrGravey
Link better quality pic

  • Supporter

Hmm now i see. u24 data type. 010 doesn't know that what a shame and it's paid pro software....

Even quick bms know it... ufff

Thanks for clearing this out.

Here's BMS for viv with C0 FB sign.

endian big
get BaseFileName basename

getDString Dummy 0x4
get Resources uint16

for i = 0 < Resources
	get ResourceOffset threebyte
	get ResourceSize threebyte
	get ResourceName string
	string Name p "%s/%s" BaseFileName ResourceName
	log Name ResourceOffset ResourceSize
next i

 

Edited by h3x3r

  • Author
On 1/29/2026 at 2:39 PM, h3x3r said:

Here's BMS for viv with C0 FB sign.

Thanks, I'll add this to my collection 🙂 Also I've realized that my solution to get the jeep textures in Noesis ONLY works for the jeep model so I have some troubleshooting to do. I think something weird with getting palette data.

Also been working on the sequel, MoH: Rising Sun, and have had some success but I'm having trouble finding mesh table/offsets. The game was released just over a year later but the formats are slightly different. I can get the models and sub meshes manually but I'd rather have a script. Mind taking a look? I'll send a couple examples. The data blocks so far seems to be:

Header?
Face index
Vertex buffer
UV buffer
Unknown
Unknown
Unknown
Header? *Includes file name

Then it repeats. I've taken a look at the bottom of the files but I can't seem to make sense of it. I've attached some troublesome files.

0i2BtIs.png

MoH_RS_testing.zip

  • Supporter

About palette. Seek paletteOffset and seek another 8 bytes back and read uint32 and miltiply it by 4 and you'll get palette size.

You can do this i think: 

bs.seek(paletteOffset - 8, NOESEEK_ABS)

paletteSize = bs.readUInt() * 4

go back to paletteOffset and read palette buffer.

rawPal = bs.readBytes(paletteSize)

Edited by h3x3r

  • Author
6 hours ago, h3x3r said:

About palette. Seek paletteOffset and seek another 8 bytes back and read uint32 and miltiply it by 4 and you'll get palette size.

You can do this i think: 

bs.seek(paletteOffset - 8, NOESEEK_ABS)

paletteSize = bs.readUInt() * 4

go back to paletteOffset and read palette buffer.

rawPal = bs.readBytes(paletteSize)

Ah I see it... fixed! Why is a multiple of 4 used, though? Either way, there are textures that do not use palette data so that's the next thing I'm looking at. I also think it is BGRX instead of BGRA, will do more testing.

Edited by MrGravey
added info

  • Supporter

I briefly checked all formats you provided but it doesn't make much sense. Can't find damn thing... The serialization of these is really wild. Wiki says it runs on Unreal Engine.

  • Author
4 hours ago, h3x3r said:

I briefly checked all formats you provided but it doesn't make much sense. Can't find damn thing... The serialization of these is really wild. Wiki says it runs on Unreal Engine.

I recently found this resource: EA SSH FSH Image (Type 1) - Reverse Engineering Wiki

In the process of creating a bms script to sort these files.

  • 2 months later...

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.