Jump to content

Gran Turismo 4 *.img


Go to solution Solved by DKDave,

Recommended Posts

  • Engineer

Hello there!

Can somebody please help update my noesis script to support this format? 

SCE_GS_PSMT8 = 19, // 8-bit indexed, packing 4 pixels per 32-bit.

So far I came across 4 formats and this one is something... Don't know how to process it in noesis.

Here is 010 Template from Nenkai

Spoiler

//------------------------------------------------
//--- 010 Editor v10.0.2 Binary Template
//
//      File: PGLUTexSet
//   Authors: Nenkai#9075
//   Version: 
//   Purpose: Texture Set for PS2 PDI Games
//  Category: Game Sprites
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------


BitfieldDisablePadding();

string PrintBufferSize(int64 val)
{
    string s;
    SPrintf(s, "Same as tbp/cbp, gets remapped at runtime - offset: 0x%x (0x%x * 64)", val * 64, val);
    return s;
}

typedef enum <byte>
{
    SCE_GS_NEAREST = 0,
    SCE_GS_LINEAR = 1,
    SCE_GS_NEAREST_MIPMAP_NEAREST = 2,
    SCE_GS_NEAREST_MIPMAP_LINEAR = 3,
    SCE_GS_LINEAR_MIPMAP_NEAREST = 4,
    SCE_GS_LINEAR_MIPMAP_LINEAR = 5,
} SCE_GS_MAG;

typedef enum <byte> 

    SCE_GS_PSMCT32 = 0, // RGBA32, uses 32-bit per pixel.
    SCE_GS_PSMCT24 = 1, // RGB24, uses 24-bit per pixel with the upper 8 bit unused.
    SCE_GS_PSMCT16 = 2, // RGBA16 unsigned, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMCT16S = 10, // RGBA16 signed, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMT8 = 19, // 8-bit indexed, packing 4 pixels per 32-bit.
    SCE_GS_PSMT4 = 20, // 4-bit indexed, packing 8 pixels per 32-bit.
    SCE_GS_PSMT8H = 27, // 8-bit indexed, but the upper 24-bit are unused.
    SCE_GS_PSMT4HL = 36, // 4-bit indexed, but the upper 24-bit are unused.
    SCE_GS_PSMT4HH = 44,
    SCE_GS_PSMZ32 = 48,  // 32-bit Z buffer
    SCE_GS_PSMZ24 = 49, // 24-bit Z buffer with the upper 8-bit unused
    SCE_GS_PSMZ16 = 50, // 16-bit unsigned Z buffer, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMZ16S = 58, // 16-bit signed Z buffer, pack two pixels in 32-bit in little endian order.
} SCE_GS_PSM;

typedef enum <byte>
{
    SCE_GS_REPEAT = 0,
    SCE_GS_CLAMP = 1,
    SCE_GS_REGION_CLAMP = 2,
    SCE_GS_REGION_REPEAT = 3,
} SCE_GS_CLAMP_PARAMS;

typedef enum <byte> { CSM1, CSM2 } SCE_GS_CSM;

typedef struct
{
    struct
    {
        int64 TBP0_TextureBasePointer  : 14 <format=hex, comment="address / 64 (words aka 4) - this is the block offset to the texture">;
        int64 TBW_TextureBufferWidth : 6 <format=hex, comment="Texel Unit Width / 64 (words aka 4)">;
        SCE_GS_PSM PSM_TexturePixelStorageFormat : 6 <comment="Texture pixel storage format">;
        int64 TW_TextureWidth : 4 <comment="Texture width - Actual size will be 2^w and 2^h">;
        int64 TH_TextureHeight : 4 <comment="Texture height - Actual size will be 2^w and 2^h">;
        int64 TCC_TextureColorComponent : 1 <comment="Texture color component, 0 = RGB, 1 = RGBA">;
        int64 TFX_TextureFunction : 2 <comment="Texture function">;
        int64 CBP_CLUTBufferBasePointer : 14 <format=hex, comment="Base address of CLUT data (actual address will be cbp x 64)">;
        SCE_GS_PSM CPSM_ClutPixelStorageFormat : 4 <comment="Format in which CLUT entries are saved">;
        SCE_GS_CSM CSM_ClutStorageMode : 1 <comment="CLUT storage mode">;
        int64 CSA_ClutEntryOffset : 5 <comment="CLUT entry offset - CSA = Offset / 16, In CSM2, CSA must be 0">;
        // 0 = Temporary buffer contents not changed
        // 1 = Load performed to CSA position of buffer
        // 2 = Load is performed to CSA position of buffer and CBP is copied to CBP0. (*2)
        // 3 = Load is performed to CSA position of buffer and CBP is copied to CBP1. (*2)
        // 4 = If CBP0 != CBP, load is performed and CBP is copied to CBP0. (*2)
        // 5 = If CBP1 != CBP, load is performed and CBP is copied to CBP1. (*2)
        int64 CLD_ClutBufferLoadControl : 3 <comment="Check template comment">;
    } sceGsTex0;

    struct
    {
        int64 LCM_LODCalculationMethod : 1 <comment="0 = (LOD = (log2(1/|Q|)<<L+K), 1 = Fixed value (LOD = K)">;
        int64 pad01 : 1;
        int64 MXL_MaximumMIPLevel : 3 <comment="0-6">;
        SCE_GS_MAG MMAG : 1 <comment="Filter when Texture is Expanded (LOD < 0) - Max LINEAR">;
        SCE_GS_MAG MMIN : 3 <comment="Filter when Texture is Reduced (LOD >= 0)">;
    
        // 0 = Value specified by MIPTBP1 and MIPTBP2 is used
        // 1 = Base address of TBP1 - TBP3 is automatically set
        int64 MTBA_unknown : 1 <comment="Base Address specification of Mipmap Texture of Level 1 or more">;
        int64 pad10 : 9;
        int64 L : 2 <comment="LOD Parameter Value L">;
        int64 pad21 : 11;
        int64 K : 12 <comment="LOD Parameter Value K">;
        int64 pad44 :20;
    } sceGsTex1;

    struct sceGsMiptbp1
    {
        unsigned long TBP1:14;
        unsigned long TBW1:6;
        unsigned long TBP2:14;
        unsigned long TBW2:6;
        unsigned long TBP3:14;
        unsigned long TBW3:6;
        unsigned long pad60:4;
    } MipTable1;
    
    struct sceGsMiptbp2
    {
        unsigned long TBP4:14;
        unsigned long TBW4:6;
        unsigned long TBP5:14;
        unsigned long TBW5:6;
        unsigned long TBP6:14;
        unsigned long TBW6:6;
        unsigned long pad60:4;
    } MipTable2;

    struct 
    {
        SCE_GS_CLAMP_PARAMS WMS_WrapModeS:2 <comment="Wrap Mode in Horizontal (S) Direction">;
        SCE_GS_CLAMP_PARAMS WMT_VrapModeT:2 <comment="Wrap Mode in Horizontal (T) Direction">;
        unsigned long MINU:10 <comment="Clamp U Direction - Lower Limit">;
        unsigned long MAXU:10 <comment="Clamp U Direction - Upper Limit">;
        unsigned long MINV:10 <comment="Clamp Y Direction - Lower Limit">;
        unsigned long MAXV:10 <comment="Clamp Y Direction - Upper Limit">;
        
        Printf("%dx%d (%s, %s)\n", MAXU+1, MAXV+1, EnumToString(WMS_WrapModeS), EnumToString(WMT_VrapModeT));
        unsigned long pad44:20;
    } sceGsClamp <optimize=false>;

} PGLUTexture <optimize=false>;

typedef struct
{
    int FileDataOffset <format=hex, fgcolor=cRed>;
    
    struct
    {
        short BlockOffset <format=hex, comment=PrintBufferSize>;
        byte BufferWidth <comment="Same as TBW">;
        SCE_GS_PSM PixelFormat;
   } Params;

   short Width;
   short Height;

} TransferInfo <optimize=false, comment="Declares a buffer for textures or palettes to use">;

// GS's Users Manual - Page 161 to 170 are useful for understanding blocks and pages
typedef struct TextureSet1
{
    local int BasePos = FTell();
    struct
    {
        char Magic[4];
        int RelocPtr;
        int unkptr;
        int FileSize <format=hex>;
    
        short BaseTBPOffset <comment="Remapped at runtime, multiplied by 64 for absolute">;
        short TotalBlockSize <format=hex, comment="Total blocks taken by all textures">;
        short PGLUTextureCount <fgcolor=cGreen>;
        short TransferCount <fgcolor=cGreen>;
        int PGLUTextureMapOffset <format=hex, fgcolor=cRed>;
        int TransferInfosOffset  <format=hex, fgcolor=cRed>;
        int ClutPatchesOffset <format=hex, fgcolor=cRed, comment="Used for cars">;
        int clutAnimationOffset <format=hex, fgcolor=cRed>; // TODO - havent seen any texture that uses that yet
        int unkOffset4 <format=hex, fgcolor=cRed>;
    } Header <size=0x30 , bgcolor=cPurple>;

    FSeek(BasePos + Header.PGLUTextureMapOffset);
    PGLUTexture Textures[Header.PGLUTextureCount];
    
    FSeek(BasePos + Header.TransferInfosOffset);
    TransferInfo Transfers[Header.TransferCount];
    
    if (Header.ClutPatchesOffset != 0)
    {
        FSeek(BasePos + Header.ClutPatchesOffset);
        struct
        {
            int ClutPatchCount;
            int ClutPatchOffsets[ClutPatchCount] <format=hex>;
            
            local int i = 0;
            for (i = 0; i < ClutPatchCount; i++)
            {
                FSeek(BasePos + ClutPatchOffsets);
                
                struct
                {
                    int NumPGLUTexturesToPatch;
                    struct
                    {
                        int CSA_ClutEntryOffset : 5;
                        int CBP_CLUTBufferBasePointer : 14 <format=hex>;
                        SCE_GS_PSM Format : 4;
                        int PGLUTextureIndex : 9;
                    } PGLUTex0TexturePatch[NumPGLUTexturesToPatch] <optimize=false>;
                } ClutPatch;
            }
        } ClutPatches;
    }
}; 

local int LastTexSetOffset;

typedef struct Images
{
    char Magic[4];
    int RelocPtr;
    int TextureCount;
    int TextureDataOffset;

    LastTexSetOffset = TextureDataOffset;

    struct
    {
        char Name[0x3C];
        int TexSetIndex;        

        local int texSetSize = ReadInt(LastTexSetOffset + 0x0C);
        local int lastPos = FTell();
        FSeek(LastTexSetOffset);
        TextureSet1 TexSet;

        LastTexSetOffset += texSetSize;
        FSeek(lastPos);
    } TextureName[TextureCount] <optimize=false>;
};

// GT3 'imgs'
if (ReadInt(0x00) == 0x53474D49)
    Images imgs;
else if (ReadInt(0x00) == 0x31786554)
   TextureSet1 tex1; 

And here is my noesis script... Thanks in advance!

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Gran Turismo 4 - Texture", ".img")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadRGBA(handle, noepyLoadRGBA)
   noesis.logPopup()
   return 1
        
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   if bs.readUInt() != 0x31786554:
      return 0
   return 1
   
def noepyLoadRGBA(data, texList):
    bs = NoeBitStream(data)
    baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
    print(baseName)
    Magic = bs.readUInt()
    bs.readBytes(8)
    FileSize = bs.readUInt()
    bs.readBytes(2)
    TotalBlockSize = bs.readUShort()
    PGLUTextureCount = bs.readUShort()
    TransferCount = bs.readUShort()
    PGLUTextureMapOffset = bs.readUInt()
    TransferInfosOffset = bs.readUInt()
    ClutPatchesOffset = bs.readUInt()
    ClutAnimationOffset = bs.readUInt()
    UnknownOffset = bs.readUInt()
    bs.readBytes(4)
    # End of Header
    
    # Texture Table
    for i in range(PGLUTextureCount):
        bs.readBytes(40)
    
    # Transfer Table
    bs.seek(TransferInfosOffset, NOESEEK_ABS)
    if TransferCount == 1:
        TexDataOffset = bs.readUInt()
        BlockOffset = bs.readUShort()
        BufferWidth = bs.readUByte()
        PixelFormat = bs.readUByte()
        TexWidth = bs.readUShort()
        TexHeight = bs.readUShort()
    else:    
        TexDataOffset = bs.readUInt()
        BlockOffset = bs.readUShort()
        BufferWidth = bs.readUByte()
        PixelFormat = bs.readUByte()
        TexWidth = bs.readUShort()
        TexHeight = bs.readUShort()
        PalDataOffset = bs.readUInt()
        PalBlockOffset = bs.readUShort()
        PalBufferWidth = bs.readUByte()
        PalPixelFormat = bs.readUByte()
        PalTexWidth = bs.readUShort()
        PalTexHeight = bs.readUShort()
    
    if PixelFormat == 20:
        bs.seek(PalDataOffset, NOESEEK_ABS)
        Palette = bs.readBytes(64)
    elif PixelFormat == 19:
        bs.seek(PalDataOffset, NOESEEK_ABS)
        Palette = bs.readBytes(1024)

    if TransferCount == 2:
        TexSize = PalDataOffset - TexDataOffset
    else:
        TexSize = FileSize - TexDataOffset
    
    bs.seek(TexDataOffset, NOESEEK_ABS)        
    data = bs.readBytes(TexSize)
    if PixelFormat == 20:
        data = rapi.imageDecodeRawPal(data, Palette, TexWidth, TexHeight, 4, "r8 g8 b8 a8")
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 19:
        data = rapi.imageDecodeRawPal(data, Palette, TexWidth, TexHeight, 8, "r8 g8 b8 a8") # I have no idea how to process
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 0:
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 1:
        texFmt = noesis.NOESISTEX_RGB24
    texList.append(NoeTexture(rapi.getInputName(), TexWidth, TexHeight, data, texFmt))
    return 1

 

gt4img.7z

Link to comment
Share on other sites

  • Solution
1 hour ago, h3x3r said:

Hello there!

Can somebody please help update my noesis script to support this format? 

SCE_GS_PSMT8 = 19, // 8-bit indexed, packing 4 pixels per 32-bit.

So far I came across 4 formats and this one is something... Don't know how to process it in noesis.

Here is 010 Template from Nenkai

  Reveal hidden contents

//------------------------------------------------
//--- 010 Editor v10.0.2 Binary Template
//
//      File: PGLUTexSet
//   Authors: Nenkai#9075
//   Version: 
//   Purpose: Texture Set for PS2 PDI Games
//  Category: Game Sprites
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------


BitfieldDisablePadding();

string PrintBufferSize(int64 val)
{
    string s;
    SPrintf(s, "Same as tbp/cbp, gets remapped at runtime - offset: 0x%x (0x%x * 64)", val * 64, val);
    return s;
}

typedef enum <byte>
{
    SCE_GS_NEAREST = 0,
    SCE_GS_LINEAR = 1,
    SCE_GS_NEAREST_MIPMAP_NEAREST = 2,
    SCE_GS_NEAREST_MIPMAP_LINEAR = 3,
    SCE_GS_LINEAR_MIPMAP_NEAREST = 4,
    SCE_GS_LINEAR_MIPMAP_LINEAR = 5,
} SCE_GS_MAG;

typedef enum <byte> 

    SCE_GS_PSMCT32 = 0, // RGBA32, uses 32-bit per pixel.
    SCE_GS_PSMCT24 = 1, // RGB24, uses 24-bit per pixel with the upper 8 bit unused.
    SCE_GS_PSMCT16 = 2, // RGBA16 unsigned, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMCT16S = 10, // RGBA16 signed, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMT8 = 19, // 8-bit indexed, packing 4 pixels per 32-bit.
    SCE_GS_PSMT4 = 20, // 4-bit indexed, packing 8 pixels per 32-bit.
    SCE_GS_PSMT8H = 27, // 8-bit indexed, but the upper 24-bit are unused.
    SCE_GS_PSMT4HL = 36, // 4-bit indexed, but the upper 24-bit are unused.
    SCE_GS_PSMT4HH = 44,
    SCE_GS_PSMZ32 = 48,  // 32-bit Z buffer
    SCE_GS_PSMZ24 = 49, // 24-bit Z buffer with the upper 8-bit unused
    SCE_GS_PSMZ16 = 50, // 16-bit unsigned Z buffer, pack two pixels in 32-bit in little endian order.
    SCE_GS_PSMZ16S = 58, // 16-bit signed Z buffer, pack two pixels in 32-bit in little endian order.
} SCE_GS_PSM;

