The rules have been updated, read them now: Rules!

Video tutorials on model formats reversing

Read or post any tutorial related to file format analysis for modding purposes.

Do you think I should make more tutorials?

yes
93
99%
no (its not understandable)
1
1%
no (its uncomfortable to view)
0
No votes
no (for other reasons)
0
No votes
 
Total votes: 94

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Sun Mar 12, 2017 3:00 pm

TLoU part 2. Includes TLoU vs Uncharted 2 part, EDGE decompression and weights. After all, EDGE proved to be different in different games, though, nothing impossible.

https://youtu.be/VBqYEaqCzxY

Image

JohnHudeski
mega-veteran
mega-veteran
Posts: 177
Joined: Wed Mar 02, 2011 10:38 pm
Has thanked: 10 times
Been thanked: 58 times

Re: Video tutorials on model formats reversing

Post by JohnHudeski » Tue Jun 05, 2018 9:42 am

It ended?

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Tue Jun 05, 2018 9:47 am

what exactly is ended?

mono24
ultra-veteran
ultra-veteran
Posts: 581
Joined: Sat Nov 06, 2010 12:27 am
Has thanked: 272 times
Been thanked: 122 times

Re: Video tutorials on model formats reversing

Post by mono24 » Wed Jun 06, 2018 4:30 am

I think he's trying to find out if you ended the tutorials, or there will be more in the future in various ways/games and such.

User avatar
zaykho
mega-veteran
mega-veteran
Posts: 215
Joined: Fri Dec 03, 2010 1:20 pm
Location: France
Has thanked: 152 times
Been thanked: 33 times

Re: Video tutorials on model formats reversing

Post by zaykho » Sun Jul 29, 2018 10:57 pm

Hum interesting, I can already read and make tools to export mesh, the only thing I'm unable to do is : read/export Bones/Skeleton animation and sometimes textures/palettes, so I think it's time for me to watch those tutorials. ; )

Thank you again daemon1 for your contributions.

JohnHudeski
mega-veteran
mega-veteran
Posts: 177
Joined: Wed Mar 02, 2011 10:38 pm
Has thanked: 10 times
Been thanked: 58 times

Re: Video tutorials on model formats reversing

Post by JohnHudeski » Tue May 14, 2019 11:07 am

Is it possible to do a tutorial on how to find encryption keys
or how to find out how exe read un-common structures

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Fri May 17, 2019 4:14 pm

JohnHudeski wrote:
Tue May 14, 2019 11:07 am
Is it possible to do a tutorial on how to find encryption keys
or how to find out how exe read un-common structures
find encryption keys - not very good idea. Such things are better kept private.
As for "how exe read" - this is rather generic, so no need to do special video about game formats, there are many existing tutorials in reverse engeneering area to cover this.

JohnHudeski
mega-veteran
mega-veteran
Posts: 177
Joined: Wed Mar 02, 2011 10:38 pm
Has thanked: 10 times
Been thanked: 58 times

Re: Video tutorials on model formats reversing

Post by JohnHudeski » Sat May 18, 2019 5:27 pm

But if the archive file containing the model asset or the ai lua is in an encrypted file.
For example, there was a game that encrypted all the game settings xml in blowfish
It really sucked cos the game had broken AI

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Thu Jan 30, 2020 5:51 pm

People asked me to make a video of real reversing process, from start to finish, thinking, making mistakes and so on.
I take an example from Twisted Metal (2012) game on PS3.

https://youtu.be/YXVuRl1QPrg
https://youtu.be/b9-kkkL8Q2s

JakeMiles
advanced
Posts: 47
Joined: Wed Aug 12, 2015 11:59 pm
Has thanked: 23 times
Been thanked: 3 times

Re: Video tutorials on model formats reversing

Post by JakeMiles » Thu Feb 13, 2020 10:30 pm

Thanks for you! For sharing tutorials and tips :)

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Tue Feb 18, 2020 5:04 pm

2 more tutorials using same twised metal game as an example, so you can better understand how the above work was done:

Basic things about 3d data reversing: how 3D data looks like in hex: compression, encryption, common data types (ints/floats), main data structures (geometry/bones/textures)
https://www.youtube.com/watch?v=i_uWw-UDpEo

Finding geometry buffers and their properties
https://www.youtube.com/watch?v=swdOXDVkGUU

EPYCSPYDER
beginner
Posts: 22
Joined: Thu Feb 06, 2020 12:48 pm
Has thanked: 6 times
Been thanked: 3 times

Re: Video tutorials on model formats reversing

Post by EPYCSPYDER » Tue Mar 31, 2020 3:01 am

Thanks for making the tutorials, I followed Twisted Metal PS3 reversing and wrote your code got the car .obj from ui.ngp
How can I make it work with little endian.

HJA_test

Code: Select all

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;

namespace HJA_test
{
    class BinaryReaderBE : BinaryReader
    {
        private byte[] a16 = new byte[2];
        private byte[] a32 = new byte[4];
        private byte[] a64 = new byte[8];
        public BinaryReaderBE(System.IO.Stream stream) : base(stream) { }
        public override float ReadSingle()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToSingle(a32, 0);
        }
        public override UInt16 ReadUInt16()
        {
            a16 = base.ReadBytes(2);
            Array.Reverse(a16);
            return BitConverter.ToUInt16(a16, 0);
        }
        public override Int16 ReadInt16()
        {
            a16 = base.ReadBytes(2);
            Array.Reverse(a16);
            return BitConverter.ToInt16(a16, 0);
        }
        public override Int32 ReadInt32()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToInt16(a32, 0);
        }
        public override UInt32 ReadUInt32()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToUInt32(a32, 0);
        }
        public override long ReadInt64()
        {
            a64 = base.ReadBytes(8);
            Array.Reverse(a64);
            return BitConverter.ToInt64(a64, 0);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberDecimalSeparator = ".";

            float x = 0;

            var sw = new StreamWriter(Path.GetFileNameWithoutExtension(args[0]) + ".obj");
            sw.Write(x.ToString("0.######", nfi));
        }
    }
}

TM2012

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Globalization;

namespace TM2012
{
    class BinaryReaderBE : BinaryReader
    {
        private byte[] a16 = new byte[2];
        private byte[] a32 = new byte[4];
        private byte[] a64 = new byte[8];
        public BinaryReaderBE(System.IO.Stream stream) : base(stream) { }
        public override float ReadSingle()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToSingle(a32, 0);
        }
        public override UInt16 ReadUInt16()
        {
            a16 = base.ReadBytes(2);
            Array.Reverse(a16);
            return BitConverter.ToUInt16(a16, 0);
        }
        public override Int16 ReadInt16()
        {
            a16 = base.ReadBytes(2);
            Array.Reverse(a16);
            return BitConverter.ToInt16(a16, 0);
        }
        public override Int32 ReadInt32()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToInt16(a32, 0);
        }
        public override UInt32 ReadUInt32()
        {
            a32 = base.ReadBytes(4);
            Array.Reverse(a32);
            return BitConverter.ToUInt32(a32, 0);
        }
        public override long ReadInt64()
        {
            a64 = base.ReadBytes(8);
            Array.Reverse(a64);
            return BitConverter.ToInt64(a64, 0);
        }
    }
    class tm2012
    {
        static void Main(string[] args)
        {
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberDecimalSeparator = ".";

            int i;
            float x, y, z;

            var fs = new FileStream("C:\\ui.ngp", FileMode.Open, FileAccess.Read);
            var br = new BinaryReaderBE(fs);
            var sw = new StreamWriter("ui.obj");

            fs.Seek(0x29ef30, SeekOrigin.Begin);
            int nv = 0x12f8;
            for (i = 0; i < nv; i++)
            {
                x = br.ReadInt16() / 4096f;
                y = br.ReadInt16() / 4096f;
                z = br.ReadInt16() / 4096f;
                sw.Write("v " + x.ToString("0.######", nfi));
                sw.Write("  " + y.ToString("0.######", nfi));
                sw.Write("  " + z.ToString("0.######", nfi));
                sw.WriteLine();
            }

            fs.Seek(0x298500, SeekOrigin.Begin);
            int nf = 0x3513 / 3;
            int f1, f2, f3;
            for (i = 0; i < nf; i++)
            {
                f1 = br.ReadUInt16() + 1; f2 = br.ReadUInt16() + 1; f3 = br.ReadUInt16() + 1;
                sw.WriteLine("f " + f1 + " " + f2 + " " + f3);
            }

            sw.Close();
        }
    }
}

