Curso completo de DarkGdk
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 17.1
17.1 Salvando e recuperando dados
1.1 Visão geral
Neste tópico vamos ver como salvar e recuperar dados que é um processo típico
de diversas situações como salvar e carregar o status de uma sessão do jogo,
para produzir e recuperar dados em formato próprio como uma mapa de jogo.
Relembramos que existe outros dois caminhos de gravar e recuperar dados em
arquivos que você pode usar para complementar ou substituir a DarkGdk
que neste aspecto apresenta algumas inconsistências na versão aqui utilizada.
Você pode usar as funções de entrada e saída da biblioteca padrão da
linguagem c/c++ e da biblioteca do Windows.
Neste tópico vamos estudar as seguintes funções de gravação:
---------------------------------------------------------------------------
dbOpenToWrite() - Cria e abre um arquivo para gravação
dbFileOpen() - Verifica o sucesso da abertura de arquivo
dbWriteString() - Grava uma string no arquivo aberto
dbWriteLong() - Grava um (long) valor de 32 bits no arquivo
dbWriteWord () - Grava um (word) valor de 16 bits no arquivo
dbWriteByte() - Grava um (byte) valor de 8 bits no arquivo
dbCloseFile() - Fecha o arquivo que foi aberto em modo leitura ou gravação
As funções de gravação acima apresentam as respectivas contraparte de leitura:
---------------------------------------------------------------------------
dbOpenToRead() - Abre um arquivo para leitura
dbReadString() - Lê uma string no arquivo aberto
dbReadLong() - Lê um (long) valor de 32 bits do arquivo
dbReadWord () - Lê um (word) valor de 16 bits do arquivo
dbReadByte() - Lê um (byte) valor de 8 bits do arquivo
A função dbReadString() não será estudada pois não está funcionando adequadamente.
Em seu lugar será utilizada a função dbReadByte().
1.1.1 Ponteiro interno
É importante saber que o arquivo em disco tem um endereço, tem uma posição
específica aonde ele está gravado. Nos bastidores do processo de gravação
e leitura é criado um ponteiro que aponta para esta posição e qualquer
operação de gravação e leitura provoca um deslocamento neste ponteiro
determinando aonde vai ocorrer a próxima leitura/gravação de dados.
Dado então a existência do ponteiro é necessário ficar atento ao tamanho,
quantidade e na ordem dos dados que serão lidos ou gravados. Um descontrole
nestes aspectos pode ocasionar discrepâncias na recuperação dos mesmos,
pode ocasionar falta de correspondência do dado gravado com o dado lido.
Segue abaixo algumas nomenclaturas e tamanho dos tipos de dados básicos:
nibble 4 bits
byte 8 bits ou dois nibbles
char 1 byte
word 2 bytes
long 4 bytes (*)
int 4 bytes (*)
* O tamanho de alguns tipos pode variar conforme o sistema e/ou compilador
sendo usados. Você pode checar o tamanho em tempo real usando o operador
sizeof() da linguagem c/c++.
1.2 Estudo das funções de leitura e gravação
dbOpenToWrite(hfile, sArquivo)
void dbOpenToWrite ( int f, char* pFilename );
Esta função cria um arquivo e o abre para gravação. Esta função falha se
o arquivo existir de forma que você deve deletá-lo e recriá-lo para ter
sucesso nos processos de gravação. Você deve verificar o sucesso da
abertura do arquivo com a função dbFileOpen().
hfile - valor que indentifica o arquivo em processsos posteriores
sArquivo - nome do arquivo a ser aberto
Ex.:
int hfile = 6;
char *sArquivo = "savegame.sav";
dbOpenToWrite(hfile,sArquivo);
if(dbFileOpen(hfile))
{
dbPrint(" savegame() ok - Arquivo criado/aberto com sucesso!");
}
else
{
MsgInfo(" Arquivo não foi aberto com sucesso!");
return;
} // endif
-------------------------------------------------------------------------------
nsts = dbFileOpen(hfile);
int dbFileOpen ( int f );
Esta função retorna 1 caso o arquivo especificado tenha sido aberto com
sucesso. Veja o exemplo de uso na função acima dbOpenToWrite().
-------------------------------------------------------------------------------
dbWriteString(hfile, sTexto);
void dbWriteString ( int f, char* pString );
Esta função grava uma string no arquivo aberto.
// Define ou coleta os dados
char *sNome = "Jogador01";
int hfile = 6;
// (...) abre o arquivo e verifica se está ok
// Grava os dados
dbWriteString (hfile, sNome);
-------------------------------------------------------------------------------
dbWriteLong(hfile, nval)
void dbWriteLong ( int f, int iValue );
Esta função escreve um valor do tipo long (4 bytes) no arquivo.
// Define ou coleta os dados
long nPontos = 558000;
int hfile = 6;
// (...) abre o arquivo e verifica se está ok
// Grava os dados
dbWriteLong (hfile, nPontos);
-------------------------------------------------------------------------------
dbWriteWord (hfile, nval)
void dbWriteWord ( int f, int iValue );
Esta função um valor tipo word (2 bytes) no arquivo.
// Define ou coleta os dados
WORD nVidas = 16;
int hfile = 6;
// (...) abre o arquivo e verifica se está ok
// Grava os dados
dbWriteWord (hfile, nVidas);
-------------------------------------------------------------------------------
dbWriteByte(hfile, nval)
void dbWriteByte ( int f, int iValue );
Esta função escreve um byte no arquivo.
// Define ou coleta os dados
char *sNome = "Jogador01";
int nSaude = 68;
int hfile = 6;
// (...) abre o arquivo e verifica se está ok
// Grava os dados
dbWriteByte (hfile, nSaude);
-------------------------------------------------------------------------------
dbCloseFile(hfile)
void dbCloseFile ( int f );
Fecha o arquivo que foi aberto em modo leitura ou gravação
Ex.:
// Fechando o arquivo...
dbCloseFile(hfile);
-------------------------------------------------------------------------------
dbOpenToRead(hfile, sArquivo)
void dbOpenToRead( int f, char* pFilename );
Abre um arquivo para leitura.
Ex.:
char *sArquivo = "savegame.sav";
// Abre e verifica arquivo
int hfile = 2;
dbOpenToRead(hfile,sArquivo);
-------------------------------------------------------------------------------
dbReadLong(hfile, &nval)
int dbReadLong( int f, int* pInteger );
Lê um (long) valor de 32 bits do arquivo.
Ex.:
// (...) abre o arquivo para leitura e verifica se está ok
int np = 0;
dbReadLong (hfile, &np);
-------------------------------------------------------------------------------
dbReadWord (hfile, &nval)
WORD dbReadWord ( int f, WORD* pWord );
Lê um (word) valor de 16 bits do arquivo.
Ex.:
// (...) abre o arquivo para leitura e verifica se está ok
WORD nv = 0;
dbReadWord (hfile, &nv);
-------------------------------------------------------------------------------
dbSkipBytes(hfile, nbytes);
void dbSkipBytes ( int f, int iSkipValue );
Esta função desloca o ponteiro de leitura para a frente pulando uma
quantidade especifica de bytes. Você vai usar essa função geralmente
para pular bytes de controle ou de demarcação de dados. Veja abaixo
na função dbReadByte() um exemplo de uso.
-------------------------------------------------------------------------------
dbReadByte(hfile, &nval)
unsigned char dbReadByte ( int f, unsigned char* pByte );
Esta função lê um byte do arquivo. Você pode usar esta função para ler
uma string que nada mais é do que uma sequência de bytes.
Ex.:
// Define ou coleta os dados
char *sNome = "Jogador01";
int hfile = 6;
// (...) abre o arquivo para gravação e verifica se está ok
// Grava os dados
dbWriteString (hfile, sNome);
// (...) abre o arquivo para leitura e verifica se está ok
// memória para guardar os dados que serão lidos.
unsigned char sn[13]="\0";
// A quantidade a ser lida deve ser a quantidade que foi gravada
int ntam = strlen("jogador01");
// Lê o byte e estoca-o em ns[posição]
for (int ncx=0; ncx < ntam; ncx++) dbReadByte(hfile, &sn[ncx]);
dbSkipBytes(hfile,2);
Repare no exemplo acima que usamos a função dbSkypeBytes() para pular
2 bytes para frente e assim posicionar corretamente o ponteiro no próximo
dado a ser lido do arquivo. Esses dois bytes que foram pulados são os
caractereres 0x0D 0x0A que são usados para ocasionar um avanço de linha
em funções ou aplicativos que exibem texto.
Outro pequeno exemplo:
BYTE ns = 0;
dbReadByte (hfile, &ns);
-------------------------------------------------------------------------------
Veja o programa exemplo dessa sessão:

