Gagnetar Posted June 21 Posted June 21 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
Engineers shak-otay Posted June 21 Engineers Posted June 21 Using hex2obj - mesh format appears to be simple (for one sub mesh at least):
Gagnetar Posted June 21 Author Posted June 21 (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 June 21 by Gagnetar reduced keyword density
Engineers shak-otay Posted June 22 Engineers Posted June 22 (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?) Edited June 22 by shak-otay
Engineers h3x3r Posted June 22 Engineers Posted June 22 (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 June 22 by h3x3r 2
Gagnetar Posted June 23 Author Posted June 23 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.
Engineers h3x3r Posted June 23 Engineers Posted June 23 I tested it on all files you provided and it works. So it must be the Noesis. Try to update it. 1
Gagnetar Posted June 23 Author Posted June 23 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?
Gagnetar Posted June 26 Author Posted June 26 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: 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?
Gagnetar Posted July 1 Author Posted July 1 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 h3x3r Posted July 3 Engineers Posted July 3 (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 July 3 by h3x3r
Gagnetar Posted July 3 Author Posted July 3 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!
Engineers h3x3r Posted July 3 Engineers Posted July 3 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....
Gagnetar Posted July 3 Author Posted July 3 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 h3x3r Posted July 3 Engineers Posted July 3 (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 July 4 by h3x3r 1
Gagnetar Posted July 4 Author Posted July 4 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 h3x3r Posted July 5 Engineers Posted July 5 (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 July 5 by h3x3r
Gagnetar Posted Thursday at 05:56 AM Author Posted Thursday at 05:56 AM 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 h3x3r Posted Thursday at 02:36 PM Engineers Posted Thursday at 02:36 PM (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. 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; Edited Thursday at 02:37 PM by h3x3r 1
Gagnetar Posted Thursday at 05:58 PM Author Posted Thursday at 05:58 PM 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. 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; 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.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now