VGA – Tutorial 18 – Denthor

--==[ PARTE 18 ]==--

Autor: DENTHOR do ASPHYXIA
Atualizado por Snowman
Traduzido por Krull >>> tradução revisada e atualizada em 2020

Para obter os programas mencionados neste tutorial, por favor baixe o zip com a versão antiga do tutorial, neste link.

Introdução

Bem-vindo de volta! Eu sei, vocês devem estar em choque por esse tutorial ter vindo tão rápido logo depois do outro, mas a razão para isso é que eu quero ter o tutorial 20 pronto até lançarem a PCGPE ][. Provavelmente não vou chegar lá, mas vou tentar! 😉

Este tutorial é sobre empacotamento de arquivos, e colocar tudo num arquivo executável. Para o tutorial 19 estou pensando em fazer um pouco mais de explicação em Assembly, e talvez demonstrar com um efeito de fogo ou algo assim.

A Pipsy quer receber email! O endereço dela é cowan@beastie.cs.und.ac.za e ela quer bater papo 🙂 Vá em frente, mande um email para ela. A co-fundadora do ctrl-alt-del, também é uma boa programadora de gráficos.

Se você gostaria de me contactar, ou ao time, há muitos modos que você pode fazê-lo:
1) Escrever uma mensagem para Grant Smith/Denthor Asphyxia em email privado na ASPHYXIA BBS.
2) Escrever para:
Grant Smith
P.O.Box 270 Kloof
3640
Natal
África do Sul
3) Ligar para mim (Grant Smith) no número (031) 73 2129 (deixe uma mensagem se você ligar quando eu estiver na faculdade) Ligue para +27-31-73-2129 se você está ligando de fora da África do Sul (A conta é sua ;-))
4) Escrever para denthor@beastie.cs.und.ac.za
5) Escrever para asphyxia@beastie.cs.und.ac.za para falar com todos nós de uma vez.

OBS1 : Se você é um representante de uma companhia ou BBS e quer que a ASPHYXIA faça um demo para você, mande um email pra mim; podemos discutir.
OBS2 : Se você fez/tentou fazer um demo, MANDE PARA MIM! Estamos nos sentindo muito solitários e queremos encontrar/ajudar/trocar código com outros grupos de demos. O que você tem a perder? Mande uma mensagem aqui e podemos ver como transferir. Nós realmente queremos ouvir de você.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Carregando um arquivo PCX

Isso é na verdade bem fácil. O arquivo PCX é dividido em três seções, um cabeçalho de 128 bytes, uma seção de dados e uma paleta de 768 bytes.

Geralmente você pode ignorar o cabeçalho de 128 bytes, mas aqui está ele:

0  Fabricante 10 = arquivo ZSoft .PCX
1  Versão
2  Codificação
3  Bits Por Pixel
4  XMin, Ymin, XMax, YMax (2 bytes cada)
12 Resolução Horizontal (2 bytes)
14 Resolução Vertical (2 bytes)
16 Setagem da pallette (48 bytes)
64 Reservado
65 Número de planos de cores
66 Bytes por linha (2 bytes)
68 1 = Cor 2 = Escala de cinza (2 bytes)
70 Em branco (58 bytes)

Isso completa os 128 bytes.

O arquivo de pallete, que tem 768 bytes, fica situado no fim do arquivo PCX. O 769º byte de trás pra frente deve ser o número decimal 12, que indica que uma paleta de cores VGA vem a seguir.

Há uma coisa que temos que fazer para pegar a paleta corretamente no VGA… a paleta do PCX tem valores para R,G,B de 0 a 255 respectivamente. Para converter isso para nossa palete padrão (VGA), devemos dividir os valores por 4, para deixá-los na faixa de 0 a 63.

Na verdade, decodificar uma figura é muito simples. Começando após o cabeçalho, lemos um byte.

Se os dois bits mais altos não estiverem setados, jogamos o valor na tela.

Checamos os bits assim:

if ((temp and $c0) = $c0) then …(bits estão setados)… else …(bits não estão setados)

C0 em hexadecimal = 11000000 em binário = 192 em decimal

Vamos olhar mais de perto…

  temp and c0h
  temp and 11000000b

Isso significa que, quando representado em formato de bit, ambos os bits têm que estar setados para o resultado ser “um”.

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

No caso acima então, os dois bits superiores de temp têm que estar setados para que o resultado seja igual a 11000000b. Se ele não for igual, os bits superiores não estão ambos setados, e podemos pôr o pixel.

Assim

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