// savegame.cpp
// Este programa ilustra como salvar e carregar(load) status de jogo(savegame)
#include "DarkGDK.h"
// Protótipo das funções
void initsys(); // inicializa o sistema
void MsgInfo(char *cText); // Mostra uma mensagem na tela
void savegame(void); // Salva jogo
void loadgame(void); // Carrega jogo
// Flag para terminar o programa. 1 = terminar
int terminar = 0;
// Elenco de cores
#define nBranco 0xFFFFFF
// ----------------------------------------------------------------------------
void DarkGDK ( void ) {
// Começo da aplicação DarkGdk
initsys();
savegame();
loadgame();
// Looping principal
while ( LoopGDK ( ) ) {
if (dbMouseClick() == 2) terminar = 1;
if (terminar == 1) break;
dbSync ( );
} // fim do while
return;
} // endfunction DarkGDK()
// ----------------------------------------------------------------------------
void initsys() {
// Esta função inicializa o sistema
dbSyncOn( );
dbCLS ( nBranco); dbInk( 0, nBranco );
dbSetWindowTitle("savegame.cpp"); dbSetTextOpaque();
} // initsys().fim
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void MsgInfo(char *cText) {
MessageBox(0, cText, "savegame.cpp", MB_OK);
}// endfuncion MsgInfo
void savegame(void) {
char *sArquivo = "savegame.sav";
char sinfo[255];
if (dbFileExist(sArquivo) == 1) dbDeleteFile(sArquivo);
char *sNome = "Jogador01"; // Jogador01
WORD nVidas = 0x4242; // 16.962 - codificado em ascii: BB
long nPontos = 0x43434343; // 1.128.481.603 - codificado em ascii: CCCC
int nSaude = 0x44 ; // 68 - codificado em ascii: D
// Abre o arquivo e verifica que está ok
int hfile = 6;
dbOpenToWrite(hfile,sArquivo);
if(dbFileOpen(hfile))
{
dbPrint("savegame() ok - Arquivo criado/aberto com sucesso!");
}
else
{
MsgInfo("Arquivo não foi aberto com sucesso!");
return;
} // endif
// Grava os dados
dbWriteString (hfile, sNome);
dbWriteWord (hfile, nVidas);
dbWriteLong (hfile, nPontos);
dbWriteByte (hfile, nSaude);
// Mostra os dados gravados
dbPrint(" ");
dbPrint("Dados gravados: ");
sprintf(sinfo,
"Nome:%s Vidas:%d Saude:%d Pontos:%li",
sNome, nVidas, nSaude, nPontos);
dbPrint(sinfo);
// Fechando o arquivo
dbCloseFile(hfile);
dbPrint(" ");
} // endfunction: savegame()
void loadgame() {
char *sArquivo = "savegame.sav";
char sinfo[255];
if (dbFileExist(sArquivo) != 1)
{
MsgInfo("Arquivo savegame.sav não existe");
return;
}
WORD nv = 0;
int np = 0;
unsigned char sn[13]="\0";
BYTE ns = 0;
// Mostra variáveis antes da leitura
dbPrint(" ");
dbPrint("----------------------- loadgame() ok --------------------------");
sprintf(sinfo, "Nome: %s Vidas: %d Saude: %d Pontos: %li",
sn, nv, ns, np, ns);
dbPrint("Valores das variaveis antes da leitura:");
dbPrint(sinfo);
// Abre e verifica arquivo
int hfile = 2;
dbOpenToRead(hfile,sArquivo);
if(dbFileOpen(hfile))
{
dbPrint(" ");
dbPrint("loadgame() ok - Arquivo aberto com sucesso para leitura!");
}
else
{
MsgInfo("Arquivo não foi aberto com sucesso!");
return;
}
int ntam = strlen("jogador01");
for (int ncx=0; ncx < ntam; ncx++) dbReadByte(hfile, &sn[ncx]);
dbSkipBytes(hfile,2);
//Esta função não está funcionado: ReadString()
dbReadWord ( hfile, &nv);
dbReadLong (hfile, &np);
dbReadByte (hfile, &ns);
dbPrint(" "); dbPrint("Variaveis depois da leitura: ");
sprintf(sinfo, "Nome: %s Vidas: %d Saude: %d Pontos: %li",
sn, nv, ns, np, ns);
dbPrint(sinfo);
// Fechando o arquivo...
dbCloseFile(hfile);
} // endfunction: loadgame
1.3 Salvando e recuperando floats
dbWriteFloat(hfile, nval)
void dbWriteFloat ( int f, float fValue );
Esta função escreve um valor do tipo float no arquivo.
// Define ou coleta os dados
float x = 320.10f; float y = 240.11f; float z = 100.12f;
// (...) abre o arquivo e verifica se está ok
// Grava os dados
dbWriteFloat (hfile, x); dbWriteFloat (hfile, y);
dbWriteFloat (hfile, z);
-------------------------------------------------------------------------------
dbReadFloat(hfile, &nval)
void dbReadFloat ( int f, float *pFloat );
Esta função lê um valor do tipo float do arquivo.
// Reserva espaço de memória
float u, v, w;
u = v = w = 0.0f;
// (...) abre o arquivo e verifica se está ok
// Lê os dados
dbReadFloat (hfile, &u); dbWriteFloat (hfile, &v);
dbWriteRead (hfile, &w);
-------------------------------------------------------------------------------
Veja o programa exemplo abaixo:

// myfloats.cpp
// Este programa ilustra como salvar e carregar(load) status de jogo(savegame)
#include "DarkGDK.h"
// Protótipo das funções
void initsys(); // inicializa o sistema
void MsgInfo(char *cText); // Mostra uma mensagem na tela
void savegame(void); // Salva jogo
void loadgame(void); // Carrega jogo
// Flag para terminar o programa. 1 = terminar
int terminar = 0;
// ----------------------------------------------------------------------------
void DarkGDK ( void ) {
// Começo da aplicação DarkGdk
initsys();
savegame(); loadgame();
// Looping principal
while ( LoopGDK ( ) ) {
if (dbMouseClick() == 2) terminar = 1;
if (terminar == 1) break;
dbSync ( );
} // fim do while
return;
} // endfunction DarkGDK()
// ----------------------------------------------------------------------------
void initsys() {
// Esta função inicializa o sistema
dbSyncOn( );
dbCLS ( 0xFFFFFF); dbInk( 0x0000FF, 0xFFFFFF );
dbSetWindowTitle("myfloats.cpp"); dbSetTextOpaque();
} // initsys().fim
// ----------------------------------------------------------------------------
void MsgInfo(char *cText) {
MessageBox(0, cText, "myfloats.cpp", MB_OK);
}// endfuncion MsgInfo
// ----------------------------------------------------------------------------
void savegame(void) {
char *sArquivo = "myfloats.sav";
char sinfo[255];
if (dbFileExist(sArquivo) == 1) dbDeleteFile(sArquivo);
float x = 320.10f; float y = 240.11f; float z = 100.12f;
// Abre o arquivo e verifica que está ok
dbPrint(" ");
int hfile = 6;
dbOpenToWrite(hfile,sArquivo);
if(dbFileOpen(hfile))
{
dbPrint(" savegame() ok - Arquivo criado/aberto com sucesso!");
}
else
{
MsgInfo(" Arquivo não foi aberto com sucesso!");
return;
} // endif
// Grava os dados
dbWriteFloat (hfile, x);
dbWriteFloat (hfile, y);
dbWriteFloat (hfile, z);
// Mostra os dados gravados
dbPrint(" ");
dbPrint(" Dados gravados: ");
sprintf(sinfo, " Posicao do jogador: (%f, %f, %f)",x,y,z);
dbPrint(sinfo);
// Fechando o arquivo
dbCloseFile(hfile);
dbPrint(" ");
} // endfunction: savegame()
// ----------------------------------------------------------------------------
void loadgame() {
char *sArquivo = "myfloats.sav";
char sinfo[255];
if (dbFileExist(sArquivo) != 1)
{
MsgInfo(" Arquivo myfloats.sav não existe");
return;
}
float u, v, w;
u = v = w = 0.0f;
// Mostra variáveis antes da leitura
dbPrint(" ");
dbPrint(" ----------------------- loadgame() ok --------------------------");
sprintf(sinfo, " Posicao do jogador: (%f, %f, %f)",u,v,w);
dbPrint(" Valores das variaveis antes da leitura:");
dbPrint(sinfo);
// Abre e verifica arquivo
int hfile = 2;
dbOpenToRead(hfile,sArquivo);
if(dbFileOpen(hfile))
{
dbPrint(" ");
dbPrint(" loadgame() ok - Arquivo aberto com sucesso para leitura!");
}
else
{
MsgInfo(" Arquivo não foi aberto com sucesso!");
return;
}
dbReadFloat ( hfile, &u);
dbReadFloat ( hfile, &v);
dbReadFloat ( hfile, &w);
dbPrint(" "); dbPrint(" Variaveis depois da leitura: ");
sprintf(sinfo, " Posicao do jogador: (%f, %f, %f)",u,v,w);
dbPrint(sinfo);
// Fechando o arquivo...
dbCloseFile(hfile);
} // endfunction: loadgame