Copy Link
Add to Bookmark
Report

VGA Trainer Program: part 18

eZine's profile picture
Published in 
VGA Trainer Program
 · 9 Aug 2024

                   Õ-------------------------------¸ 
| W E L C O M E |
| To the VGA Trainer Program | |
| By | |
| DENTHOR of ASPHYXIA | | |
Ô-------------------------------¾ | |
--------------------------------Ù |
--------------------------------Ù

--==[ PART 18 ]==--

Introduction

Welcome back! I know, I know, you all all shocked that this tut is out so soon after the last one, but the reason for this is that I want to have Tut 20 out by the time PCGPE ][ is released. I probably won't get that far, but I'll try! ;)

This tut is on file packing, and putting everything into one executeable file. For tut 19, I am thinking of doing a bit more on explaining assembler, and perhaps demonstrating with a fire effect or something.

My mailserver is back up, thanks to Nobody (that's his handle). You write to denthor@beastie.cs.und.ac.za with the subject line request-list. The mailserver checks each incoming letter for this subject line, and if it finds one of the specific strings, it mails you back a certain file. I never get to see these messages! If you want to mail me personally, give your message a unique subject name! Anyway, you can request all the tuts through this mailserver.

I hope you are all subscribed to the Demuan List! If not, ftp it off ftp.eng.ufl.edu every sunday. I am writing a "humor" column there until someone can figure out something else I could write ;-).

Pipsy wants mail! She is at cowan@beastie.cs.und.ac.za and wants to chat :) Go on, mail her. The co-founder of ctrl-alt-del, she is also a good graphics coder.

If you would like to contact me, or the team, there are many ways you can do it :

  1. Write a message to Grant Smith/Denthor/Asphyxia in private mail on the ASPHYXIA BBS.
  2. Write to : Grant Smith P.O.Box 270 Kloof 3640 Natal South Africa
  3. Call me (Grant Smith) at (031) 73 2129 (leave a message if you call during varsity). Call +27-31-73-2129 if you call from outside South Africa. (It's YOUR phone bill ;-))
  4. Write to denthor@beastie.cs.und.ac.za in E-Mail.
  5. Write to asphyxia@beastie.cs.und.ac.za to get to all of us at once.

NB : If you are a representative of a company or BBS, and want ASPHYXIA to do you a demo, leave mail to me; we can discuss it.
NNB : If you have done/attempted a demo, SEND IT TO ME! We are feeling quite lonely and want to meet/help out/exchange code with other demo groups. What do you have to lose? Leave a message here and we can work out how to transfer it. We really want to hear from you!

Loading a PCX file

This is actually quite easy. The PCX file is divided into three sections, mainly a 128 byte header, a data section, and a 768 byte pallette.

You can usually ignore the 128 byte header, but here it is :

0  Manufacturer     10 = ZSoft .PCX file 
1 Version
2 Encoding
3 Bits Per Pixel
4 XMin, Ymin, XMax, YMax (2 bytes each)
12 Horizontal Resolution (2 bytes)
14 Verticle Resolution (2 bytes)
16 Color pallette setting (48 bytes)
64 Reserved
65 Number of color planes
66 Bytes per line (2 bytes)
68 1 = Color 2 = Grayscale (2 bytes)
70 Blank (58 bytes)

That makes 128 bytes.

The pallette file, which is 768 bytes, is situated at the very end of the PCX file. The 769'th byte back should be the decimal 12, which indicates that a VGA color pallette is to follow.

There is one thing that we have to do to get the pallette correct in VGA ... the PCX pallette values for R,G,B are 0 to 255 respectively. To convert this to our standard (VGA) pallette, we must divide the R,G,B values by 4, to get them into a range of 0 to 63.

Actually decoding the image is very simple. Starting after the 128 byte header, we read a byte.

If the top two bits of this byte are not set, we dump the value to the screen.

We check bits as follows :

if ((temp and $c0) = $c0) then ...(bits are set)... else ...(bits are not set)

C0 in hex = 11000000 in binary = 192 in decimal

Let's look at that more closely...

  temp  and  c0h 
temp and 11000000b

That means, when represented in bit format, both corresponding bits have to be set to one for the result to be one.

0 and 0 = 0     1 and 0 = 0    0 and 1 = 0    1 and 1 = 1

In the above case then, both of the top two bits of temp have to be set for the result to equal 11000000b. If it does not equal that, the top two bits are not both set, and we can put the pixel.

So

  if ((temp and $c0) = $c0) then BEGIN 
END else BEGIN
putpixel (screenpos,0,temp,vga);
inc (screenpos);
END;

If the top two bits _are_ set, things change. The bottom six bits become a loop counter, which the next byte is repeated.

  if ((temp and $c0) = $c0) then BEGIN 
{ Read in next byte, temp2 here.}
for loop1:=1 to (temp and $3f) do BEGIN
putpixel (screenpos,0,temp2,vga);
inc (screenpos);
END;
END else BEGIN
putpixel (screenpos,0,temp,vga);
inc (screenpos);
END;

There is our PCX loader. You will note that by and'ing temp by $3f, I am and'ing it by 00111111b ... in other words, clearing the top two bits.

The sample program has the above in assembler, but it is the same procedure... and you can read tut 19 for more info on how to code in assembler.

In the sample I assume that the pic is 320x200, with a maximum size of 66432 bytes.

File packing

The question is simple .. how do I get all my files into one executable? Having many small data files can start to look unprofessional.

An easy way to have one .exe and one .dat file when dealing with many cel's etc. is easy .... you would alter your load procedure, which looks as follows :

VAR temp : Array [1..5,1..256] of byte; 
Procedure Init;
BEGIN
loadcel ('pic1.cel',temp[1]);
loadcel ('pic2.cel',temp[2]);
loadcel ('pic3.cel',temp[3]);
loadcel ('pic4.cel',temp[4]);
loadcel ('pic5.cel',temp[5]);
END;

For one compile you would do this :

VAR temp : Array [1..5,1..256] of byte; 
Procedure Init;
VAR f:File;
BEGIN
loadcel ('pic1.cel',temp[1]);
loadcel ('pic2.cel',temp[2]);
loadcel ('pic3.cel',temp[3]);
loadcel ('pic4.cel',temp[4]);
loadcel ('pic5.cel',temp[5]);
assign (f,'pic.dat');
rewrite (f,1);
blockwrite (f,temp,sizeof(temp));
close (f);
END;

From then on, you would do :

VAR temp : Array [1..5,1..256] of byte; 
Procedure Init;
VAR f:File;
BEGIN
assign (f,'pic.dat');
reset (f,1);
blockread (f,temp,sizeof(temp));
close (f);
END;

This means that instead of five data files, you now have one! You have also stripped the 800 byte cel header too :) ... note that this will work for any form of data, not just cel files.

The next logical step is to include this data in the .exe file, but the question is how? In an earlier tutorial, I converted my data file to constants and placed it inside my main program. This is not good mainly because of space restrictions ... you can only have so many constants, and what if your data file is two megs big?

Attached with this tutorial is a solution. I have written a program that combines your data files with your executable file, no matter how big the data is. The only thing you have to worry about is a small change in your data loading methods. Lets find out what.

Using the file packer

Normally you would load your data as follows :

Procedure Init; 
BEGIN
loadcel ('bob.bob',temp);
loadpcx ('den.pcx',VGA); { Load a PCX file }
loaddat ('data.dat',lookup); { Load raw data into lookup }
END;

Easy, hey? Now, using the file packer, you would change this to :

USES fpack; 
Procedure Init;
BEGIN
total := 3;
infodat[1] := 'bob.bob';
infodat[2] := 'den.pcx';
infodat[3] := 'data.dat';
loadcel (1,temp);
loadpcx (2,VGA);
loaddat (3,lookup);
END;