typedef enum <byte>
{
    SCE_GS_REPEAT = 0,
    SCE_GS_CLAMP = 1,
    SCE_GS_REGION_CLAMP = 2,
    SCE_GS_REGION_REPEAT = 3,
} SCE_GS_CLAMP_PARAMS;

typedef enum <byte> { CSM1, CSM2 } SCE_GS_CSM;

typedef struct
{
    struct
    {
        int64 TBP0_TextureBasePointer  : 14 <format=hex, comment="address / 64 (words aka 4) - this is the block offset to the texture">;
        int64 TBW_TextureBufferWidth : 6 <format=hex, comment="Texel Unit Width / 64 (words aka 4)">;
        SCE_GS_PSM PSM_TexturePixelStorageFormat : 6 <comment="Texture pixel storage format">;
        int64 TW_TextureWidth : 4 <comment="Texture width - Actual size will be 2^w and 2^h">;
        int64 TH_TextureHeight : 4 <comment="Texture height - Actual size will be 2^w and 2^h">;
        int64 TCC_TextureColorComponent : 1 <comment="Texture color component, 0 = RGB, 1 = RGBA">;
        int64 TFX_TextureFunction : 2 <comment="Texture function">;
        int64 CBP_CLUTBufferBasePointer : 14 <format=hex, comment="Base address of CLUT data (actual address will be cbp x 64)">;
        SCE_GS_PSM CPSM_ClutPixelStorageFormat : 4 <comment="Format in which CLUT entries are saved">;
        SCE_GS_CSM CSM_ClutStorageMode : 1 <comment="CLUT storage mode">;
        int64 CSA_ClutEntryOffset : 5 <comment="CLUT entry offset - CSA = Offset / 16, In CSM2, CSA must be 0">;
        // 0 = Temporary buffer contents not changed
        // 1 = Load performed to CSA position of buffer
        // 2 = Load is performed to CSA position of buffer and CBP is copied to CBP0. (*2)
        // 3 = Load is performed to CSA position of buffer and CBP is copied to CBP1. (*2)
        // 4 = If CBP0 != CBP, load is performed and CBP is copied to CBP0. (*2)
        // 5 = If CBP1 != CBP, load is performed and CBP is copied to CBP1. (*2)
        int64 CLD_ClutBufferLoadControl : 3 <comment="Check template comment">;
    } sceGsTex0;

    struct
    {
        int64 LCM_LODCalculationMethod : 1 <comment="0 = (LOD = (log2(1/|Q|)<<L+K), 1 = Fixed value (LOD = K)">;
        int64 pad01 : 1;
        int64 MXL_MaximumMIPLevel : 3 <comment="0-6">;
        SCE_GS_MAG MMAG : 1 <comment="Filter when Texture is Expanded (LOD < 0) - Max LINEAR">;
        SCE_GS_MAG MMIN : 3 <comment="Filter when Texture is Reduced (LOD >= 0)">;
    
        // 0 = Value specified by MIPTBP1 and MIPTBP2 is used
        // 1 = Base address of TBP1 - TBP3 is automatically set
        int64 MTBA_unknown : 1 <comment="Base Address specification of Mipmap Texture of Level 1 or more">;
        int64 pad10 : 9;
        int64 L : 2 <comment="LOD Parameter Value L">;
        int64 pad21 : 11;
        int64 K : 12 <comment="LOD Parameter Value K">;
        int64 pad44 :20;
    } sceGsTex1;

    struct sceGsMiptbp1
    {
        unsigned long TBP1:14;
        unsigned long TBW1:6;
        unsigned long TBP2:14;
        unsigned long TBW2:6;
        unsigned long TBP3:14;
        unsigned long TBW3:6;
        unsigned long pad60:4;
    } MipTable1;
    
    struct sceGsMiptbp2
    {
        unsigned long TBP4:14;
        unsigned long TBW4:6;
        unsigned long TBP5:14;
        unsigned long TBW5:6;
        unsigned long TBP6:14;
        unsigned long TBW6:6;
        unsigned long pad60:4;
    } MipTable2;

    struct 
    {
        SCE_GS_CLAMP_PARAMS WMS_WrapModeS:2 <comment="Wrap Mode in Horizontal (S) Direction">;
        SCE_GS_CLAMP_PARAMS WMT_VrapModeT:2 <comment="Wrap Mode in Horizontal (T) Direction">;
        unsigned long MINU:10 <comment="Clamp U Direction - Lower Limit">;
        unsigned long MAXU:10 <comment="Clamp U Direction - Upper Limit">;
        unsigned long MINV:10 <comment="Clamp Y Direction - Lower Limit">;
        unsigned long MAXV:10 <comment="Clamp Y Direction - Upper Limit">;
        
        Printf("%dx%d (%s, %s)\n", MAXU+1, MAXV+1, EnumToString(WMS_WrapModeS), EnumToString(WMT_VrapModeT));
        unsigned long pad44:20;
    } sceGsClamp <optimize=false>;

} PGLUTexture <optimize=false>;

