From 4e755f8797eb3b8cba68a3fa3d0e7f9d4e63dfd3 Mon Sep 17 00:00:00 2001 From: dpethes Date: Sat, 18 Oct 2014 22:12:27 +0200 Subject: [PATCH] hmt parser: proper texture entry parsing. Actual pixel data is not read yet --- doc/file_hmt_spec.txt | 29 +++-- hmt_parser/hmt_parser.pas | 105 ++++++++++++----- hmt_parser/rs_image.pas | 238 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+), 38 deletions(-) create mode 100644 hmt_parser/rs_image.pas diff --git a/doc/file_hmt_spec.txt b/doc/file_hmt_spec.txt index 797859f..84c250d 100644 --- a/doc/file_hmt_spec.txt +++ b/doc/file_hmt_spec.txt @@ -8,24 +8,35 @@ MC * material entry (36B) 2B int 0, 1 (trooper, mat2) 4B float (misc) 4B float (1.0) - 4B int always zero - 4B int always 0x0A + 4B int zero + 4B int 0x0A 16B name } 4B int texcount Tc -TC * texture entry +TC * texture entry 52B { - 4B int texture data offset - 28B - 4B int ? texname offset + 16, sometimes + 4B int pixel offset + 28B zero + 4B int palette offset 4B int texname offset 2B int width 2B int height - 8B ? texture format? + 8B texture format { + 1B 0x01 + 1B bits per pixel: + -0: 4bit + -1: 8bit + 1B subtype: + -3 RGBA + -4 greyscale + -5 ? ov_rdir + 1B 0, 0x40, 0x80 + 4B RGBA transparent color? + } } TC * texture data { 16B texname = material name - 48B 16xRGB palette pri r_pilot, e_core, moze byt iny pocet - N * texture pixels, grayscale 4bit + xB RGB palette + xB pixels/samples } diff --git a/hmt_parser/hmt_parser.pas b/hmt_parser/hmt_parser.pas index f487459..6eebd29 100644 --- a/hmt_parser/hmt_parser.pas +++ b/hmt_parser/hmt_parser.pas @@ -4,7 +4,8 @@ unit hmt_parser; interface uses - sysutils, Classes; + sysutils, Classes, + rs_image; type THmtMaterial = record @@ -17,7 +18,10 @@ type THmtTexture = record data_offset: integer; - unknown: array[0..47] of byte; + palette_offset: integer; + name_offset: integer; + width, height: word; + name: array[0..15] of byte; end; THmtFile = record @@ -28,7 +32,7 @@ type textures: array of THmtTexture; end; -procedure ParseHmtFile(const fname: string); + function ParseHmtFile(const fname: string): THmtFile; //************************************************************************************************** implementation @@ -44,12 +48,67 @@ begin end; end; -procedure ParseHmtFile(const fname: string); + +procedure ReadTexture(var tex: THmtTexture; var f: TMemoryStream); +var + image: TRSImage; + buf: array[0..27] of byte; + description: TImageDescription; + bpp: byte; + color_rgba: integer; + pos: int64; +begin + tex.data_offset := f.ReadDWord; + f.ReadBuffer(buf, 28); + tex.palette_offset := f.ReadDWord; + tex.name_offset := f.ReadDWord; + tex.width := f.ReadWord; + tex.height := f.ReadWord; + + f.ReadByte; //0x01 + bpp := f.ReadByte; + image.type_ := f.ReadByte; + f.ReadByte; + color_rgba := f.ReadDWord; + + pos := f.Position; + f.Seek(tex.name_offset, TSeekOrigin.soBeginning); + f.ReadBuffer(tex.name, 16); + f.Seek(pos, TSeekOrigin.soBeginning); + + description := ImageDescription[image.type_]; + image.sampleBits := description.sample_bits; + image.paletteEntries := description.palette_entries; + if image.type_ = 4 then + image.sampleBits := bpp * 4 + 4; + + writeln(NameToString(tex.name)); + writeln('size: ', tex.width, 'x', tex.height); + writeln('subtype: ', image.type_); + writeln('sample bits: ', image.sampleBits); +end; + + +procedure ReadMaterial(var mat: THmtMaterial; var f: TMemoryStream); +begin + mat.type1 := f.ReadWord; + mat.type2 := f.ReadWord; + mat.unknown_float1 := f.ReadDWord; + mat.unknown_float2 := f.ReadDWord; + mat.zero := f.ReadDWord; + mat.hex_a := f.ReadDWord; + f.ReadBuffer(mat.name, 16); + + writeln(NameToString(mat.name)); + if (mat.zero <> 0) or (mat.hex_a <> $A) then + writeln('unusual file'); +end; + + +function ParseHmtFile(const fname: string): THmtFile; var f: TMemoryStream; hmt: THmtFile; - mat: THmtMaterial; - tex: THmtTexture; i: Integer; begin f := TMemoryStream.Create; @@ -62,41 +121,29 @@ begin hmt.texture_count := f.ReadDWord; f.Seek(8, TSeekOrigin.soBeginning); - writeln('materials: ', hmt.material_count); - writeln('textures: ', hmt.texture_count); - writeln(' texture bytes: ', f.Size - sizeof(hmt.texture_count) - hmt.texture_offset); - //read materials + writeln('materials: ', hmt.material_count); SetLength(hmt.materials, hmt.material_count); for i := 0 to hmt.material_count - 1 do begin - mat.type1 := f.ReadWord; - mat.type2 := f.ReadWord; - mat.unknown_float1 := f.ReadDWord; - mat.unknown_float2 := f.ReadDWord; - mat.zero := f.ReadDWord; - mat.hex_a := f.ReadDWord; - f.ReadBuffer(mat.name, 16); - - hmt.materials[i] := mat; + ReadMaterial(hmt.materials[i], f); end; - for mat in hmt.materials do begin - writeln(NameToString(mat.name)); - if (mat.zero <> 0) or (mat.hex_a <> $A) then - writeln('unusual file'); + if hmt.texture_count = 0 then begin + result := hmt; + f.Free; + exit; end; //read textures - if hmt.texture_count = 0 then - exit; - f.Seek(hmt.texture_offset + sizeof(hmt.texture_count), fsFromCurrent); + writeln('textures: ', hmt.texture_count); + f.Seek(hmt.texture_offset + 4, TSeekOrigin.soBeginning); SetLength(hmt.textures, hmt.texture_count); for i := 0 to hmt.texture_count - 1 do begin - tex.data_offset := f.ReadDWord; - f.ReadBuffer(tex.unknown, 48); + ReadTexture(hmt.textures[i], f); end; - + f.Free; + result := hmt; end; end. diff --git a/hmt_parser/rs_image.pas b/hmt_parser/rs_image.pas new file mode 100644 index 0000000..d909cf0 --- /dev/null +++ b/hmt_parser/rs_image.pas @@ -0,0 +1,238 @@ +unit rs_image; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +type + TRGB = array[0..2] of byte; + PRGB = ^TRGB; + TPalette = array[0..256] of TRGB; + + TRSImage = record + data_size: integer; + width, height: integer; + type_: byte; + sampleBits: byte; + paletteEntries: integer; + pixels: pbyte; + samples: pbyte; + palette: TPalette; + end; + +type + TImageDescription = record + palette_entries: integer; + sample_bits: integer; + //alpha: byte; + end; + +const + ImageDescription: array[0..5] of TImageDescription = ( + (palette_entries: 16; sample_bits: 4), + (palette_entries: 256; sample_bits: 8), + (palette_entries: 0; sample_bits: 16), + (palette_entries: 0; sample_bits: 32), + (palette_entries: 0; sample_bits: 4), + (palette_entries: 0; sample_bits: 16) + ); + + +function LoadImageFromPack(var f: file): TRSImage; + +//************************************************************************************************** +implementation + +procedure Unpack4To8bit(const src: PByte; const samples: integer; const dst: PByte); +var + i: Integer; + v: byte; +begin + for i := 0 to samples div 2 - 1 do begin + v := src[i]; + dst[i * 2 ] := ((v shr 4) and %1111) shl 4; + dst[i * 2 + 1] := (v and %1111) shl 4; + end; +end; + + +procedure Unpack4bitTo24bitRGB(const src: PByte; const size: integer; const dst: PByte; const pal: TPalette); +var + i: Integer; + index: integer; + dest: PRGB; +begin + dest := PRGB(dst); + for i := 0 to size div 2 - 1 do begin + index := src[i]; + dest[i * 2 ] := pal[(index shr 4) and 15]; + dest[i * 2 + 1] := pal[index and 15]; + end; +end; + + +procedure Unpack8bitTo24bitRGB(const src: PByte; const size: integer; const dst: PByte; const pal: TPalette); +var + i: Integer; + index: integer; + dest: PRGB; +begin + dest := PRGB(dst); + for i := 0 to size - 1 do begin + index := src[i]; + dest[i] := pal[index]; + end; +end; + + +procedure UseOddBytes(const src: PByte; const size: integer; const dst: pbyte); +var + i: integer; +begin + for i := 0 to size - 1 do begin + dst[i] := src[i * 2 + 1]; + end; +end; + + +procedure DecodePixels(var img: TRSImage); +var + size: integer; +begin + img.pixels := nil; + if not(img.type_ in [0, 1, 2, 3, 4, 5]) then exit; + + if img.sampleBits = 32 then begin + size := img.width * img.height * 4; + img.pixels := GetMem(size); + Move(img.samples^, img.pixels^, size); + end; + + if img.sampleBits = 4 then begin + //4bit grayscale + if img.paletteEntries = 0 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + Unpack4To8bit(img.samples, size, img.pixels); + end; + //24bit RGB palettized + if img.paletteEntries = 16 then begin + size := img.width * img.height; + img.pixels := GetMem(size * 3); + Unpack4bitTo24bitRGB(img.samples, size, img.pixels, img.palette); + end; + end; + + if img.sampleBits = 8 then begin + //8bit grayscale + if img.paletteEntries = 0 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + move(img.samples^, img.pixels^, size); + end; + //24bit RGB palettized + if img.paletteEntries = 256 then begin + size := img.width * img.height; + img.pixels := GetMem(size * 3); + Unpack8bitTo24bitRGB(img.samples, size, img.pixels, img.palette); + end; + end; + + if img.sampleBits = 16 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + UseOddBytes(img.samples, size, img.pixels); + end; +end; + + +procedure LoadPalette(var f: file; var image: TRSImage); +var + entries: integer; +begin + entries := image.paletteEntries; + case entries of + 16: Blockread(f, image.palette, entries * 3); //RGB + 256: Blockread(f, image.palette, entries * 3); //RGB + end; +end; + + +procedure LoadSamples(var f: file; var image: TRSImage); +var + sample_bits: integer; + size: integer; +begin + sample_bits := image.sampleBits; + size := image.width * image.height * sample_bits div 8; + image.samples := getmem(size); + Blockread(f, image.samples^, size); + if image.type_ = 2 then + Blockread(f, image.samples^, size div 4); +end; + + +procedure LoadImageHeader(var f: file; var image: TRSImage); +var + h: word; + w: word; + buffer: array[0..15] of byte; + description: TImageDescription; + bpp: byte; +begin + blockread(f, w, 2); + blockread(f, h, 2); + blockread(f, buffer, 4); + blockread(f, buffer[8], 4); //zero padding + + w := w + (w and 1); //make width even + image.width := w; + image.height := h; + bpp := buffer[1]; + image.type_ := buffer[2]; //image type + + description := ImageDescription[image.type_]; + image.sampleBits := description.sample_bits; + image.paletteEntries := description.palette_entries; + if image.type_ = 4 then + image.sampleBits := bpp * 4 + 4; + + writeln('data size: ', image.data_size); + writeln('size: ', image.width, 'x', image.height); + writeln('subtype: ', image.type_); + writeln('sample bits: ', image.sampleBits); + writeln('attrs: ', buffer[0], ', ', buffer[1], ', ', buffer[3]); +end; + + +procedure LoadName(var f: file; const data_size: integer); +var + i: integer; + buffer: array[0..15] of byte; + s: string; +begin + s := ''; + blockread(f, buffer, data_size); + for i := 0 to data_size - 1 do + s += char(buffer[i]); + s := Trim(s); + writeln('name: ', s); +end; + + +function LoadImageFromPack(var f: file): TRSImage; +var + offset, string_offset: integer; + buffer: array[0..31] of byte; +begin + LoadPalette(f, result); + LoadSamples(f, result); + DecodePixels(result); + LoadName(f, result.data_size - string_offset); +end; + +end. +