Jump to content

Psi-Ops: The Mindgate Conspiracy (PC) - .w32 format (also tools to read and write the extracted formats and rebuild the container)


Recommended Posts

Posted

Psi-Ops is a short experience with an irrelevant story that I think would be sweet to make quick character mods for, especially since the game comes with unlockable outfits and characters for campaign use with mechanical changes compared to the base character. However, from what I understand it was built using a modified UE2 engine and no one really knows a whole lot about the formats.  The game was actually released for free on PC, and after its parent company also ceased to be. Thus it should be accessible without any potential legal issue here https://www.myabandonware.com/game/psi-ops-the-mindgate-conspiracy-dvc

Watto's Game Extractor can seemingly extract data from the .w32 archives (which appear to contain the desired character data), and I asked Watto himself to take a look at "MAINNICK.w32" in the included attachment and he had this to say, 
 

Quote

 

When looking at the MESH files, for example, we can see what appears to be a list of face indexes, but there's no vertexes in the file. We couldn't find a matching file where the vertexes were located.

For the TEX files, they are really small and look like they just contain metadata for an image (width, height, format, ...). We assume maybe the texture data is stored in another file like the TBX file, however we couldn't find anywhere in the TEX that tells us where the image data is stored in the TBX.

This game looks like it has lots of small pieces that need to fit together, and we weren't able to really figure out some of these fundamental issues, so think it might be better with someone else.

The best option is probably to ask at https://reshax.com/ as there are people there who can help with all kinds of files (especially texture and model files).

 

Thus, I am now here! Inside of the "MAINNICK.w32" appears to be:
.smsh (skinned mesh)
.skel (skeleton)
.tbx (texture) //guessing it's only a diffuse texture, game probably doesn't have more than that lol
.tex (material)
.strs (???)
.vbx (???) //referred to as a "global vertex buffer"

I also included MAINPK, as she's one of the unlockable skins, just incase there's potential variation as the aformentioned character is the base skin, which allows cutscenes and hitgrunts. 
In addition, i've also included what appears to be the games general containers (divided by language given the final character) which includes .mesh files, which I'm assuming is static mesh.

Let me know if anyone is willing to make .bms and/or noesis import/export scripts (anything that would allow me to make character replacer mods) I'd be super grateful!

Psi-Ops W32s.zip

Posted (edited)

Oooh! Thank you for taking a look at this! Ahh that looks right. The game has no face posing, so nothing to worry about in that department. I'm guessing it's separated by mesh groups? fascinating. Then workflow will then consist of finding the location of the segments, maybe? Are the vertex skins just embedded in the mesh as normal? I'm super inexperienced with RE tools and I'm a 3D artist rather than a programmer, but is there anything I can do to aid the process?

Edited by Gagnetar
reduced keyword density
  • Engineers
Posted (edited)

Didn't check for "mesh groups", just the next face index block (FI block). Goes like so:

mainnick.w32 (see advice in appended picture)

As a start you'll need to load said w32 into hex2obj and enter the parameters from the picture in my previous post.

Then proceed as follows:

Quote

check address from picture in previous post, lower left window

0x522f0 endOf first FI block   (04A00100, some signature?)

next FI block: 0x52300 to 533C6, delta 0x10C6 -> 4294 dec.
div 2= 2147 (word FIs), FI count

2C4803 (some signature?)

 

 

mainnick_sub_mesh_2.png

Edited by shak-otay
  • Engineers
Posted (edited)

Here's Noesis script for textures.

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Psi Ops - Texture", ".w32")
   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.read(20)
    ResourceTableOffset = bs.readUInt()
    bs.read(12)
    StringTableOffset = bs.readUInt()

    bs.seek(ResourceTableOffset, NOESEEK_ABS)
    ResourceCount = bs.readUInt()
    
    for i in range(0, ResourceCount):
        Extension = bs.readUInt()
        Unknown_0 = bs.readUInt()
        ResourceSize = bs.readUInt()
        ResourceNameOffset = bs.readUInt()
        cPos_0 = bs.tell()
        bs.seek(StringTableOffset + ResourceNameOffset, NOESEEK_ABS)
        ResourceName = bs.readString()
        bs.seek(cPos_0, NOESEEK_ABS)
        ResourceOffset = bs.readUInt()
        Unknown_1 = bs.readUInt()
        cPos_1 = bs.tell()
        
        if Extension == 544761204:
            bs.seek(ResourceOffset, NOESEEK_ABS)
            TextureWidth = bs.readUInt()
            TextureHeight = bs.readUInt()
            RawDataSize = bs.readUInt() -20
            Unknown_0 = bs.readUInt()
            BufferInfoOffset = bs.readUInt()
            Unknown_1 = bs.readUInt()
            MipMap = bs.readUInt()
            Unknown_2 = bs.readUInt()
            PixelFormat = bs.readUInt()
            Unknown_3 = bs.readUInt()
            RawDataOffset = bs.readUInt()
            bs.seek(RawDataOffset, NOESEEK_ABS)
            TextureBuffer = bs.read(RawDataSize)
            bs.seek(cPos_1, NOESEEK_ABS)
            
            if PixelFormat == 19:
                print("Pixel Format > R8")
            elif PixelFormat == 12:
                print("Pixel Format > DXT1")
            elif PixelFormat == 14:
                print("Pixel Format > DXT3")
            elif PixelFormat == 15:
                print("Pixel Format > DXT5")
            elif PixelFormat == 18:
                print("Pixel Format > RGBA8")
            else:
                print("Unknown Pixel Format > ",PixelFormat)

            if PixelFormat == 12:
                texFmt = noesis.NOESISTEX_DXT1
            elif PixelFormat == 14:
                texFmt = noesis.NOESISTEX_DXT3
            elif PixelFormat == 15:
                texFmt = noesis.NOESISTEX_DXT5
            elif PixelFormat == 18:
                texFmt = noesis.NOESISTEX_RGBA32
            elif PixelFormat == 19:
                TextureBuffer = rapi.imageDecodeRaw(TextureBuffer, TextureWidth, TextureHeight, "b0 g0 r8 a0")
                texFmt = noesis.NOESISTEX_RGBA32
            texList.append(NoeTexture(ResourceName, TextureWidth, TextureHeight, TextureBuffer, texFmt))
    return 1

 

Edited by h3x3r
  • Like 2
Posted
9 hours ago, h3x3r said:

Here's Noesis script for textures.

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Psi Ops - Texture", ".w32")
   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.read(20)
    ResourceTableOffset = bs.readUInt()
    bs.read(12)
    StringTableOffset = bs.readUInt()

    bs.seek(ResourceTableOffset, NOESEEK_ABS)
    ResourceCount = bs.readUInt()
    
    for i in range(0, ResourceCount):
        Extension = bs.readUInt()
        Unknown_0 = bs.readUInt()
        ResourceSize = bs.readUInt()
        ResourceNameOffset = bs.readUInt()
        cPos_0 = bs.tell()
        bs.seek(StringTableOffset + ResourceNameOffset, NOESEEK_ABS)
        ResourceName = bs.readString()
        bs.seek(cPos_0, NOESEEK_ABS)
        ResourceOffset = bs.readUInt()
        Unknown_1 = bs.readUInt()
        cPos_1 = bs.tell()
        
        if Extension == 544761204:
            bs.seek(ResourceOffset, NOESEEK_ABS)
            TextureWidth = bs.readUInt()
            TextureHeight = bs.readUInt()
            RawDataSize = bs.readUInt() -20
            Unknown_0 = bs.readUInt()
            BufferInfoOffset = bs.readUInt()
            Unknown_1 = bs.readUInt()
            MipMap = bs.readUInt()
            Unknown_2 = bs.readUInt()
            PixelFormat = bs.readUInt()
            Unknown_3 = bs.readUInt()
            RawDataOffset = bs.readUInt()
            bs.seek(RawDataOffset, NOESEEK_ABS)
            TextureBuffer = bs.read(RawDataSize)
            bs.seek(cPos_1, NOESEEK_ABS)
            
            if PixelFormat == 19:
                print("Pixel Format > R8")
            elif PixelFormat == 12:
                print("Pixel Format > DXT1")
            elif PixelFormat == 14:
                print("Pixel Format > DXT3")
            elif PixelFormat == 15:
                print("Pixel Format > DXT5")
            elif PixelFormat == 18:
                print("Pixel Format > RGBA8")
            else:
                print("Unknown Pixel Format > ",PixelFormat)

            if PixelFormat == 12:
                texFmt = noesis.NOESISTEX_DXT1
            elif PixelFormat == 14:
                texFmt = noesis.NOESISTEX_DXT3
            elif PixelFormat == 15:
                texFmt = noesis.NOESISTEX_DXT5
            elif PixelFormat == 18:
                texFmt = noesis.NOESISTEX_RGBA32
            elif PixelFormat == 19:
                TextureBuffer = rapi.imageDecodeRaw(TextureBuffer, TextureWidth, TextureHeight, "b0 g0 r8 a0")
                texFmt = noesis.NOESISTEX_RGBA32
            texList.append(NoeTexture(ResourceName, TextureWidth, TextureHeight, TextureBuffer, texFmt))
    return 1

 

Ooh! textures! I was really stoked to see this. I'm guessing the script probably needs some additional pointer information?
It spits out this error tried to export the texture and gives a similar error trying to open it.

image.png

Posted
9 hours ago, h3x3r said:

I tested it on all files you provided and it works. So it must be the Noesis. Try to update it.

Thanks! that was in fact the issue. Apologies for my ignorance. I know you guys do this stuff regularly but I'm always super impressed seeing how these little scripts turn the seemingly complete gibberish of a an obscure game format into plainly readable files.

Do you know how make something to dump the .w32 and create another? 

Posted
On 6/22/2025 at 1:31 AM, shak-otay said:

Didn't check for "mesh groups", just the next face index block (FI block). Goes like so:

mainnick.w32 (see advice in appended picture)

As a start you'll need to load said w32 into hex2obj and enter the parameters from the picture in my previous post.

Then proceed as follows:

 

mainnick_sub_mesh_2.png

I remember seeing your old tool on the forums before they exploded! Tracked down your newer version and finally had time to dig through it today. Following your notes, pictures and values I managed to get the stuff you had to show up. The head group seems to have a missing triangle on the head? not sure if that's a mesh viewer thing or not, maybe the normal is just backwards there. Far as I can tell the next start address is 533E0 -> 53AD8. I tried my best to figure out the vert start address but to no avail. I believe I found some kind of ballpark range though, it seems like the next face index group is the torso, perhaps it separated by shirt and pants?

Armor.png

Head.png

Torso.png

psiops_040804_000.jpg

Posted

So I'm curious if I could commission you guys to make the mod tools? I'm not picky as to who does what or how many people are needed for the task. They could be blender, noesis, maya or just BMS scripts/individual mod tools.

I've got Paypal (I'm based in the USA), if you've got a RE category (or categories) you particularly excel at and are willing to make the tools for me, just name a price and what you're willing to make!

  • Engineers
Posted (edited)
On 6/21/2025 at 6:10 AM, Gagnetar said:

However, from what I understand it was built using a modified UE2 engine and no one really knows a whole lot about the formats.

Where did you get that? I read on wiki it's custom made + havok physics.

Anyway structure of w32 is a bit pain to reverse. I only reversed file table section so far. There are also some unknown data on the start of file sometimes... Not sure where are defined.

Also found this in exe dump.

Spoiler

05051979
cheat_unlock_arcade
07041979
cheat_unlock_coop
7734206
cheat_unlock_survival
465486
cheat_unlock_darkMode
564689
cheat_unlock_trainingNick
484646
cheat_unlock_urbanNick
975466
cheat_unlock_wastelandNick
456498
cheat_unlock_stealthNick
135488
cheat_unlock_sara
468799
cheat_unlock_psiSara
678999
cheat_unlock_tonya
231644
cheat_unlock_suicideSara
489788
cheat_unlock_marlena
136876
cheat_unlock_fetishPyro
135454
cheat_unlock_bikiniPyro
65496873
cheat_unlock_saranae
459797
cheat_unlock_general
431644
cheat_unlock_clownGeneral
497878
cheat_unlock_barrett
196001
cheat_unlock_trainingBarret1
196002
cheat_unlock_trainingBarret2
196003
cheat_unlock_trainingBarret3
196004
cheat_unlock_trainingBarret4
196005
cheat_unlock_trainingBarret5
196006
cheat_unlock_trainingBarret6
468987
cheat_unlock_jov
231324
cheat_unlock_weiLu
978789
cheat_unlock_dragonWeiLu
654654
cheat_unlock_tranquilityWeiLu
321646
cheat_unlock_MP1
698799
cheat_unlock_MP2
654659
cheat_unlock_MP3
698798
cheat_unlock_jack
365498
cheat_unlock_unSoldier
454566
cheat_unlock_burnedSoldier
998789
cheat_unlock_labcoat
364654
cheat_unlock_dockworker
978798
cheat_unlock_komiko
546546
cheat_unlock_scorpion
05120926
cheat_unlock_pitfall
76635766
cheat_unlock_panicRoom
020615
cheat_unlock_upAndOver
945678
cheat_unlock_stopLights
9442662
cheat_unlock_gasoline
154897
cheat_unlock_bottomlessPit
090702
cheat_unlock_tkAlley
154684
cheat_unlock_gearGauntlet
428584
cheat_unlock_tipTheBuddha
565485
cheat_unlock_psiPool
659785
cheat_unlock_auraPool
568789
cheat_unlock_bouncyBouncy
456878
cheat_unlock_gnomotron

 

Edited by h3x3r
Posted
13 minutes ago, h3x3r said:

Where did you get that? I read on wiki it's custom made + havok physics.

Anyway structure of w32 is a bit pain to reverse. I only reversed file table section so far. There are also some unknown data on the start of file sometimes... Not sure where are defined.

Also found this in exe dump.

  Hide contents

05051979
cheat_unlock_arcade
07041979
cheat_unlock_coop
7734206
cheat_unlock_survival
465486
cheat_unlock_darkMode
564689
cheat_unlock_trainingNick
484646
cheat_unlock_urbanNick
975466
cheat_unlock_wastelandNick
456498
cheat_unlock_stealthNick
135488
cheat_unlock_sara
468799
cheat_unlock_psiSara
678999
cheat_unlock_tonya
231644
cheat_unlock_suicideSara
489788
cheat_unlock_marlena
136876
cheat_unlock_fetishPyro
135454
cheat_unlock_bikiniPyro
65496873
cheat_unlock_saranae
459797
cheat_unlock_general
431644
cheat_unlock_clownGeneral
497878
cheat_unlock_barrett
196001
cheat_unlock_trainingBarret1
196002
cheat_unlock_trainingBarret2
196003
cheat_unlock_trainingBarret3
196004
cheat_unlock_trainingBarret4
196005
cheat_unlock_trainingBarret5
196006
cheat_unlock_trainingBarret6
468987
cheat_unlock_jov
231324
cheat_unlock_weiLu
978789
cheat_unlock_dragonWeiLu
654654
cheat_unlock_tranquilityWeiLu
321646
cheat_unlock_MP1
698799
cheat_unlock_MP2
654659
cheat_unlock_MP3
698798
cheat_unlock_jack
365498
cheat_unlock_unSoldier
454566
cheat_unlock_burnedSoldier
998789
cheat_unlock_labcoat
364654
cheat_unlock_dockworker
978798
cheat_unlock_komiko
546546
cheat_unlock_scorpion
05120926
cheat_unlock_pitfall
76635766
cheat_unlock_panicRoom
020615
cheat_unlock_upAndOver
945678
cheat_unlock_stopLights
9442662
cheat_unlock_gasoline
154897
cheat_unlock_bottomlessPit
090702
cheat_unlock_tkAlley
154684
cheat_unlock_gearGauntlet
428584
cheat_unlock_tipTheBuddha
565485
cheat_unlock_psiPool
659785
cheat_unlock_auraPool
568789
cheat_unlock_bouncyBouncy
456878
cheat_unlock_gnomotron

 

Good question honestly, Google may just be making this up as I was unable to locate the source. They act like they built it from scratch as you found, which would make more sense considering if it really was UE2 something would already have been able to extract it.

Oh sweet! Guessing you're poking around with Ghidra? Think the .exe will reveal some information about the .w32? It boasts an in-game cheat menu, so as you can imagine that's what those are for.

I apologize about the difficulty, I hope it doesn't get much harder. I get really happy everytime I see you guys working hard on this, so I just wanted to say thank you again!

Psi-Ops_Engine.png

  • Engineers
Posted

Well I was wrong with unknown data at the start of file. I noticed there is offset for it in table so repacking is possible... But not sure how to repack texture/vertex buffer if we change something in it.

Anyway I am not working with any disassemblers/debugges, just simple string dump in hex editor. This is above my skill....

Posted
29 minutes ago, h3x3r said:

Well I was wrong with unknown data at the start of file. I noticed there is offset for it in table so repacking is possible... But not sure how to repack texture/vertex buffer if we change something in it.

Anyway I am not working with any disassemblers/debugges, just simple string dump in hex editor. This is above my skill....

Hmm. Would it be easier to just extract the whole .w32 and then make another one, rather than try and replace stuff in an already existing one? If the table "names" are the same I'd imagine it'd still work? 

  • Engineers
Posted (edited)

The logic behind it is not easy. The more i research it the more i am worried...

I will post some findings later...

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

local uint32 i,j,k,l;
struct
{
    char Sign[4];
    uint32 Version;
    uint32 Unknown_0;
    uint32 ResourceTypeInfoOffset;
    uint32 ResourceTypeInfoSize;
    uint32 ResourceInfoOffset;
    uint32 ResourceInfoSize;
    uint32 UnknownTableOffset;
    uint32 UnknownTableSize;
    uint32 StringTableOffset;
    uint32 StringTableSize;
    uint32 Null;
}Header;

FSeek(Header.StringTableOffset);
byte StringTableData[Header.StringTableSize];

FSeek(Header.ResourceTypeInfoOffset);
struct
{
    uint32 ResourceTypeCount;
    struct
    {
        char ExtensionName[4];
        uint32 ResourceCount;
        uint32 Unknown;
        uint32 ResourceTableOffset;
    }ResourceTypeInfo[ResourceTypeCount]<optimize=false>;
}ResourceTypeTable;

FSeek(Header.ResourceInfoOffset);
struct
{
    uint32 TotalResourceCount;
    
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        FSeek(Header.ResourceInfoOffset + ResourceTypeTable.ResourceTypeInfo[i].ResourceTableOffset);
        struct 
        {
            char Extension[4];
            uint32 ResourceType;
            uint32 ResourceSize;
            uint32 ResourceNameOffset;
            local uint32 cPos=FTell();
            FSeek(Header.StringTableOffset + ResourceNameOffset);
            string ResourceName;
            FSeek(cPos);
            uint32 ResourceOffset;
            uint32 Unknown;
        }ResourceInfo[ResourceTypeTable.ResourceTypeInfo[i].ResourceCount]<optimize=false>;
    }
    struct
    {
        char Extension[4];
    }ExtensionBuffer[ResourceTypeTable.ResourceTypeCount]<optimize=false>;
}ResourceTable;

FSeek(Header.UnknownTableOffset);
byte UnknownTableData[Header.UnknownTableSize];

    

Also each table + resources are aligned.

Edited by h3x3r
  • Thanks 1
Posted
21 hours ago, h3x3r said:

The logic behind it is not easy. The more i research it the more i am worried...

I will post some findings later...

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