typedef struct
{
    int FileDataOffset <format=hex, fgcolor=cRed>;
    
    struct
    {
        short BlockOffset <format=hex, comment=PrintBufferSize>;
        byte BufferWidth <comment="Same as TBW">;
        SCE_GS_PSM PixelFormat;
   } Params;

   short Width;
   short Height;

} TransferInfo <optimize=false, comment="Declares a buffer for textures or palettes to use">;

// GS's Users Manual - Page 161 to 170 are useful for understanding blocks and pages
typedef struct TextureSet1
{
    local int BasePos = FTell();
    struct
    {
        char Magic[4];
        int RelocPtr;
        int unkptr;
        int FileSize <format=hex>;
    
        short BaseTBPOffset <comment="Remapped at runtime, multiplied by 64 for absolute">;
        short TotalBlockSize <format=hex, comment="Total blocks taken by all textures">;
        short PGLUTextureCount <fgcolor=cGreen>;
        short TransferCount <fgcolor=cGreen>;
        int PGLUTextureMapOffset <format=hex, fgcolor=cRed>;
        int TransferInfosOffset  <format=hex, fgcolor=cRed>;
        int ClutPatchesOffset <format=hex, fgcolor=cRed, comment="Used for cars">;
        int clutAnimationOffset <format=hex, fgcolor=cRed>; // TODO - havent seen any texture that uses that yet
        int unkOffset4 <format=hex, fgcolor=cRed>;
    } Header <size=0x30 , bgcolor=cPurple>;

    FSeek(BasePos + Header.PGLUTextureMapOffset);
    PGLUTexture Textures[Header.PGLUTextureCount];
    
    FSeek(BasePos + Header.TransferInfosOffset);
    TransferInfo Transfers[Header.TransferCount];
    
    if (Header.ClutPatchesOffset != 0)
    {
        FSeek(BasePos + Header.ClutPatchesOffset);
        struct
        {
            int ClutPatchCount;
            int ClutPatchOffsets[ClutPatchCount] <format=hex>;
            
            local int i = 0;
            for (i = 0; i < ClutPatchCount; i++)
            {
                FSeek(BasePos + ClutPatchOffsets);
                
                struct
                {
                    int NumPGLUTexturesToPatch;
                    struct
                    {
                        int CSA_ClutEntryOffset : 5;
                        int CBP_CLUTBufferBasePointer : 14 <format=hex>;
                        SCE_GS_PSM Format : 4;
                        int PGLUTextureIndex : 9;
                    } PGLUTex0TexturePatch[NumPGLUTexturesToPatch] <optimize=false>;
                } ClutPatch;
            }
        } ClutPatches;
    }
}; 

local int LastTexSetOffset;

typedef struct Images
{
    char Magic[4];
    int RelocPtr;
    int TextureCount;
    int TextureDataOffset;

    LastTexSetOffset = TextureDataOffset;

    struct
    {
        char Name[0x3C];
        int TexSetIndex;        

        local int texSetSize = ReadInt(LastTexSetOffset + 0x0C);
        local int lastPos = FTell();
        FSeek(LastTexSetOffset);
        TextureSet1 TexSet;

        LastTexSetOffset += texSetSize;
        FSeek(lastPos);
    } TextureName[TextureCount] <optimize=false>;
};

// GT3 'imgs'
if (ReadInt(0x00) == 0x53474D49)
    Images imgs;
else if (ReadInt(0x00) == 0x31786554)
   TextureSet1 tex1; 

And here is my noesis script... Thanks in advance!

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Gran Turismo 4 - Texture", ".img")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadRGBA(handle, noepyLoadRGBA)
   noesis.logPopup()
   return 1
        
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   if bs.readUInt() != 0x31786554:
      return 0
   return 1
   
def noepyLoadRGBA(data, texList):
    bs = NoeBitStream(data)
    baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
    print(baseName)
    Magic = bs.readUInt()
    bs.readBytes(8)
    FileSize = bs.readUInt()
    bs.readBytes(2)
    TotalBlockSize = bs.readUShort()
    PGLUTextureCount = bs.readUShort()
    TransferCount = bs.readUShort()
    PGLUTextureMapOffset = bs.readUInt()
    TransferInfosOffset = bs.readUInt()
    ClutPatchesOffset = bs.readUInt()
    ClutAnimationOffset = bs.readUInt()
    UnknownOffset = bs.readUInt()
    bs.readBytes(4)
    # End of Header
    
    # Texture Table
    for i in range(PGLUTextureCount):
        bs.readBytes(40)
    
    # Transfer Table
    bs.seek(TransferInfosOffset, NOESEEK_ABS)
    if TransferCount == 1:
        TexDataOffset = bs.readUInt()
        BlockOffset = bs.readUShort()
        BufferWidth = bs.readUByte()
        PixelFormat = bs.readUByte()
        TexWidth = bs.readUShort()
        TexHeight = bs.readUShort()
    else:    
        TexDataOffset = bs.readUInt()
        BlockOffset = bs.readUShort()
        BufferWidth = bs.readUByte()
        PixelFormat = bs.readUByte()
        TexWidth = bs.readUShort()
        TexHeight = bs.readUShort()
        PalDataOffset = bs.readUInt()
        PalBlockOffset = bs.readUShort()
        PalBufferWidth = bs.readUByte()
        PalPixelFormat = bs.readUByte()
        PalTexWidth = bs.readUShort()
        PalTexHeight = bs.readUShort()
    
    if PixelFormat == 20:
        bs.seek(PalDataOffset, NOESEEK_ABS)
        Palette = bs.readBytes(64)
    elif PixelFormat == 19:
        bs.seek(PalDataOffset, NOESEEK_ABS)
        Palette = bs.readBytes(1024)

    if TransferCount == 2:
        TexSize = PalDataOffset - TexDataOffset
    else:
        TexSize = FileSize - TexDataOffset
    
    bs.seek(TexDataOffset, NOESEEK_ABS)        
    data = bs.readBytes(TexSize)
    if PixelFormat == 20:
        data = rapi.imageDecodeRawPal(data, Palette, TexWidth, TexHeight, 4, "r8 g8 b8 a8")
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 19:
        data = rapi.imageDecodeRawPal(data, Palette, TexWidth, TexHeight, 8, "r8 g8 b8 a8") # I have no idea how to process
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 0:
        texFmt = noesis.NOESISTEX_RGBA32
    elif PixelFormat == 1:
        texFmt = noesis.NOESISTEX_RGB24
    texList.append(NoeTexture(rapi.getInputName(), TexWidth, TexHeight, data, texFmt))
    return 1

 

gt4img.7z 51.28 kB · 2 downloads

If you change your line in the script to:

data = rapi.imageDecodeRawPal(data, Palette, TexWidth, TexHeight, 8, "r8 g8 b8 a8", noesis.DECODEFLAG_PS2SHIFT)

Seems to look correct with that flag enabled.

 

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

  • 3 weeks later...
  • Engineer

Well seems like i can't get over these. It looks like they are stored as blocks.

I noticed that file 0x0005da40.img is constructed from 2 images leads to single one.

Also can't believe it that file 0x0002cb40.img store over 70+ textures. But maybe yes since they are all low res.

Use template to show more info.

If anyone know a way please post a solution. Noesis script would be perfect but anything else also. Thanks in advance!

gt4_tex.7z

Link to comment
Share on other sites

  • 4 weeks later...

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...