Not too difficult? Now, this is still using the normal data files on your hard drive. You would then run PACK.EXE, select the program's .exe as the base, then select "bob.bob", "den.pcx" and "data.dat", in order (1, 2, 3). Hit "c" to continue, and it will combine the files. Your programs .exe file will be able to run independently of the separate data files on disk, because the data files are imbedded with the .exe.

Let us take a closer look at the load procedures. Normally a load procedure would look as follows :

Procedure LoadData (name:string; p:pointer); 
VAR f:file;
BEGIN
assign (f,name);
reset (f,1);
blockread (f,p^,filesize(f);
close (f);
END;

In FPack.pas, we do the following :

Function LoadData (num:integer; p:pointer):Boolean; 
VAR f:file;
BEGIN
If pack then BEGIN
assign (f,paramstr(0));
reset (f,1);
seek (f,infopos[num]);
blockread (f, p^, infopos[num+1]-infopos[num]);
close (f);
END else BEGIN
assign (f,infodat[num]);
reset (f,1);
blockread (f, p^, filesize (f));
close (f);
END;
END;

As you can see, we just have two special cases depending on weather or not the .exe file has been packed yet.

NOTE : After you have packed the file, you CAN NOT pklite it. You can however pklite the .exe _before_ you run pack.exe ... in other words, you can not use pklite to try pack your data files.

PACK.EXE does have a limitation ... you can only pack 24 data files together. If you would like it to do more, mail me ... It should be easy to increase the number.

In the sample program, FINAL.EXE is the same as temp.pas, except it has it's PCX embedded inside it. I ran pack2.exe, selected final.exe and eye.pcx, hit "C", and there it was. You will notice that eye.pcx is not included in the directory ... it is now part of the exe!

eye.pcx was draw by Gary Morton of iCANDi Design. The other pcx is an old one draw by Fubar.

In closing

Well, that's about it for this trainer... next one (as I have mentioned twice already ;) will be on assembler, with a flame routine thrown in.

Any good html programmers out there? My web page really needs help ;-)

As soon as I figure out how, I will also be creating a mailing list, in which people with internet addresses will be able to get all new trainers delivered directly into their mailbox's. I hope to announce something in tut 20.

This tut has been a bit of a departure from normal tuts ... aside from the PCX loading routines, the rest has been "non programming" oriented ... don't worry, next weeks one will be back to normal.

Byeeeee....

- Denthor

The following are official ASPHYXIA distribution sites :

É--------------------------Ë----------------Ë-----» 
|BBS Name |Telephone No. |Open |
Ì--------------------------Î----------------Î-----¹
|ASPHYXIA BBS #1 |+27-31-765-5312 |ALL | *Moving*
|ASPHYXIA BBS #2 |+27-31-765-6293 |ALL | *Moving*
|C-Spam BBS |410-531-5886 |ALL |
|POP! |+27-12-661-1257 |ALL |
|Soul Asylum |+358-0-5055041 |ALL |
|Wasted Image |407-838-4525 |ALL |
|Reckless Life |351-01-716 67 58|ALL |
|Mach 5 BBS |+1 319-355-7336 |ALL |
|House of Horror |+1 513-734-6470 |ALL |
|Zero Level |+39 6-810-9934 |ALL |
È--------------------------Ê----------------Ê-----¼

Leave me mail if you want to become an official Asphyxia BBS distribution site.

FPACK.PAS

UNIT FPack; 

INTERFACE

USES crt;

VAR InfoDat : array [1..25] of string[12];
Total : Integer;

Function LoadData (num:integer; p:pointer):Boolean;
{ Load raw data into data at pointer "p" }
Function LoadCel (num:integer; p:pointer):Boolean;
{ Load in a cel into pointer "p" }
Function LoadPCX (num:integer; where:word; dopal : Boolean):Boolean;
{ Load a PCX file to the screen "where"
Dopal = True sets up the correct PCX pallette, otherwise it leaves
the pallette alone }

IMPLEMENTATION