daemon1
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2301
Joined: Tue Mar 24, 2015 8:12 pm
Has thanked: 54 times
Been thanked: 1953 times

Re: Video tutorials on model formats reversing

Post by daemon1 » Tue Mar 31, 2020 6:25 am

Change this:

Code: Select all

var br = new BinaryReaderBE(fs);
to this:

Code: Select all

var br = new BinaryReader(fs);
It will use the "usual" BinaryReader, which is little endian.

EPYCSPYDER
beginner
Posts: 22
Joined: Thu Feb 06, 2020 12:48 pm
Has thanked: 6 times
Been thanked: 3 times

Re: Video tutorials on model formats reversing

Post by EPYCSPYDER » Tue Mar 31, 2020 7:05 pm

Any chance you would do a code tutorial on the smd mesh/skeleton blender tools you used with The Last of Us?

mariokart64n
ultra-veteran
ultra-veteran
Posts: 567
Joined: Sun Jun 05, 2005 12:00 pm
Location: Ontario, Canada
Has thanked: 34 times
Been thanked: 197 times

Re: Video tutorials on model formats reversing

Post by mariokart64n » Thu Feb 25, 2021 7:19 am

daemon's videos are very well organized, so alot of respect to him for making these videos because they are so clear and informative.

I would like to also share some information and videos, for those who may be interested.. If this is not okay to share here then please let me know and I will remove it.


My Own Videos:
  1. 14:17 -> What is Binary
  2. 12:20 -> Hex Editing
  3. 21:41 -> Programming
  4. 14:36 -> Programming with Data Types
  5. 12:07 -> Hex Editor
  6. 09:43 -> Model Theory
  7. 24:58 -> Format Study
  8. 18:23 -> Writing C++ Byte Class
  9. 12:20 -> Writing C++ Mesh Reader / Exporter
  10. 34:51 -> Writing Maxscript Mesh Importer
  11. 34:12 -> Writing Noesis Python Mesh Importer

Simple Code Examples

Blender

Code: Select all

import bpy
import struct
from pathlib import Path

SEEK_ABS = 0
SEEK_REL = 1
SEEK_END = 2

class fopen:
    little_endian = True
    file = ""
    data = bytes()
    size = 0
    pos = 0
    isGood = False
    def __init__(self, filename = None, mode='rb', isLittleEndian=True):
        if filename != None and Path(filename).is_file():
            self.data = open(filename, mode).read()
            self.size = len(self.data)
            self.pos = 0
            self.file = filename
            self.little_endian = isLittleEndian
            self.isGood = True
        return None

    def read_and_unpack(self, unpack, size):
        '''
          Charactor, Byte-order
          @,         native, native
          =,         native, standard
          <,         little endian
          >,         big endian
          !,         network

          Format, C-type,         Python-type, Size[byte]
          c,      char,           byte,        1
          b,      signed char,    integer,     1
          B,      unsigned char,  integer,     1
          h,      short,          integer,     2
          H,      unsigned short, integer,     2
          i,      int,            integer,     4
          I,      unsigned int,   integer,     4
          f,      float,          float,       4
          d,      double,         float,       8
        '''
        value = 0
        if self.size > 0 and self.pos + size < self.size:
            value = struct.unpack_from(unpack, self.data, self.pos)[0]
            self.pos += size
        return value

    def set_pointer(self, offset):
        self.pos = offset
        return None

def fseek(bitStream, offset, dir):
    if dir == 0:
        bitStream.set_pointer(offset)
    elif dir == 1:
        bitStream.set_pointer(bitStream.pos + offset)
    elif dir == 2:
        bitStream.set_pointer(bitStream.pos - offset)
    return None

def readShort(bitStream, isSigned=0):
    fmt = '>' if not bitStream.little_endian else '<'
    fmt += 'h' if isSigned == 0 else 'H'
    return (bitStream.read_and_unpack(fmt, 2))