Se os bits estiverem os dois setados, as coisas mudam. Os 6 bits inferiores viram um contador de loop, de quantas vezes o próximo byte será repetido.

if ((temp and $c0) = $c0) then BEGIN
{ Lê o próximo, temp2 aqui.}
  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;

Aí está nosso carregador de PCX. Você vai notar que fazendo AND com $3f, estou fazendo AND com 00111111b… em outras palavras, limpando os bits superiores.

O programa de amostra tem o que está acima, em assembly, mas é a mesma rotina… e você pode ler o tutorial 19 para mais informação em como codificar em assembly.

Na amostra eu assumo que a figura seja 320×200 de tamanho, com máximo de 66432 bytes.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Empacotamento de arquivos

A pergunta é simples… como consigo todos os meus arquivos em um executável? Ter um monte de arquivos de dados pode começar a parecer pouco profissional.

Um modo fácil de ter um .exe e não um arquivo .dat quando lidar com muitos cel’s, etc, é fácil… você teria que alterar sua rotina de carregar, que se parece com isso:

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;

Você deveria fazer isso:

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;

De agora em diate, você deveria fazer isso:

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;

Isso significa que, ao invés de cinco arquivos de dados, você terá apenas um agora! Você também cortou o header do cel de 800 bytes 🙂 … Note que isso vai funcionar para qualquer dado, não apenas para arquivos cel.

O próximo passo lógico é incluir esse dado no arquivo .exe, mas a pergunta é: “como?”. Num tutorial anterior, eu converti meu arquivo de dados para constantes e coloquei dentro do meu programa principal. Isso não é bom, principalmente por causa de restrições de espaço… você só pode ter um número de constantes, e se seu dado tiver 2 megas?

Anexado a esse tutorial está uma solução. Eu escrevi um programa que combina seus arquivos de dados com seu executável, não importa o quão grande o dado seja. A única coisa que você precisa se preocupar é com uma pequena mudança em seus métodos de carregar. Vamos calcular o que é para mudar.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Usando o empacotador de arquivos

Normalmente você carregaria seu dados desse modo:

Procedure Init;
BEGIN
  loadcel ('bob.bob',temp);
  loadpcx ('den.pcx',VGA); { Carrega um arquivo PCX }
  loaddat ('data.dat',lookup); 
       { Carrega dados em lookup }
END;

Fácil, né? Agora, usando o empacotador de arquivos, você mudaria isso para:

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;

Não é muito difícil? Agora, isso ainda está usando os arquivos de dados normais no seu disco. Você então rodaria o PACK.EXE, selecionar o .exe do programa como base, selecionar “bob.bob”, “den.pcx” e “data.dat”, na ordem (1, 2 3). Aperte “c” para continuar, e ele vai combinar os arquivos. O arquivo .exe do seu programa poderá rodar independentemente de qualquer arquivo de dados no disco, porque os arquivos de dados estão embutidos dentro do .exe.

Vamos olhar mais de perto as rotinas de carregar. Normalmente uma procedure de carregamento seria desse tipo:

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

No FPack.pas, fazemos o seguinte:

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;

Como você pode ver, temos dois casos especiais dependendo de termos ou não o arquivo .exe empacotado.

NOTA: Depois que você empacotou o arquivo, você NÃO PODE usar o pklite nele. Você pode rodar o pklite no arquivo .exe antes de rodar o pack.exe… em outras palavras, você não pode usar o pklite para tentar
comprimir seus arquivos de dados.

PACK.EXE tem uma limitação… você só pode empacotar 24 arquivos de dados. se você gostaria que ele fizesse mais, mande-me um email… seria bem fácil aumentar esse número.

No programa de amostra, FINAL.EXE é o mesmo que temp.pas, exceto que ele tem seu PCS embutido dentro dele. Eu rodei o pack2.exe, selecionei o final.exe e eye.pcx, apertei “c”, e ali estava ele. Você vai notar que eye.pcx não está incluído no diretório… ele agora é parte do exe!

eye.pcx foi desenhado por Gary Morton da iCANDi Design. O outro pcx é um desenho antigo do Fubar.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Fechando

Bem, isso é tudo, para esse treinamento… o próximo (como já mencionei duas vezes 😉 ) será sobre assembly, com uma rotina de chamas.

Esse tutorial foi meio diferente dos tutoriais normais… tirando as rotinas para carregar PCX, o resto foi “não orientado a programação”… não se preocupe, nas próximas semanas ele volta ao normal.

Tchauuuuu….

  • Denthor
  • Krull

A Nova Krull's HomePage