local uint32 i,j,k,l;
struct
{
    char Sign[4];
    uint32 Version;
    uint32 Unknown_0;
    uint32 ResourceTypeInfoOffset;
    uint32 ResourceTypeInfoSize;
    uint32 ResourceInfoOffset;
    uint32 ResourceInfoSize;
    uint32 UnknownTableOffset;
    uint32 UnknownTableSize;
    uint32 StringTableOffset;
    uint32 StringTableSize;
    uint32 Null;
}Header;

FSeek(Header.StringTableOffset);
byte StringTableData[Header.StringTableSize];

FSeek(Header.ResourceTypeInfoOffset);
struct
{
    uint32 ResourceTypeCount;
    struct
    {
        char ExtensionName[4];
        uint32 ResourceCount;
        uint32 Unknown;
        uint32 ResourceTableOffset;
    }ResourceTypeInfo[ResourceTypeCount]<optimize=false>;
}ResourceTypeTable;

FSeek(Header.ResourceInfoOffset);
struct
{
    uint32 TotalResourceCount;
    
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        FSeek(Header.ResourceInfoOffset + ResourceTypeTable.ResourceTypeInfo[i].ResourceTableOffset);
        struct 
        {
            char Extension[4];
            uint32 ResourceType;
            uint32 ResourceSize;
            uint32 ResourceNameOffset;
            local uint32 cPos=FTell();
            FSeek(Header.StringTableOffset + ResourceNameOffset);
            string ResourceName;
            FSeek(cPos);
            uint32 ResourceOffset;
            uint32 Unknown;
        }ResourceInfo[ResourceTypeTable.ResourceTypeInfo[i].ResourceCount]<optimize=false>;
    }
    struct
    {
        char Extension[4];
    }ExtensionBuffer[ResourceTypeTable.ResourceTypeCount]<optimize=false>;
}ResourceTable;

FSeek(Header.UnknownTableOffset);
byte UnknownTableData[Header.UnknownTableSize];

    

Also each table + resources are aligned.

Woo! You can do it! I believe in you! I'll see if I can find anyone to help you out, Is there a particular skill/program experience I should be looking for?

  • Engineers
Posted (edited)

You need someone who excel in visual studio. But that's not ends here... This game load almost everything from w32 containers. And I don't think it can load loose/unpacked files.

For example file type "tex " is just 48 bytes info which leads to the another file called "tbX " or as a file name "XBOX_global_texture_buffer" which holds raw data of texture described in "tex " file.

And I believe that same things happen to a model.

There is "vbX " or as a file name "XBOX_global_vertex_buffer" which consist vertices, uv and maybe more, but not sure yet.

Another file "mesh" holds info about buffer offset + count in "vbX " + include face index. Not reversed yet.

Edited by h3x3r
Posted
On 7/5/2025 at 10:22 AM, h3x3r said:

You need someone who excel in visual studio. But that's not ends here... This game load almost everything from w32 containers. And I don't think it can load loose/unpacked files.

For example file type "tex " is just 48 bytes info which leads to the another file called "tbX " or as a file name "XBOX_global_texture_buffer" which holds raw data of texture described in "tex " file.

And I believe that same things happen to a model.

There is "vbX " or as a file name "XBOX_global_vertex_buffer" which consist vertices, uv and maybe more, but not sure yet.

Another file "mesh" holds info about buffer offset + count in "vbX " + include face index. Not reversed yet.

Hmm, I've checked reddit and the discord and it seems there's no interest. I'll poke around again later today and hope I can either figure something out or get someone who does. 
Maybe I'll check out those coding assistance websites I saw in more detail, with luck there's someone with RE experience that can help you out.
I'm sure you've been busy, but I just figured I'd check in! How are you feeling?

  • Engineers
Posted (edited)

Hey, I am fine. Here's at least SkinnedMesh parser. I have also made Noesis script if you care, but without skin/skeleton, I am not good at it. But 010 Template covered it.

There are also some downsides. I don't know how to read both texture and model in one script. So you must first use script for textures, export them and remove script from plugins\python folder and use this one for a model.

