Requesting assistance on Delphi sourcecode (Meen IMG2BMP)
Posted: September 30th, 2012, 2:02 am
In previous post I made introducing information on I.M.Meen which I transferred and expanded on in the Modding Wiki, I am lacking information on masking which was not fully explained in the topic but featured in the tool. Delphi is a language I barely understand. I'm no programmer though, more of a minor code revision/augment is my height. Usually I just do things directly by hex and see what happens.
I can't quite gather this (the handling of masking) into an explainable format (the only thing I do not have documented regarding Meen IMGs). The tool obviously too has no handling for images larger than 64x64 and recursive indexes (as those used in Chill Manor), and the usage of a predefined palette array rather than loading from the source makes little sense to me and seems... to be an unnecessarily complicated brute-force method. Then again, do not know if Delphi has binary source read capabilities or a binary-to-array procedure as I've seen with C++/VB. Ah well... in any case, any help is appreciated.
Code: Select all
(* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* IMG2BMP.DPR (Delphi 7 project) *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* I.M.Meen/Chill Manor (1995 PC/DOS) IMG2BMP *
* Author: WRS (xentax.com) *
* Thread: http://forum.xentax.com/viewtopic.php?f=18&t=3929 *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *)
program IMG2BMP;
{$APPTYPE CONSOLE}
uses
Windows,
Classes,
SysUtils,
Graphics;
const
// image dimentions
IMG_MAX_WIDTH = 64;
IMG_MAX_HEIGHT = 64;
// palette from "MeenMapPalette.bin" (formatting was automated!)
IMG_PALETTE : array[0..767] of Byte = (
$00,$00,$00, $00,$00,$BC, $00,$A8,$00, $00,$A8,$A8, $A8,$00,$00, $A8,$00,$A8, $A8,$54,$00, $A8,$A8,$A8,
$54,$54,$54, $54,$54,$FC, $54,$FC,$54, $54,$FC,$FC, $FC,$54,$54, $FC,$54,$FC, $FC,$FC,$54, $FC,$FC,$FC,
$F8,$54,$54, $F0,$44,$44, $E8,$30,$30, $E4,$20,$20, $DC,$10,$10, $D4,$00,$00, $C8,$00,$00, $BC,$00,$00,
$AC,$00,$00, $9C,$00,$00, $8C,$00,$00, $7C,$00,$00, $70,$00,$00, $64,$00,$00, $5C,$00,$00, $50,$00,$00,
$44,$00,$00, $38,$00,$00, $30,$00,$00, $28,$00,$00, $24,$00,$00, $1C,$00,$00, $14,$00,$00, $0C,$00,$00,
$D8,$70,$34, $C8,$64,$2C, $BC,$5C,$24, $AC,$50,$1C, $9C,$44,$10, $8C,$38,$08, $80,$30,$08, $78,$2C,$04,
$6C,$24,$04, $64,$20,$04, $58,$18,$00, $50,$10,$00, $4C,$10,$00, $48,$0C,$00, $44,$0C,$00, $40,$08,$00,
$3C,$08,$00, $38,$04,$00, $30,$04,$00, $28,$04,$00, $20,$04,$00, $18,$04,$00, $10,$00,$00, $0C,$00,$00,
$C0,$E4,$FC, $B0,$E0,$FC, $A8,$D4,$F0, $9C,$C8,$E4, $90,$BC,$D8, $84,$B0,$CC, $78,$A0,$C0, $6C,$94,$B0,
$64,$88,$A0, $58,$78,$90, $4C,$6C,$80, $44,$5C,$6C, $38,$4C,$5C, $2C,$40,$4C, $28,$38,$40, $20,$30,$38,
$1C,$28,$30, $18,$20,$24, $10,$18,$1C, $0C,$10,$14, $08,$0C,$10, $08,$0C,$0C, $04,$08,$0C, $04,$08,$08,
$F0,$F0,$94, $E4,$E4,$88, $D8,$D8,$78, $CC,$CC,$68, $B8,$B8,$60, $A8,$A8,$58, $98,$98,$50, $88,$88,$44,
$74,$74,$3C, $64,$64,$34, $58,$58,$2C, $50,$50,$28, $44,$44,$24, $38,$38,$1C, $30,$30,$18, $24,$24,$10,
$24,$20,$10, $20,$1C,$10, $1C,$18,$0C, $1C,$14,$0C, $14,$10,$08, $10,$0C,$08, $08,$08,$04, $04,$04,$00,
$F0,$D4,$B8, $E0,$C4,$A4, $D4,$B4,$94, $C4,$A4,$80, $B8,$94,$70, $A8,$84,$5C, $9C,$74,$48, $8C,$60,$38,
$80,$50,$24, $74,$48,$20, $68,$40,$18, $60,$38,$10, $54,$2C,$0C, $48,$24,$04, $40,$1C,$00, $38,$18,$00,
$30,$14,$00, $28,$10,$00, $20,$0C,$00, $18,$08,$00, $10,$04,$00, $0C,$04,$00, $0C,$04,$00, $08,$04,$00,
$E8,$C0,$FC, $E0,$A4,$FC, $D8,$88,$FC, $CC,$6C,$FC, $C4,$50,$FC, $BC,$34,$FC, $B4,$18,$FC, $A8,$00,$FC,
$9C,$00,$E8, $8C,$00,$D4, $7C,$00,$C0, $70,$00,$AC, $60,$00,$98, $50,$00,$84, $48,$00,$74, $3C,$00,$64,
$34,$00,$54, $2C,$00,$44, $20,$00,$34, $18,$00,$24, $14,$00,$20, $10,$00,$18, $0C,$00,$14, $08,$00,$0C,
$FC,$D0,$AC, $F8,$C8,$98, $F8,$BC,$88, $F8,$B4,$74, $F4,$A8,$64, $F4,$A0,$50, $F0,$94,$40, $F0,$88,$2C,
$EC,$80,$1C, $DC,$74,$14, $CC,$6C,$10, $BC,$60,$0C, $AC,$58,$04, $9C,$4C,$00, $8C,$44,$00, $78,$3C,$00,
$68,$30,$00, $54,$28,$00, $44,$1C,$00, $30,$14,$00, $28,$10,$00, $20,$0C,$00, $18,$08,$00, $10,$08,$00,
$E8,$E8,$E8, $DC,$DC,$DC, $D4,$D4,$D4, $C8,$C8,$C8, $BC,$BC,$BC, $B0,$B0,$B0, $B0,$B0,$B0, $A4,$A4,$A4,
$98,$98,$98, $8C,$8C,$8C, $80,$80,$80, $70,$70,$70, $68,$68,$68, $5C,$5C,$5C, $50,$50,$50, $48,$48,$48,
$3C,$3C,$3C, $30,$30,$30, $28,$28,$28, $20,$20,$20, $18,$18,$18, $10,$10,$10, $08,$08,$08, $00,$00,$00,
$F8,$F8,$D0, $F0,$F0,$C4, $EC,$E8,$B4, $E0,$E0,$B0, $D8,$D8,$A4, $CC,$CC,$94, $BC,$B8,$88, $A4,$A0,$74,
$94,$90,$64, $84,$7C,$54, $74,$6C,$48, $64,$5C,$40, $58,$54,$38, $50,$48,$34, $44,$40,$2C, $38,$34,$24,
$30,$2C,$20, $24,$20,$18, $1C,$1C,$14, $18,$14,$0C, $10,$0C,$08, $0C,$08,$04, $08,$04,$04, $00,$00,$00,
$C8,$F8,$AC, $B4,$F4,$98, $A0,$F0,$80, $88,$EC,$6C, $74,$E8,$58, $60,$E4,$44, $50,$D0,$38, $40,$C0,$30,
$30,$AC,$24, $20,$98,$1C, $10,$84,$10, $00,$70,$08, $00,$68,$08, $00,$5C,$04, $00,$50,$04, $00,$48,$04,
$00,$3C,$04, $00,$30,$04, $00,$2C,$00, $00,$24,$00, $00,$1C,$00, $00,$14,$00, $00,$0C,$00, $00,$04,$00
);
type
{
Sprite margins (from left side of image)
}
IMG_PADDED_HEAD = packed record
Left,
Right : Word;
end;
{
Line margins (from left side of image)
Line width is (EOL - SOL)
SOL ---> EOL
When EOL == 0, there isn't anymore data, and the structure stops
PIX is the base palette index pointer
}
IMG_PADDED_FOOT = packed record
EOL,
SOL,
PIX : SmallInt;
end;
var
IMGF : TMemoryStream; // file i/o
HeaderFlag,
tHeader,
Headers,
PaddedHeaders,
h,i,j : Word;
dOffset,
hCPos,
pxOrigin : LongWord;
px,
x,
y : Byte;
BMP : TBitmap; // writing bitmaps the dull way
PixelData : array[0..((IMG_MAX_WIDTH * IMG_MAX_HEIGHT) -1)] of Byte;
pHead : IMG_PADDED_HEAD;
Foot : IMG_PADDED_FOOT;
pFooter : Array of SmallInt;
TickCnt : LongWord; // speed testing
procedure PrintUsage;
begin
Write(' > Usage: '+ExtractFileName(ParamStr(0))+' <filename>'#13#10#13#10);
end;
procedure PreExit;
begin
Write('Press ENTER to exit...'#13#10);
Readln;
end;
// entry point
begin
// program info
Write('I.M.Meen/Chill Manor (1995 PC/DOS) IMG2BMP'#13#10);
Write(' Author:'#9'WRS (xentax.com)'#13#10);
Write(' Thread:'#9'http://forum.xentax.com/viewtopic.php?f=18&t=3929'#13#10#13#10);
// check we have an argument
if ParamCount() <> 1 then
begin
PrintUsage;
PreExit;
Exit;
end;
// check it's a file
if FileExists(ParamStr(1)) = false then
begin
Write(' > ERROR: Could not load "'+ParamStr(1)+'"'#13#10#13#10);
PreExit;
Exit;
end;
// load file
IMGF := TMemoryStream.Create;
IMGF.LoadFromFile(ParamStr(1));
// read in the header flag
IMGF.Read(HeaderFlag, 2);
{
if HeaderFlag == 1 then there are no padded images
else, there are more headers with padding image info
}
if HeaderFlag = 1 then
begin
IMGF.Read(Headers, 2);
PaddedHeaders := 0;
end
else
begin
Headers := 0;
IMGF.Read(tHeader, 2); Headers := Headers + tHeader;
IMGF.Read(tHeader, 2); Headers := Headers + tHeader;
IMGF.Read(PaddedHeaders, 2);
// skip other header info (null or otherwise unknown 16-bit integers)
IMGF.Seek(2 + (HeaderFlag * 2), soBeginning);
end;
Write(' > Expecting '+IntToStr(Headers+PaddedHeaders)+' files'#13#10);
// parse all square (no padding, 64x64) sprites
for h := 1 to Headers do
begin
// read sprite offset
IMGF.Read(dOffset, 4);
// save pos and goto sprite data
hCPos := IMGF.Position;
IMGF.Seek(dOffset, soBeginning);
Write(' > Reading sprite @ '+inttostr(IMGF.Position)+#13#10);
BMP := TBitmap.Create;
BMP.Width := IMG_MAX_WIDTH;
BMP.Height := IMG_MAX_HEIGHT;
BMP.PixelFormat := pf24bit;
IMGF.Read(PixelData, IMG_MAX_WIDTH * IMG_MAX_HEIGHT);
for y:=0 to IMG_MAX_HEIGHT-1 do
begin
for x:=0 to IMG_MAX_WIDTH-1 do
begin
// Get palette color index from pixeldata
BMP.Canvas.Pixels[x,y] := (IMG_PALETTE[ (PixelData[(x*64)+y] * 3) + 0 ] * $1)
+(IMG_PALETTE[ (PixelData[(x*64)+y] * 3) + 1 ] * $100)
+(IMG_PALETTE[ (PixelData[(x*64)+y] * 3) + 2 ] * $10000);
end;
end;
BMP.SaveToFile(Format('Sprite_%.4d.bmp', [h]));
BMP.Free;
// return back to header data
IMGF.Seek(hCPos, soBeginning);
end;
// parse all other sprites (padded, 64x64) sprites
for h := 1 to PaddedHeaders do
begin
// read sprite offset
IMGF.Read(dOffset, 4);
// save pos and goto sprite data
hCPos := IMGF.Position;
IMGF.Seek(dOffset, soBeginning);
Write(' > Reading alpha sprite @ '+inttostr(IMGF.Position));
TickCnt := GetTickCount();
// read in the sprite margins
IMGF.Read(pHead, SizeOf(IMG_PADDED_HEAD));
{
these values are used to determine how many footers there are
it may not be completely reliable, so beware
}
// read all the footer pointers
SetLength(pFooter, pHead.Right-pHead.Left+1);
for i:=0 to (pHead.Right-pHead.Left) do
begin
IMGF.Read(tHeader, 2);
pFooter[i] := tHeader;
end;
BMP := TBitmap.Create;
BMP.Width := IMG_MAX_WIDTH;
BMP.Height := IMG_MAX_HEIGHT;
BMP.PixelFormat := pf24bit;
// fill the canvas with our alpha layer color
with BMP.Canvas do
begin
Pen.Color := $FF00FF; // magenta chroma key
// anything which isn't in the pallete will do
Brush.Color := Pen.Color;
end;
BMP.Canvas.Rectangle(0,0,IMG_MAX_WIDTH,IMG_MAX_HEIGHT);
// read all footer data
for i:=0 to (pHead.Right-pHead.Left) do
begin
IMGF.Seek(dOffset + pFooter[i], soBeginning);
// read until EOL == 0
while true do
begin
IMGF.Read(tHeader, 2);
if tHeader = 0 then break;
Foot.EOL := tHeader;
IMGF.Read(tHeader, 2); Foot.SOL := tHeader;
IMGF.Read(tHeader, 2); Foot.PIX := tHeader;
// for each pixel in this line
for j:= Foot.SOL to Foot.EOL -1 do
begin
pxOrigin := IMGF.Position;
// get the palette index
// position = sprite offset + pixel offset + line pos
IMGF.Seek(dOffset + Foot.PIX + (j - Foot.SOL), soBeginning);
IMGF.Read(px, 1);
BMP.Canvas.Pixels[pHead.Left + i, j] :=
(IMG_PALETTE[ (px * 3) + 0 ] * $1)
+(IMG_PALETTE[ (px * 3) + 1 ] * $100)
+(IMG_PALETTE[ (px * 3) + 2 ] * $10000);
IMGF.Seek(pxOrigin, soBeginning);
end;
end;
end;
BMP.SaveToFile(Format('Sprite_%.4d.bmp', [Headers + h]));
BMP.Free;
Write(' ('+IntToStr(GetTickCount-TickCnt)+' ms)'#13#10);
// return back to header data
IMGF.Seek(hCPos, soBeginning);
end;
IMGF.Free;
Write(' > Done!'#13#10);
PreExit;
Exit;
end.