def readLong(bitStream, isSigned=0):
    fmt = '>' if not bitStream.little_endian else '<'
    fmt += 'i' if isSigned == 0 else 'I'
    return (bitStream.read_and_unpack(fmt, 4))

def readFloat(bitStream):
    fmt = '>f' if not bitStream.little_endian else '<f'
    return (bitStream.read_and_unpack(fmt, 4))

def mesh (vertices = [], faces = []):
    bpy.context.view_layer.objects.active = None
    msh = bpy.data.meshes.new('Mesh')
    msh.from_pydata(vertArray, [], faceArray)
    msh.update()
    obj = bpy.data.objects.new('Object', msh)
    bpy.data.collections[bpy.context.view_layer.active_layer_collection.name].objects.link(obj)



f = fopen('C:\\Users\\Corey\\Desktop\\grimlock\\models\\vis1\\geo_veh_c_sec_wheel.xgm', 'rb')

fseek(f, 0x1D8, SEEK_ABS)

# Reading Buffer Info
vertex_buffer_size = readLong(f)
index_buffer_size =  readLong(f)

# Calculate the Counts from Buffer Info
vertex_buffer_stride = 24
index_buffer_stride = 2
vertex_count = int(vertex_buffer_size / vertex_buffer_stride)
index_count = int(index_buffer_size / index_buffer_stride / 3)

# Read The buffers
vertArray = []
faceArray = []
fseek(f, 0x29C, SEEK_ABS)

for i in range(0, vertex_count):
    vertArray.append( (readFloat(f),  readFloat(f), readFloat(f)) )
    fseek(f, (vertex_buffer_stride - 12), SEEK_REL)
    
for i in range(0,index_count):
    faceArray.append((readShort(f),  readShort (f), readShort (f)))
    

# make mesh
mesh(vertArray, faceArray)
3ds Max (MaxScript)

Code: Select all

f = fopen "C:\\Users\\Corey\\Desktop\\grimlock\\models\\vis1\\geo_veh_c_sec_wheel.xgm" "rb"

SEEK_ABS = #seek_set
SEEK_REL = #seek_cur
SEEK_END = #seek_end

fseek f 0x1D8 SEEK_ABS

-- Reading Buffer Info
vertex_buffer_size = readLong f
index_buffer_size =  readLong f

-- Calculate the Counts from Buffer Info
vertex_buffer_stride = 24
index_buffer_stride = 2
vertex_count = vertex_buffer_size / vertex_buffer_stride
index_count = index_buffer_size / index_buffer_stride / 3

-- Read The buffers
vertArray = #()
faceArray = #()
fseek f 0x29C SEEK_ABS

for i = 1 to vertex_count do (
	append vertArray [-readFloat f,  readFloat f, readFloat f]
	fseek f (vertex_buffer_stride - 12) SEEK_REL
	)

for i = 1 to index_count do (
	append faceArray ([readShort f,  readShort f, readShort f] + 1)
	)

-- import to the scene
mesh vertices:vertArray faces:faceArray

fclose f
Cinema4D

Code: Select all

import c4d
import maxon
from c4d import *
from maxon import *

# Enums!
SEEK_ABS = 0
SEEK_REL = 1
SEEK_END = 2