VAR pack:boolean;
InfoPos:Array[1..25] of longint;

Procedure DoSinglePal(Col,R,G,B : Byte); assembler;
asm
mov dx,3c8h
mov al,[col]
out dx,al
inc dx
mov al,[r]
out dx,al
mov al,[g]
out dx,al
mov al,[b]
out dx,al
end;


Function LoadData (num:integer; p:pointer):Boolean;
VAR f:file;
BEGIN
If num>Total then BEGIN
LoadData:=FALSE;
exit;
END;
If pack then BEGIN
assign (f,paramstr(0));
reset (f,1);
seek (f,infopos[num]);
blockread (f, p^, infopos[num+1]-infopos[num]);
close (f);
END else BEGIN
assign (f,infodat[num]);
reset (f,1);
blockread (f, p^, filesize (f));
close (f);
END;
END;

Function LoadCel (num:integer; p:pointer):Boolean;
VAR f:file;
BEGIN
If num>Total then BEGIN
LoadCel:=FALSE;
exit;
END;
If pack then BEGIN
assign (f,paramstr(0));
reset (f,1);
seek (f,infopos[num]+800);
blockread (f, p^, infopos[num+1]-infopos[num]-800);
close (f);
END else BEGIN
assign (f,infodat[num]);
reset (f,1);
seek (f,800);
blockread (f, p^, filesize (f));
close (f);
END;
END;

Function LoadPCX (num:integer; where:word; dopal : Boolean):Boolean;
VAR f:file;
res,loop1:word;
temp:pointer;
pallette: Array[0..767] Of Byte;
BEGIN
If num>Total then BEGIN
LoadPCX:=FALSE;
exit;
END;
If pack then BEGIN
assign (f,paramstr(0));
reset (f,1);
if dopal then BEGIN
Seek(f,infopos[num+1]-768);
BlockRead(f,pallette,768);
For loop1:=0 To 255 Do
dosinglepal (loop1,pallette[loop1*3] shr 2,pallette[loop1*3+1] shr 2,pallette[loop1*3+2] shr 2);
END;
seek (f,infopos[num]+128);
END else BEGIN
assign (f,infodat[num]);
reset (f,1);
if dopal then BEGIN
Seek(f,FileSize(f)-768);
BlockRead(f,pallette,768);
For loop1:=0 To 255 Do
dosinglepal (loop1,pallette[loop1*3] shr 2,pallette[loop1*3+1] shr 2,pallette[loop1*3+2] shr 2);
END;
seek (f,128);
END;
getmem (temp,65535);
blockread (f,temp^,65535,res);
asm
push ds
mov ax,where
mov es,ax
xor di,di
xor ch,ch
lds si,temp
@Loop1 :
lodsb
mov bl,al
and bl,$c0
cmp bl,$c0
jne @Single

mov cl,al
and cl,$3f
lodsb
rep stosb
jmp @Fin
@Single :
stosb
@Fin :
cmp di,63999
jbe @Loop1
pop ds
end;
freemem (temp,65535);
close (f);
END;


Procedure Startup;
CONST header : String[42] = '(c) Asphyxia 1995 The Asphyxia File Packer';
VAR buf : String[43];
f : file;
ch:byte;
BEGIN
Total:=0;
Fillchar (infodat, sizeof(infodat), 0);
assign (f,paramstr(0));
reset (f,1);
seek (f,filesize(f)-43);
blockread (f,buf,43);
if buf=header then BEGIN
seek (f,filesize(f)-44-100);
blockread (f,ch,1);
blockread (f,infopos,sizeof(infopos));
Pack:=TRUE;
END else BEGIN
Pack:=FALSE;
END;
close (f);
END;

BEGIN
Startup;
END.

TEMP.PAS

USES fpack,crt; 

BEGIN
total:=1;
infodat[1]:='grim.pcx';
asm
mov ax,0013h
int 10h
end;

loadpcx (1,$a000,true);
readkey;

asm
mov ax,0003h
int 10h
end;
END.

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT