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.