class fopen: # reads file to buffer, and reads values from buffer
    byteStream = maxon.BaseArray(maxon.Char)
    size = 0
    pos = 0
    eof = False # End of Stream
    bebo = False # Big Endian Byte Order
    def __init__(self, filename = ""):
        if filename != "":
            fname = maxon.Url(filename)
            if (fname.IoDetect() ==  maxon.IODETECT.FILE):
                inputStream = fname.OpenInputStream()
                self.size = inputStream.GetStreamLength()
                if self.size > 0:
                    self.byteStream.Resize(self.size)
                    inputStream.ReadEOS(self.byteStream)
                    self.pos = 0
                inputStream.Close()
    def seek (self, offset, curdir = 0):
        if curdir == SEEK_ABS:
            self.pos = offset
        elif curdir == SEEK_REL:
            self.pos += offset
        elif curdir == SEEK_END:
            self.pos = self.size - offset
        if self.pos > self.size or self.pos < 0:
            self.pos = 0
            self.eof = True
        return not self.eof
    def tell (self):
        return self.pos
    def unsigned_to_signed (self, n, nbits):
        result = n
        if (n > pow(2, nbits) / 2):
            result = n - pow(2, nbits)
        return result
    def readByte (self, isSigned = True):
        val = -1
        if self.pos + 1 < self.size:
            val = ord(self.byteStream[self.pos])
            self.pos+=1
            if isSigned: val = self.unsigned_to_signed(val, 8)
        return val
    def readShort (self, isSigned = True):
        byteOrder = [0, 1]
        if self.bebo: byteOrder = [1, 0]
        val = -1
        if self.pos + 1 < self.size:
            val =  ord(self.byteStream[self.pos + byteOrder[0]]) * 0x0001
            val += ord(self.byteStream[self.pos + byteOrder[1]]) * 0x0100
            self.pos+=2
            if isSigned: val = self.unsigned_to_signed(val, 16)
        return val
    def readLong (self, isSigned = True):
        byteOrder = [0, 1, 2, 3]
        if self.bebo: byteOrder = [3, 2, 1, 0]
        val = -1
        if self.pos + 1 < self.size:
            val =  ord(self.byteStream[self.pos + byteOrder[0]]) * 0x00000001
            val += ord(self.byteStream[self.pos + byteOrder[1]]) * 0x00000100
            val += ord(self.byteStream[self.pos + byteOrder[2]]) * 0x00010000
            val += ord(self.byteStream[self.pos + byteOrder[3]]) * 0x01000000
            self.pos+=4
            if isSigned: val = self.unsigned_to_signed(val, 32)
        return val
    def readFloat (self):
        inputAsInt = self.readLong(False)
        fraction = 0.0
        for i in range(0, 23): fraction += (1 & (inputAsInt >> ((23 - i) - 1))) * (pow(2, -(i + 1)))
        sign = -1
        if (inputAsInt >> 31) & 0x00000001 == 0:
            sign = 1
        return (sign * (1 + fraction) * (pow(2, (((inputAsInt & 0x7F800000) >> 23) - 127))))

class mesh: # builds mesh into C4D
    def __init__(self, vertices = [], faces = []):
        if len(vertices) > 0 and len(faces) > 0:
            mypoly = c4d.BaseObject(c4d.Opolygon) #Create an empty polygon object
            mypoly.ResizeObject(len(vertices), len(faces)) #New number of points, New number of polygons
            for i in range(0, len(vertices)):
                mypoly.SetPoint(i, vertices[i])
            for i in range(0, len(faces)):
               mypoly.SetPolygon(i, faces[i])
            doc.InsertObject(mypoly,None,None)
            mypoly.Message(c4d.MSG_UPDATE)
            c4d.EventAdd()


f = fopen("C:\\Users\\Corey\\Desktop\\grimlock\\models\\vis1\\geo_veh_c_sec_wheel.xgm")

f.seek(0x1D8, SEEK_ABS)

# Reading Buffer Info
vertex_buffer_size = f.readLong()
index_buffer_size =  f.readLong()

# Calculate the Counts from Buffer Info
vertex_buffer_stride = 24
index_buffer_stride = 2
vertex_count = int(vertex_buffer_size / vertex_buffer_stride)
index_count = int(index_buffer_size / index_buffer_stride / 3)

# Read The buffers
vertArray = []
faceArray = []
f.seek(0x29C, SEEK_ABS)

for i in range(0, vertex_count):
    vertArray.append(c4d.Vector(f.readFloat(), f.readFloat(), f.readFloat()))
    f.seek(vertex_buffer_stride - 12, SEEK_REL)
    
for i in range(0,index_count):
    faceArray.append(c4d.CPolygon(f.readShort(), f.readShort(), f.readShort()))


# make mesh
mesh(vertArray, faceArray)
Noesis

Code: Select all

from inc_noesis import *


def registerNoesisTypes():
    handle = noesis.register("load an xgm", ".xgm")
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    noesis.logPopup()
    return 1


def noepyCheckType(data):
    if len(data) < 12: return 0
    f = NoeBitStream(data)
    f.seek(8)
    if f.readInt() != 0x4D534758: return 0
    return 1


def noepyLoadModel(data, mdlList):
    if len(data) < 12: return 0
    f = NoeBitStream(data)
    f.seek(8)
    if f.readInt() != 0x4D534758: return 0

    f.seek(0x1D8, NOESEEK_ABS)

    # Reading Buffer Info
    vertex_buffer_size = f.readInt()
    index_buffer_size = f.readInt()

    # Calculate the Counts from Buffer Info
    vertex_buffer_stride = 24
    index_buffer_stride = 2
    vertex_count = int(vertex_buffer_size / vertex_buffer_stride)
    index_count = int(index_buffer_size / index_buffer_stride / 3)

    # Read The buffers
    vertArray = []
    faceArray = []
    f.seek(0x29C, NOESEEK_ABS)

    for i in range(0, vertex_count):
        vertArray.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
        f.seek((vertex_buffer_stride - 12), NOESEEK_REL)

    for i in range(0, index_count):
        f1 = f.readShort()
        f2 = f.readShort()
        f3 = f.readShort()
        faceArray.append(f1)
        faceArray.append(f3)
        faceArray.append(f2)

    # import to the scene
    mdl = NoeModel()
    mdl.setMeshes([NoeMesh(faceArray, vertArray)])
    mdlList.append(mdl)

    return 1
QuickBMS

Code: Select all

# script for QuickBMS http://quickbms.aluigi.org


# Set File Number
f = 0

goto 0x1D8 f SEEK_SET

# Reading Buffer Info
get vertex_buffer_size long f
get index_buffer_size long f

# Calculate the Counts from Buffer Info
set vertex_buffer_stride long 24
set index_buffer_stride long 2

set vertex_count long vertex_buffer_size
set index_count long index_buffer_size
math vertex_count /= vertex_buffer_stride
math index_count /= index_buffer_stride
math index_count /= 3

# Set ObjFile
set obj string "o mesh"
set temp string ""
string temp p= "%c%c%c%c" 0x0D 0x0A 0x0D 0x0A
string obj += temp

# Read The buffers
set skip long 0
math skip = vertex_buffer_stride
math skip -= 12

goto 0x29C f SEEK_SET
for i = 0 < vertex_count
    
    get pos_x long f
    get pos_y long f
    get pos_z long f
    
    string temp p= "v %f" pos_x
    string obj += temp
    
    string temp p= " %f" pos_y
    string obj += temp
    
    string temp p= " %f" pos_z
    string obj += temp
    
    string temp p= "%c%c" 0x0D 0x0A
    string obj += temp

    goto skip f SEEK_CUR
next i

string temp p= "%c%c%c%c" 0x0D 0x0A 0x0D 0x0A
string obj += temp
for i = 0 < index_count
    get face_a short f
    get face_b short f
    get face_c short f
    
    math face_a += 1
    math face_b += 1
    math face_c += 1
    
    string temp p= "f %i" face_a
    string obj += temp
    
    string temp p= " %i" face_c
    string obj += temp
    
    string temp p= " %i" face_b
    string obj += temp
    
    string temp p= "%c%c" 0x0D 0x0A
    string obj += temp
    
next i

// write the OBJ
strlen obj_size obj
putvarchr MEMORY_FILE obj_size 0
put obj string MEMORY_FILE
get NAME basename
string file_name p= "%s.obj" NAME
log file_name 0 obj_size MEMORY_FILE
C++

Code: Select all

#include <iostream>
#include <fstream>
#include <iomanip>

using namespace std;

#define SEEK_ABS 0
#define SEEK_REL 1
#define SEEK_END 2

struct bytes {
	ifstream file;
	bool isGood;
	bytes (string filename) {
		file.open (filename.c_str(), std::ifstream::binary);
		isGood = file.good();
		}
	~bytes () {
		file.close();
		}
	int16_t readInt16 () {
		int32_t input;
		file.read(reinterpret_cast<char*>(&input), sizeof(int16_t));
		return input;
		}
	int32_t readInt32 () {
		int32_t input;
		file.read(reinterpret_cast<char*>(&input), sizeof(int32_t));
		return input;
		}
	float readFloat32 () {
		float input;
		file.read(reinterpret_cast<char*>(&input), sizeof(input));
		return input;
		}
	void seek (int offset, int dir = 0) {
		if (dir == SEEK_ABS) {
			file.seekg(offset, file.beg);
			}
		else if (dir == SEEK_REL) {
			file.seekg(offset, file.cur);
			}
		else if (dir == SEEK_END) {
			file.seekg(offset, file.end);
			}
		}
	};

int main() {

	// Open File
	bytes f("C:\\Users\\Corey\\Desktop\\grimlock\\models\\vis1\\geo_veh_c_sec_wheel.xgm");


	// Check is open
	if (!f.isGood) {return 0;}

	// Read the Buffer Info
	f.seek(0x1D8);
	int vertex_buffer_size = f.readInt32();
	int index_buffer_size = f.readInt32();

	// Calculate the Counts from Buffer Info
	int vertex_buffer_stride = 24;
	int index_buffer_stride = 2;
	int vertex_count = vertex_buffer_size / vertex_buffer_stride;
	int index_count = index_buffer_size / index_buffer_stride / 3;


	cout << "vertex_buffer_size: " << vertex_buffer_size << endl;
	cout << "index_buffer_size: " << index_buffer_size << endl;

	// Read Buffer
	f.seek(0x29C);

	// write the OBJ
	std::ofstream out;
	out.open ("C:\\Users\\Corey\\Desktop\\grimlock\\models\\vis1\\geo_veh_c_sec_wheel.obj", std::ofstream::out);

	out << "o meshobject" << endl << endl;

	for (int i = 0; i < vertex_count; i++) {
		out  << std::setprecision(6) << "v " << f.readFloat32();
		out  << std::setprecision(6) << " " << f.readFloat32();
		out  << std::setprecision(6) << " " << f.readFloat32() << endl;
		f.seek(vertex_buffer_stride - 12, SEEK_REL);
		}
	out << endl;
	for (int i = 0; i < index_count; i++) {
		out << "f " << f.readInt16() + 1;
		out << " " << f.readInt16() + 1;
		out << " " << f.readInt16() + 1 << endl;
		}

	out.close();
    return 0;
	}
C#

Code: Select all

using System;
using System.IO;
using System.Text;

namespace ModelDumper {
    class Program {
        static int readShort(ref Byte[] buffer, ref int ptr) {
            // Converts Unsigned 16bit Integer from the buffer
            int word = (buffer[ptr++] & 0xFF) | ((buffer[ptr++] & 0xFF) << 8);
            return word;
            }
        static int readLong(ref Byte[] buffer, ref int ptr) {
            // Converts Unsigned 32bit Integer from the buffer
            int dword = (buffer[ptr++] & 0xFF) | ((buffer[ptr++] & 0xFF) << 8) | ((buffer[ptr++] & 0xFF) << 16) | ((buffer[ptr++] & 0xFF) << 24);
            return dword;
            }
        static double readFloat(ref Byte[] buffer, ref int ptr) {
            int inputAsInt = readLong(ref buffer, ref ptr);
            double fraction = 0.0F;
            for (int i = 0; i < 23; i++) {
                fraction += (1 & (inputAsInt >> ((23 - i) - 1))) * (Math.Pow(2, -(i + 1)));
                }
            int sign = -1;
            if (((inputAsInt >> 31) & 0x00000001) == 0) {
                sign = 1;
                }
            return (sign * (1 + fraction) * (Math.Pow(2, (((inputAsInt & 0x7F800000) >> 23) - 127))));
            }
        static void Main(string[] args) {
            
            // Open File into a FileStream
            string file = "C:\\Users\\Corey\\Videos\\Captures\\Vghd\\geo_veh_c_sec_wheel.xgm";
            var fs = new FileStream(file, FileMode.Open);
            var fsize = (int)fs.Length;
            var buffer = new byte[fsize];
            fs.Read(buffer, 0, fsize);
            fs.Close();
            
            int ptr = 0x1D8;
            
            int vertex_buffer_size = readLong(ref buffer, ref ptr);
            int index_buffer_size = readLong(ref buffer, ref ptr);
            
            // Calculate the Counts from Buffer Info
            int vertex_buffer_stride = 24;
            int index_buffer_stride = 2;
            int vertex_count = vertex_buffer_size / vertex_buffer_stride;
            int index_count = index_buffer_size / index_buffer_stride / 3;
            
            // Set Obj File
            string objfile = "o mesh\ng mesh\n\n";
            
            // Read The buffers
            int f1;
            int f2;
            int f3;
            ptr = 0x29C;
            for (int i = 0; i < vertex_count; i++) {
                objfile += "v " + (readFloat(ref buffer, ref ptr)).ToString("0.000000");
                objfile += " " + (readFloat(ref buffer, ref ptr)).ToString("0.000000");
                objfile += " " + (readFloat(ref buffer, ref ptr)).ToString("0.000000") + "\n";
                ptr += vertex_buffer_stride - 12;
                }
            objfile += "\n";
            
            for (int i = 0; i < index_count; i++) {
                f1 = readShort(ref buffer, ref ptr);
                f2 = readShort(ref buffer, ref ptr);
                f3 = readShort(ref buffer, ref ptr);
                objfile += "f " + (f1 + 1).ToString("0");
                objfile += " " + (f3 + 1).ToString("0");
                objfile += " " + (f2 + 1).ToString("0") + "\n";
                }
            
            // write the OBJ
            using (StreamWriter outputFile = new StreamWriter(Path.Combine(Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file) + ".obj"), false)) {
                outputFile.WriteLine(objfile);
                }
            }
        }
    }
Visual Basics (Microsoft Excel VBA)

Code: Select all

Sub binToMesh()
    
    Dim file As String: file = "C:\\Users\\Corey\\Videos\\Captures\\Vghd\\geo_veh_c_sec_wheel.xgm"
    
    Dim F As Long: F = FreeFile
    Open file For Binary As F
    
    Seek F, 1 + &H1D8
    
    ' Reading Buffer Info
    Dim vertex_buffer_size As Long: Get F, , vertex_buffer_size
    Dim index_buffer_size As Long: Get F, , index_buffer_size
    
    
    ' Calculate the Counts from Buffer Info
    Dim vertex_buffer_stride As Long: vertex_buffer_stride = 24
    Dim index_buffer_stride As Long: index_buffer_stride = 2
    Dim vertex_count As Long: vertex_count = CLng(CSng(vertex_buffer_size) / CSng(vertex_buffer_stride))
    Dim index_count As Long: index_count = CLng(CDbl(index_buffer_size) / CDbl(index_buffer_stride) / 3#)
    
    
    ' Read The buffers
    Seek F, 1 + &H29C
    
    
    Dim objFile As String: objFile = "o mesh" + vbNewLine + "g mesh" + vbNewLine + vbNewLine
    Dim pos_x As Single, pos_y As Single, pos_z As Single
    Dim face_a As Integer, face_b As Integer, face_c As Integer
    
    Dim i As Long
    For i = 1 To vertex_count
        Get F, , pos_x
        Get F, , pos_y
        Get F, , pos_z
        objFile = objFile + "v " + Format$(pos_x, "0.000000")
        objFile = objFile + " " + Format$(pos_y, "0.000000")
        objFile = objFile + " " + Format$(pos_z, "0.000000") + vbNewLine
        Seek F, Seek(F) + (vertex_buffer_stride - 12)
    Next i
    objFile = objFile + vbNewLine
    
    For i = 1 To index_count
        Get F, , face_a
        Get F, , face_b
        Get F, , face_c
        objFile = objFile + "f " + CStr(face_a + 1)
        objFile = objFile + " " + CStr(face_c + 1)
        objFile = objFile + " " + CStr(face_b + 1) + vbNewLine
    Next i
    
    Close F
    
    ' Write Obj
    Open (file + ".obj") For Output As #1
    Print #1, objFile
    Close #1

End Sub


Here is the file that these programs work with:
geo_veh_c_sec_wheel.zip
You do not have the required permissions to view the files attached to this post.
Maxscript and other finished work I've done can be found on my DeviantArt account

Post Reply