Spoiler
from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Psi Ops - Geometry", ".w32")
   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()
	# Lists
	ExtensionTypeList        = []
	ResourceCountList        = []
	ResourceTableOffsetList  = []
	ResourceOffsetList       = []
	ResourceNameList         = []
	MaterialInfoOffsetList   = []
	StrideSizeList           = []
	MaterialIndexList        = []
	IndexCountList           = []
	VertexBufferSizeList     = []
	VertexBufferOffsetList   = []
	IndexOffsetList          = []
	ShapeBoneIndexList       = []
	ShapeBoneIndexOffsetList = []
	_ = []

	Underline = "_"

    # Header Info
	Sign                   = bs.readUInt()
	Version                = bs.readUInt()
	Unknown_0              = bs.readUInt()
	ResourceTypeInfoOffset = bs.readUInt()
	ResourceTypeInfoSize   = bs.readUInt()
	ResourceInfoOffset     = bs.readUInt()
	ResourceInfoSize       = bs.readUInt()
	UnknownTableOffset     = bs.readUInt()
	UnknownTableSize       = bs.readUInt()
	StringTableOffset      = bs.readUInt()
	StringTableSize        = bs.readUInt()
	Null                   = bs.readUInt()
	# Header Info End
	
	bs.seek(ResourceTypeInfoOffset, NOESEEK_ABS)
    # Resource Type Table
	ResourceTypeCount = bs.readUInt()
	for i in range(0, ResourceTypeCount):
	    ExtensionTypeList.append(bs.readUInt())
	    ResourceCountList.append(bs.readUInt())
	    Unknown = bs.readUInt()
	    ResourceTableOffsetList.append(bs.readUInt())
	
	for i in range(0, ResourceTypeCount):
	    ExtensionType       = ExtensionTypeList[i]
	    ResourceCount       = ResourceCountList[i]
	    ResourceTableOffset = ResourceTableOffsetList[i]
	    if ExtensionType == 1752395123:
	        bs.seek(ResourceInfoOffset + ResourceTableOffset, NOESEEK_ABS)
	        for j in range(0, ResourceCount):
	            Extension          = bs.readUInt()
	            ResourceType       = bs.readUInt()
	            ResourceSize       = bs.readUInt()
	            ResourceNameOffset = bs.readUInt()
	            ResourceOffsetList.append(bs.readUInt())
	            Unknown            = bs.readUInt()
	            EndOfInfo          = bs.tell()
	            bs.seek(StringTableOffset + ResourceNameOffset, NOESEEK_ABS)
	            ResourceNameList.append(bs.readString())
	            bs.seek(EndOfInfo, NOESEEK_ABS)

	        for j in range(0, ResourceCount):
	            ResourceOffset      = ResourceOffsetList[j]
	            ResourceName        = ResourceNameList[j]
	            bs.seek(ResourceOffset, NOESEEK_ABS)
	            BBox                = bs.read(24)
	            MaterialTableOffset = bs.readUInt()
	            ShapeCount          = bs.readUInt()
	            ShapeTableOffset    = bs.readUInt()
	            SkeletonOffset      = bs.readUInt()
	            Unknown             = bs.readUInt()

	            bs.seek(MaterialTableOffset, NOESEEK_ABS)
	            MaterialCount       = bs.readUInt()
	            for k in range(0, MaterialCount):
	                MaterialInfoOffsetList.append(bs.readUInt())
                    
	            bs.seek(ShapeTableOffset, NOESEEK_ABS)    
	            for k in range(0, ShapeCount):
	                Null = bs.readUInt()
	                StrideSizeList.append(bs.readUInt())
	                MaterialIndexList.append(bs.readUInt())
	                Unknown_0 = bs.readUInt()
	                IndexCountList.append(bs.readUInt())
	                VertexBufferSizeList.append(bs.readUInt())
	                VertexBufferOffsetPointer = bs.readUInt()
	                VertexBufferOffsetPointerPos = bs.tell()
	                bs.seek(VertexBufferOffsetPointer + 12, NOESEEK_ABS)
	                VertexBufferOffsetList.append(bs.readUInt())
	                bs.seek(VertexBufferOffsetPointerPos, NOESEEK_ABS)
	                IndexOffsetList.append(bs.readUInt())
	                Unknown_1 = bs.readUInt()
	                ShapeBoneIndexList.append(bs.readUInt())
	                ShapeBoneIndexOffsetList.append(bs.readUInt())

	            for k in range(0, ShapeCount):
	                ShapeIdDigfmt = "{:04d}".format(k)
	                MaterialInfoOffset = MaterialInfoOffsetList[k]
	                StrideSize = StrideSizeList[k]
	                MaterialIndex = MaterialIndexList[k]
	                IndexCount = IndexCountList[k]
	                VertexBufferSize = VertexBufferSizeList[k]
	                VertexBufferOffset = VertexBufferOffsetList[k]
	                IndexOffset = IndexOffsetList[k]
	                ShapeBoneIndex = ShapeBoneIndexList[k]
	                ShapeBoneIndexOffset = ShapeBoneIndexOffsetList[k]
                    
	                bs.seek(MaterialInfoOffset, NOESEEK_ABS)
	                MaterialNameOffset = bs.readUInt()
	                ShaderNameOffset = bs.readUInt()
	                bs.read(28)
	                Unknown = bs.readUInt()
	                MaterialTextureInfoOffset = bs.readUInt()
	                TextureNameOffset = bs.readUInt()
	                bs.seek(MaterialNameOffset, NOESEEK_ABS)
	                MaterialName = bs.readString()
	                bs.seek(TextureNameOffset, NOESEEK_ABS)
	                TextureName = bs.readString()

	                bs.seek(VertexBufferOffset, NOESEEK_ABS)
	                VertexBuffer = bs.read(VertexBufferSize)
	                if StrideSize == 56:
	                    rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 0)
	                    rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 12)
	                    rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 48)
	                    rapi.rpgBindColorBufferOfs(VertexBuffer, noesis.RPGEODATA_BYTE, StrideSize, 24, 4)
                        

	                bs.seek(IndexOffset, NOESEEK_ABS)
	                IndexBuffer = bs.read(IndexCount * 2)
	                rapi.rpgSetMaterial(TextureName)
	                rapi.rpgSetName(ResourceName + Underline + ShapeIdDigfmt)
	                rapi.rpgCommitTriangles(IndexBuffer, noesis.RPGEODATA_USHORT, IndexCount, noesis.RPGEO_TRIANGLE_STRIP)

	mdl = rapi.rpgConstructModel()
	mdlList.append(mdl)
	return 1

 

Also script for textures always output first file with incorrect file name. Don't know how to fix it.

image.png.feca5a0d1bede607308ca1d5348712bf.png

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

local uint32 i,j,k,l;
struct
{
    char Sign[4];
    uint32 Version;
    uint32 Unknown_0;
    uint32 ResourceTypeInfoOffset;
    uint32 ResourceTypeInfoSize;
    uint32 ResourceInfoOffset;
    uint32 ResourceInfoSize;
    uint32 UnknownTableOffset;
    uint32 UnknownTableSize;
    uint32 StringTableOffset;
    uint32 StringTableSize;
    uint32 Null;
}Header;

FSeek(Header.StringTableOffset);
byte StringTableData[Header.StringTableSize];

FSeek(Header.ResourceTypeInfoOffset);
struct
{
    uint32 ResourceTypeCount;
    for (i=0; i < ResourceTypeCount; i++)
    {
        struct
        {
            char ExtensionName[4];
            uint32 ResourceCount;
            uint32 Unknown;
            uint32 ResourceTableOffset;
        }ResourceTypeInfo;
    }
}ResourceTypeTable;

FSeek(Header.ResourceInfoOffset);
struct
{
    uint32 TotalResourceCount;
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        struct
        {
            FSeek(Header.ResourceInfoOffset + ResourceTypeTable.ResourceTypeInfo[i].ResourceTableOffset);
            for (j=0; j < ResourceTypeTable.ResourceTypeInfo[i].ResourceCount; j++)
            {
                struct 
                {
                    char Extension[4];
                    uint32 ResourceType;
                    uint32 ResourceSize;
                    uint32 ResourceNameOffset;
                    local uint32 cPos=FTell();
                    FSeek(Header.StringTableOffset + ResourceNameOffset);
                    string ResourceName;
                    FSeek(cPos);
                    uint32 ResourceOffset;
                    uint32 Unknown;
                }ResourceInfo;
            }
        }ResourceTableInfo;
    }
    struct
    {
        char Extension[4];
    }ExtensionBuffer[ResourceTypeTable.ResourceTypeCount]<optimize=false>;
}ResourceTable;

FSeek(Header.UnknownTableOffset);
byte UnknownTableData[Header.UnknownTableSize];

//Resources
FSeek(0);
struct
{
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        if (ResourceTypeTable.ResourceTypeInfo[i].ExtensionName == "smsh")
        {
            struct
            {
                for (j=0; j < ResourceTypeTable.ResourceTypeInfo[i].ResourceCount; j++)
                {
                    FSeek(ResourceTable.ResourceTableInfo[i].ResourceInfo[j].ResourceOffset);
                    struct
                    {
                        float BBox[6];
                        uint32 MaterialTableOffset;
                        uint32 ShapeCount;
                        uint32 ShapeTableOffset;
                        uint32 SkeletonOffset;
                        uint32 Unknown;
                        FSeek(MaterialTableOffset);
                        uint32 MaterialCount;
                        for (k=0; k < MaterialCount; k++)
                            uint32 MaterialInfoOffset;
    
                        for (k=0; k < MaterialCount; k++)
                        {
                            FSeek(MaterialInfoOffset[k]);
                            struct
                            {
                                uint32 MaterialNameOffset;
                                local uint32 MaterialNamePos=FTell();
                                FSeek(MaterialNameOffset);
                                string MaterialName;
                                FSeek(MaterialNamePos);
                                uint32 ShaderNameOffset;
                                local uint32 ShaderNamePos=FTell();
                                FSeek(ShaderNameOffset);
                                string ShaderName;
                                FSeek(ShaderNamePos);
                                uint32 Unknown_0;
                                uint32 Unknown_1;
                                uint32 Unknown_2;
                                uint32 Unknown_3;
                                uint32 Unknown_4;
                                uint32 Unknown_5;
                                uint32 Unknown_6;
                                uint32 Unknown_7;
                                uint32 MaterialTextureInfoOffset;
                                uint32 TextureNameOffset;
                                local uint32 TextureNamePos=FTell();
                                FSeek(TextureNameOffset);
                                string TextureName;
                                FSeek(TextureNamePos);
                                uint32 Unknown_8;
                                float Unknown_9;
                            }MaterialInfo;
                        }
    
                        FSeek(SkeletonOffset);
                        struct
                        {
                            uint32 SkeletonNameOffset;
                            local uint32 SkeletonNamePos=FTell();
                            FSeek(SkeletonNameOffset);
                            string SkeletonName;
                            FSeek(SkeletonNamePos);
                            float BBox[6];
                            uint32 BoneIndexCount;
                            uint32 BoneBaseOffset;
                            FSeek(BoneBaseOffset);
                            struct
                            {
                                struct
                                {
                                    float Mat00,Mat01,Mat02,Mat03,
                                          Mat10,Mat11,Mat12,Mat13,
                                          Mat20,Mat21,Mat22,Mat23,
                                          Mat30,Mat31,Mat32,Mat33;
                                }Matrix4x4_0;
                                struct
                                {
                                    float Mat00,Mat01,Mat02,Mat03,
                                          Mat10,Mat11,Mat12,Mat13,
                                          Mat20,Mat21,Mat22,Mat23,
                                          Mat30,Mat31,Mat32,Mat33;
                                }Matrix4x4_1;
                                uint32 BoneNameOffset;
                                local uint32 BoneNamePos=FTell();
                                FSeek(BoneNameOffset);
                                string BoneName;
                                FSeek(BoneNamePos);
                                uint32 Unknown_1;
                                uint32 BoneId;
                                uint32 ParentBoneId;
                                uint32 Unknown_2; //Some kind of offset to bone
                                uint32 Unknown_3[11];
                            }BoneInfo[BoneIndexCount]<optimize=false>;
                        }SkeletonInfo;
    
                        FSeek(ShapeTableOffset);
                        for (k=0; k < ShapeCount; k++)
                        {
                            struct
                            {
                                uint32 Null;
                                uint32 StrideSize;
                                uint32 MaterialIndex;
                                uint32 Unknown_0;
                                uint32 IndexCount;
                                uint32 VertexBufferSize;
                                uint32 VertexBufferOffsetPointer;
                                local uint32 VertexBufferOffsetPos=FTell();
                                FSeek(VertexBufferOffsetPointer + 12);
                                uint32 VertexBufferOffset;
                                FSeek(VertexBufferOffsetPos);
                                uint32 IndexOffset;
                                uint32 Unknown_1;
                                uint32 ShapeBoneIndex;
                                uint32 ShapeBoneIndexOffset;
                                local uint32 ShapeBoneIndexPos=FTell();
                                FSeek(ShapeBoneIndexOffset);
                                ubyte ShapeBoneId[ShapeBoneIndex];
                                FSeek(VertexBufferOffset);
                                if (StrideSize == 56)
                                {
                                    struct
                                    {
                                        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
                                        ubyte Unknown_0,Unknown_1,Unknown_2,Unknown_3; //Color maybe?
                                        ubyte Unknown_4,Unknown_5,Unknown_6,Unknown_7; //Something related to bone/skin?
                                        float Unknown_8,Unknown_9;                     //No idea
                                        uint32 Unknown_10,Unknown_11;                  //No idea
                                        float UVPosX,UVPosY;
                                    }VertexBuffer[VertexBufferSize/StrideSize];
                                }
                                FSeek(IndexOffset);
                                byte IndexBuffer[IndexCount*2];
                                FSeek(ShapeBoneIndexPos);
                            }ShapeInfo;
                        }
                    }SkinnedMesh;
                }
            }ResourceFile;
        }
    }
}ResourceFileTable;

 

image.thumb.png.d8c7df8cb0e93598a5815137442fbd5a.png

Edited by h3x3r
  • Like 1
Posted
3 hours ago, h3x3r said:

Hey, I am fine. Here's at least SkinnedMesh parser. I have also made Noesis script if you care, but without skin/skeleton, I am not good at it. But 010 Template covered it.

There are also some downsides. I don't know how to read both texture and model in one script. So you must first use script for textures, export them and remove script from plugins\python folder and use this one for a model.

  Hide contents
from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Psi Ops - Geometry", ".w32")
   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()
	# Lists
	ExtensionTypeList        = []
	ResourceCountList        = []
	ResourceTableOffsetList  = []
	ResourceOffsetList       = []
	ResourceNameList         = []
	MaterialInfoOffsetList   = []
	StrideSizeList           = []
	MaterialIndexList        = []
	IndexCountList           = []
	VertexBufferSizeList     = []
	VertexBufferOffsetList   = []
	IndexOffsetList          = []
	ShapeBoneIndexList       = []
	ShapeBoneIndexOffsetList = []
	_ = []

	Underline = "_"

    # Header Info
	Sign                   = bs.readUInt()
	Version                = bs.readUInt()
	Unknown_0              = bs.readUInt()
	ResourceTypeInfoOffset = bs.readUInt()
	ResourceTypeInfoSize   = bs.readUInt()
	ResourceInfoOffset     = bs.readUInt()
	ResourceInfoSize       = bs.readUInt()
	UnknownTableOffset     = bs.readUInt()
	UnknownTableSize       = bs.readUInt()
	StringTableOffset      = bs.readUInt()
	StringTableSize        = bs.readUInt()
	Null                   = bs.readUInt()
	# Header Info End
	
	bs.seek(ResourceTypeInfoOffset, NOESEEK_ABS)
    # Resource Type Table
	ResourceTypeCount = bs.readUInt()
	for i in range(0, ResourceTypeCount):
	    ExtensionTypeList.append(bs.readUInt())
	    ResourceCountList.append(bs.readUInt())
	    Unknown = bs.readUInt()
	    ResourceTableOffsetList.append(bs.readUInt())
	
	for i in range(0, ResourceTypeCount):
	    ExtensionType       = ExtensionTypeList[i]
	    ResourceCount       = ResourceCountList[i]
	    ResourceTableOffset = ResourceTableOffsetList[i]
	    if ExtensionType == 1752395123:
	        bs.seek(ResourceInfoOffset + ResourceTableOffset, NOESEEK_ABS)
	        for j in range(0, ResourceCount):
	            Extension          = bs.readUInt()
	            ResourceType       = bs.readUInt()
	            ResourceSize       = bs.readUInt()
	            ResourceNameOffset = bs.readUInt()
	            ResourceOffsetList.append(bs.readUInt())
	            Unknown            = bs.readUInt()
	            EndOfInfo          = bs.tell()
	            bs.seek(StringTableOffset + ResourceNameOffset, NOESEEK_ABS)
	            ResourceNameList.append(bs.readString())
	            bs.seek(EndOfInfo, NOESEEK_ABS)

	        for j in range(0, ResourceCount):
	            ResourceOffset      = ResourceOffsetList[j]
	            ResourceName        = ResourceNameList[j]
	            bs.seek(ResourceOffset, NOESEEK_ABS)
	            BBox                = bs.read(24)
	            MaterialTableOffset = bs.readUInt()
	            ShapeCount          = bs.readUInt()
	            ShapeTableOffset    = bs.readUInt()
	            SkeletonOffset      = bs.readUInt()
	            Unknown             = bs.readUInt()

	            bs.seek(MaterialTableOffset, NOESEEK_ABS)
	            MaterialCount       = bs.readUInt()
	            for k in range(0, MaterialCount):
	                MaterialInfoOffsetList.append(bs.readUInt())
                    
	            bs.seek(ShapeTableOffset, NOESEEK_ABS)    
	            for k in range(0, ShapeCount):
	                Null = bs.readUInt()
	                StrideSizeList.append(bs.readUInt())
	                MaterialIndexList.append(bs.readUInt())
	                Unknown_0 = bs.readUInt()
	                IndexCountList.append(bs.readUInt())
	                VertexBufferSizeList.append(bs.readUInt())
	                VertexBufferOffsetPointer = bs.readUInt()
	                VertexBufferOffsetPointerPos = bs.tell()
	                bs.seek(VertexBufferOffsetPointer + 12, NOESEEK_ABS)
	                VertexBufferOffsetList.append(bs.readUInt())
	                bs.seek(VertexBufferOffsetPointerPos, NOESEEK_ABS)
	                IndexOffsetList.append(bs.readUInt())
	                Unknown_1 = bs.readUInt()
	                ShapeBoneIndexList.append(bs.readUInt())
	                ShapeBoneIndexOffsetList.append(bs.readUInt())

	            for k in range(0, ShapeCount):
	                ShapeIdDigfmt = "{:04d}".format(k)
	                MaterialInfoOffset = MaterialInfoOffsetList[k]
	                StrideSize = StrideSizeList[k]
	                MaterialIndex = MaterialIndexList[k]
	                IndexCount = IndexCountList[k]
	                VertexBufferSize = VertexBufferSizeList[k]
	                VertexBufferOffset = VertexBufferOffsetList[k]
	                IndexOffset = IndexOffsetList[k]
	                ShapeBoneIndex = ShapeBoneIndexList[k]
	                ShapeBoneIndexOffset = ShapeBoneIndexOffsetList[k]
                    
	                bs.seek(MaterialInfoOffset, NOESEEK_ABS)
	                MaterialNameOffset = bs.readUInt()
	                ShaderNameOffset = bs.readUInt()
	                bs.read(28)
	                Unknown = bs.readUInt()
	                MaterialTextureInfoOffset = bs.readUInt()
	                TextureNameOffset = bs.readUInt()
	                bs.seek(MaterialNameOffset, NOESEEK_ABS)
	                MaterialName = bs.readString()
	                bs.seek(TextureNameOffset, NOESEEK_ABS)
	                TextureName = bs.readString()

	                bs.seek(VertexBufferOffset, NOESEEK_ABS)
	                VertexBuffer = bs.read(VertexBufferSize)
	                if StrideSize == 56:
	                    rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 0)
	                    rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 12)
	                    rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, StrideSize, 48)
	                    rapi.rpgBindColorBufferOfs(VertexBuffer, noesis.RPGEODATA_BYTE, StrideSize, 24, 4)
                        

	                bs.seek(IndexOffset, NOESEEK_ABS)
	                IndexBuffer = bs.read(IndexCount * 2)
	                rapi.rpgSetMaterial(TextureName)
	                rapi.rpgSetName(ResourceName + Underline + ShapeIdDigfmt)
	                rapi.rpgCommitTriangles(IndexBuffer, noesis.RPGEODATA_USHORT, IndexCount, noesis.RPGEO_TRIANGLE_STRIP)

	mdl = rapi.rpgConstructModel()
	mdlList.append(mdl)
	return 1

 

Also script for textures always output first file with incorrect file name. Don't know how to fix it.

image.png.feca5a0d1bede607308ca1d5348712bf.png

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

local uint32 i,j,k,l;
struct
{
    char Sign[4];
    uint32 Version;
    uint32 Unknown_0;
    uint32 ResourceTypeInfoOffset;
    uint32 ResourceTypeInfoSize;
    uint32 ResourceInfoOffset;
    uint32 ResourceInfoSize;
    uint32 UnknownTableOffset;
    uint32 UnknownTableSize;
    uint32 StringTableOffset;
    uint32 StringTableSize;
    uint32 Null;
}Header;

FSeek(Header.StringTableOffset);
byte StringTableData[Header.StringTableSize];

FSeek(Header.ResourceTypeInfoOffset);
struct
{
    uint32 ResourceTypeCount;
    for (i=0; i < ResourceTypeCount; i++)
    {
        struct
        {
            char ExtensionName[4];
            uint32 ResourceCount;
            uint32 Unknown;
            uint32 ResourceTableOffset;
        }ResourceTypeInfo;
    }
}ResourceTypeTable;

FSeek(Header.ResourceInfoOffset);
struct
{
    uint32 TotalResourceCount;
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        struct
        {
            FSeek(Header.ResourceInfoOffset + ResourceTypeTable.ResourceTypeInfo[i].ResourceTableOffset);
            for (j=0; j < ResourceTypeTable.ResourceTypeInfo[i].ResourceCount; j++)
            {
                struct 
                {
                    char Extension[4];
                    uint32 ResourceType;
                    uint32 ResourceSize;
                    uint32 ResourceNameOffset;
                    local uint32 cPos=FTell();
                    FSeek(Header.StringTableOffset + ResourceNameOffset);
                    string ResourceName;
                    FSeek(cPos);
                    uint32 ResourceOffset;
                    uint32 Unknown;
                }ResourceInfo;
            }
        }ResourceTableInfo;
    }
    struct
    {
        char Extension[4];
    }ExtensionBuffer[ResourceTypeTable.ResourceTypeCount]<optimize=false>;
}ResourceTable;

FSeek(Header.UnknownTableOffset);
byte UnknownTableData[Header.UnknownTableSize];

//Resources
FSeek(0);
struct
{
    for (i=0; i < ResourceTypeTable.ResourceTypeCount; i++)
    {
        if (ResourceTypeTable.ResourceTypeInfo[i].ExtensionName == "smsh")
        {
            struct
            {
                for (j=0; j < ResourceTypeTable.ResourceTypeInfo[i].ResourceCount; j++)
                {
                    FSeek(ResourceTable.ResourceTableInfo[i].ResourceInfo[j].ResourceOffset);
                    struct
                    {
                        float BBox[6];
                        uint32 MaterialTableOffset;
                        uint32 ShapeCount;
                        uint32 ShapeTableOffset;
                        uint32 SkeletonOffset;
                        uint32 Unknown;
                        FSeek(MaterialTableOffset);
                        uint32 MaterialCount;
                        for (k=0; k < MaterialCount; k++)
                            uint32 MaterialInfoOffset;
    
                        for (k=0; k < MaterialCount; k++)
                        {
                            FSeek(MaterialInfoOffset[k]);
                            struct
                            {
                                uint32 MaterialNameOffset;
                                local uint32 MaterialNamePos=FTell();
                                FSeek(MaterialNameOffset);
                                string MaterialName;
                                FSeek(MaterialNamePos);
                                uint32 ShaderNameOffset;
                                local uint32 ShaderNamePos=FTell();
                                FSeek(ShaderNameOffset);
                                string ShaderName;
                                FSeek(ShaderNamePos);
                                uint32 Unknown_0;
                                uint32 Unknown_1;
                                uint32 Unknown_2;
                                uint32 Unknown_3;
                                uint32 Unknown_4;
                                uint32 Unknown_5;
                                uint32 Unknown_6;
                                uint32 Unknown_7;
                                uint32 MaterialTextureInfoOffset;
                                uint32 TextureNameOffset;
                                local uint32 TextureNamePos=FTell();
                                FSeek(TextureNameOffset);
                                string TextureName;
                                FSeek(TextureNamePos);
                                uint32 Unknown_8;
                                float Unknown_9;
                            }MaterialInfo;
                        }
    
                        FSeek(SkeletonOffset);
                        struct
                        {
                            uint32 SkeletonNameOffset;
                            local uint32 SkeletonNamePos=FTell();
                            FSeek(SkeletonNameOffset);
                            string SkeletonName;
                            FSeek(SkeletonNamePos);
                            float BBox[6];
                            uint32 BoneIndexCount;
                            uint32 BoneBaseOffset;
                            FSeek(BoneBaseOffset);
                            struct
                            {
                                struct
                                {
                                    float Mat00,Mat01,Mat02,Mat03,
                                          Mat10,Mat11,Mat12,Mat13,
                                          Mat20,Mat21,Mat22,Mat23,
                                          Mat30,Mat31,Mat32,Mat33;
                                }Matrix4x4_0;
                                struct
                                {
                                    float Mat00,Mat01,Mat02,Mat03,
                                          Mat10,Mat11,Mat12,Mat13,
                                          Mat20,Mat21,Mat22,Mat23,
                                          Mat30,Mat31,Mat32,Mat33;
                                }Matrix4x4_1;
                                uint32 BoneNameOffset;
                                local uint32 BoneNamePos=FTell();
                                FSeek(BoneNameOffset);
                                string BoneName;
                                FSeek(BoneNamePos);
                                uint32 Unknown_1;
                                uint32 BoneId;
                                uint32 ParentBoneId;
                                uint32 Unknown_2; //Some kind of offset to bone
                                uint32 Unknown_3[11];
                            }BoneInfo[BoneIndexCount]<optimize=false>;
                        }SkeletonInfo;
    
                        FSeek(ShapeTableOffset);
                        for (k=0; k < ShapeCount; k++)
                        {
                            struct
                            {
                                uint32 Null;
                                uint32 StrideSize;
                                uint32 MaterialIndex;
                                uint32 Unknown_0;
                                uint32 IndexCount;
                                uint32 VertexBufferSize;
                                uint32 VertexBufferOffsetPointer;
                                local uint32 VertexBufferOffsetPos=FTell();
                                FSeek(VertexBufferOffsetPointer + 12);
                                uint32 VertexBufferOffset;
                                FSeek(VertexBufferOffsetPos);
                                uint32 IndexOffset;
                                uint32 Unknown_1;
                                uint32 ShapeBoneIndex;
                                uint32 ShapeBoneIndexOffset;
                                local uint32 ShapeBoneIndexPos=FTell();
                                FSeek(ShapeBoneIndexOffset);
                                ubyte ShapeBoneId[ShapeBoneIndex];
                                FSeek(VertexBufferOffset);
                                if (StrideSize == 56)
                                {
                                    struct
                                    {
                                        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
                                        ubyte Unknown_0,Unknown_1,Unknown_2,Unknown_3; //Color maybe?
                                        ubyte Unknown_4,Unknown_5,Unknown_6,Unknown_7; //Something related to bone/skin?
                                        float Unknown_8,Unknown_9;                     //No idea
                                        uint32 Unknown_10,Unknown_11;                  //No idea
                                        float UVPosX,UVPosY;
                                    }VertexBuffer[VertexBufferSize/StrideSize];
                                }
                                FSeek(IndexOffset);
                                byte IndexBuffer[IndexCount*2];
                                FSeek(ShapeBoneIndexPos);
                            }ShapeInfo;
                        }
                    }SkinnedMesh;
                }
            }ResourceFile;
        }
    }
}ResourceFileTable;

 

image.thumb.png.d8c7df8cb0e93598a5815137442fbd5a.png

Ohh!!!!! that's so cool! What wonderful progress! No big deal about needing two different scripts for export. This is so exciting!
You're doing excellent work, I think Durik256 from this website is especially skilled at skeletons, I'll see if I can get him to help you out.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...