26 de maio de 2010

Como criar um Pack (pacote)

Pode parecer difícil criar um desses, mas é simples criar um arquivo que apenas guarde outros dentro dele.

Vamos fazer um teste usando o próprio Dos!

NOSSO PRIMEIRO PACOTE !
Pegue uma diretório vazio em seu computador e adicione nele os arquivos que você quer
armazenar em um pacote. Entre no Dos e vá para esse diretório, e escreva o seguinte:
C:\>copy *.* pacote.pak


Pronto, você agora tem um pacote com todos os arquivos do diretório =)
Mas acontece que dessa forma você não terá informações importantes como nome e
tamanho e quantidade de arquivos caso queira recriar os arquivos individuais =(
É necessário então criar uma espécie de cabeçalho mestre, que nos informe esses
dados, ou pelo menos nos informe coisas do tipo: quantidade de arquivos, versão do
pacote e uma espécie de assinatura para termos certeza que o arquivo realmente é um
pacote criado por nós.
CRIANDO UM PACOTE
Vamos ver como ficaria uma estrutura mestre:

typedef struct Pacote
{
int iAssinatura; // algum valor tipo "hPAK"
int iVersao; // versão do pacote
int iQuantDeArq; // quantos arquivos tem nele
}
Pacote;

Você deve estar pensando: mas com esses dados não conseguimos saber o tamanho
nem o nome dos arquivos !
Sim, um pacote na realidade vai conter 2 cabeçalhos diferentes !
Esse cabeçalho acima é o de identificação do arquivo principal, ou seja, o pacote. À partir
deste cabeçalho, teremos outro para cada arquivo, com as informações necessárias
sobre o arquivo. É bom ter isso em mente quando desenvolve um pacote, o primeiro
cabeçalho deve conter informações de como o arquivo está composto, informações
gerais que sirvam para todos os outros arquivos dentro dele. O segundo cabeçalho sim
vai conter informações individuais sobre cada arquivo, nome, tamanho, qual compactação
foi usada para armazená-lo com mais eficiência, etc.
O segundo header simples pode ser assim:

typedef struct ArqInfo
{
char szNome[256]; // nome do arquivo
int iTamanho; // tamanho
int iCompacUsada; // compactação usada nele}
ArqInfo;

Simples né ?
A estrutura interna de um arquivo agora seria mais ou menos assim:

Pacote
ArqInfo
iTamanho de dados
ArqInfo
iTamanho de dados
ArqInfo
iTamanho de dados
.
.
.

fim do arquivo
Outro método bom para criar um pacote seria colocar todos os cabeçalhos seguidos logo
no início, e depois os dados. Isso ajuda na hora de carregar o arquivo, apenas lê o
Pacote e depois a ArqInfo Pacote.iQuantDeArq de vezes !
Mas para isso ser vantajoso em termos de velocidade, teríamos que ter também uma
outra variável na estrutura ArqInfo que indique o offset de dentro do pacote de onde
começa o arquivo real. Isso nos dá uma velocidade bem maior na leitura do arquivo, mas
dificulta a ampliação ou a remoção de um arquivo do pacote.

Pacote
ArqInfo
ArqInfo
ArqInfo
.
.
.
iTamanho de dados
iTamanho de dados
iTamanho de dados
.
.
.
fim do arquivo

Alguns jogos que usam pacotes não armazenam o nome do arquivo, mas sim um número
que indique o arquivo. Isso ajuda a diminuir o tamanho dos arquivos e também dificulta
para quem abre o arquivo entender como ele é composto.
Exemplo:

#define IMG_NAVE 0x03
e no código do game teria escrito assim:
img = ProcuraImagem ( IMG_NAVE );

Isso também é interessante pois o arquivo pode estar em um determinado local dentro do
pacote na primeira versão, mas pode estar em locais diferentes em outras, apenas
mantendo o número definido na criação =)
NOSSO PACOTE
Resolvi desenvolver as rotinas para ilustrar esse tutorial usando o primeiro método,
porém com as estruturas contendo mais variáveis que serão usadas durante o avanço
deste tutorial
Definições:
Primeiro foi necessário definir alguns valores que ajudarão no código:

#define PACK_OK 0
#define PACK_PARAM_ERR 1
#define PACK_ARQ_ERR 2
#define PACK_MEM_ERR 3
#define PACK_ARQ_TAM_ERR 4
#define PACK_HD_TAM_ERR 5
#define PACK_HD_WRITE_ERR 6
#define PACK_HD_READ_ERR 7
#define PACK_ASSINATURA_ERR 8
#define PACK_ASSINATURA (0<<24)|('a'<<16)|('9'<<8)|('2')

Tirando a última definição, todas as outras são usadas para retorno das rotinas, para
saber se ocorreu algum erro durante a criação do pacote. A última como o próprio nome
diz, é a assinatura.
Estrutura de cabeçalho mestre:

typedef struct _PACK
{
unsigned long dAssinatura;
unsigned long dCrc;
HPACKARQ ptsArquivos;
}
PACK, *HPACK;

Na realidade esta estrutura é mais que o cabeçalho mestre, ela contém também um
ponteiro para a lista de estrutura de arquivos, que veremos a seguir:
Estrutura de cabeçalho de arquivos individuais:

typedef struct _PACKARQ
{
unsigned long dArqTamanho;
unsigned long dFlags;
char acArqNome[16];
unsigned char *pabArqData;
struct _PACKARQ *ptsArqProx;
}PACKARQ, *HPACKARQ;

Esta estrutura também contém um ponteiro para a próxima estrutura de arquivos, fazendo
com que eles fiquem em uma lista ligada quando estiverem na memória. Isso facilita
adicionar arquivos em qualquer parte do pacote, como também apagar
Os nomes das variáveis são bem claros, menos o dFlags que pode não significar nada
por enquanto, mas será usado para identificarmos quais compactações foram usadas no
arquivo =)
O source que acompanha esse pdf tem as rotinas criadas para criar pacotes, carregar
arquivos, gravar pacote e gravar arquivos individuais !
Potótipos:

HPACK CriaPack (void);

Inicializa uma estrutura mestre de pacote na memória

unsigned long CarregaArquivo (const char *pacNome, HPACK hPack);

Adiciona um arquivo com nome pacNome ao pacote
unsigned long GravaArquivo (HPACKARQ hPackArq);
Grava um arquivo que havia sido armazenado dentro do pack no
diretório corrente

unsigned long GravaPack (const char *pacPackNome, HPACK hPack);

Grava o pacote criado com CriaPack() no diretório atual. O pacote
é composto de todos os arquivos carregados com
CarregaArquivo()
HPACK CarregaPack (const char *pacPackNome, unsigned long *dErr);

Carrega um pacote gravado com GravaPack()
unsigned long DestroiPack (HPACK hPack);

Libera a memória usada pelo pacote e por seus arquivos
Na próxima parte deste tutorial incrementaremos o nosso pacote com rotinas dedicadas à
compactação =)...
Obrigado por ler este tutorial, espero que tenha sido útil para você entender melhor
sobre pacotes!

0 comentários: