Gerenciamento de conteúdo (parte 2)

No post passado demonstrei algumas funções para criação e leitura de um pacote de arquivos binários extremamente simples. Neste artigo, iremos colocar as funcionalidades explicadas lá numa classe, e fazer um pequeno gerenciador de conteúdo para que fique fácil recuperar imagens desse arquivo como surfaces SDL.

(NOTA: Pode acessar a parte 1 desta série AQUI)

Inicialmente, vamos olhar a definição da classe responsável por LER arquivos Pack:


class PackReader
{
private:
   FILE            *mFile;
   PackHeader      mHeader;
   PackFileEntry   *mEntries;

public:
   // construtor padrao
   PackReader() : mFile(0), mEntries(0) { }
   ~PackReader() { close(); }

   // abre/fecha arquivo
   void open ( const string &file_name );
   void close ();

   // pega surface SDL
   SDLSurface* getSurface ( const dword id );
};

E agora a implementação:


void PackReader::open ( const string &file_name )
{
   mFile = fopen(file_name.c_str(), "rb");
   if ( !mFile ) { /*ERRO*/ }

   // pega dados do arquivo
   fread(&mHeader, sizeof(PackHeader), 1, mFile);
   mEntries = new PackFileEntry[mHeader.numFiles];
   fread(mEntries, sizeof(PackFileEntry), mHeader.numFiles, mFile);
}

void PackReader::close ()
{
   if ( mFile )
   {
      fclose(mFile);
      mFile = 0;
   }
   memset(&mHeader, 0, sizeof(PackHeader));
   if ( mEntries )
   {
      delete[] mEntries;
      mEntries = 0;
   }
}

SDLSurface* PackReader::getSurface ( const dword id )
{
   if ( !mFile ) { /*ERRO*/ }
   if ( id >= mHeader.numFiles ) { /*ERRO*/ }

   // cria buffer de dados e le do arquivo
   byte *buffer = new byte[mEntries[id].size];
   fseek(pack, mEntries[id].offset, SEEK_SET);
   fread(buffer, sizeof(byte), mEntries[id].size, mFile);
   // transforma em RWOps
   SDL_RWops *rw = SDL_RWFromMem(buffer, mEntries[id].size);
   SDLSurface *surf = <span>IMG_Load_RW ( rw, 1 );</span>

   // apaga dados lidos e retorna imagem
   delete[] buffer;
   return surf;
}

Agora que temos a classe para ler os dados de forma encapsulada, vamos criar o nosso primeiro gerenciador.

Um gerenciador de conteúdo é responsavel por ler e controlar todo o conteúdo de um jogo. Este nosso gerenciador de exemplo vai nos fornecer métodos para ler e liberar surfaces SDL. Iremos nos aprofundar nele e no sistema de arquivamento ao longo dos artigos, implementando diversas técnicas e sistemas de apoio importantes. Por hora, vamos ver o básico:


// estrutura de apoio
struct SurfaceContainer
{
   SDLSurface *surface;
   int        refCount;
};

// o gerenciador em si
class ContentManager
{
private:
   // o nosso leitor de pack
   PackReader                     mPackReader;
   // este map vai conter os conteúdos já em uso
   map<dword,SurfaceContainer*>   mContent;

public:
   ContentManager();
   ~ContentManager();

   void init ();
   SDLSurface *getSurface ( const dword id );
   void releaseSurface ( const SDLSurface *surface );
};

A classe inclui apenas 3 métodos: init(), q vai inicializar o gerenciador, e os dois métodos para pegar e liberar surfaces. Vamos olhar rapidamente a sua implementação:


void ContentManager::init ()
{
   mPackReader.open("arquivo.pck");
}

SDLSurface *ContentManager::getSurface ( const dword id )
{
   // primeiramente, verificamos se já existe esta surface na memória
   map<dword, SurfaceContainer*>::iterator it = mContent.find(id);
   if ( it != mContent.end() )
   {
      // já temos essa surface. Incrementa contador e
      // retorna surface
      SurfaceContainer *container = it->second;
      container->refCount++;
      return container->surface;
   }

   // nao encontramos nenhuma surface, entao vamos ler do arquivo
   SurfaceContainer *container = new SurfaceContainer();
   container.surface = mPackReader.getSurface(id);
   // inicializar contador de referencia
   container.refCount = 1;
   // colocar no map
   mContent[id] = container;
   // e retornar
   return container.surface;
}

void ContentManager::releaseSurface ( const SDLSurface *surface  )
{
   // vamos procurar o container desta surface
   dword id;
   SurfaceContainer *container = 0;
   map<dword, SurfaceContainer*>::iterator it = mContent.begin();
   map<dword, SurfaceContainer*>::iterator itEnd = mContent.end();
   for ( ; it != itEnd; ++it )
   {
      if ( it->second->surface == surface )
      {
         id = it->first;
         container = it->second;
      }
   }

   // checar erros
   if ( !container ) { /*ERRO*/ }

   // decrementa contador e verifica se podemos remover da memoria
   container->refCount--;
   if ( !(container->refCount) )
   {
      mContent.remove(id);
      SDL_FreeSurface(container->surface);
      delete container;
   }
}

Pronto. Este gerenciador apenas controla surfaces SDL a partir de imagens estocadas no nosso arquivo Pack binário. O controle de uso é simples e com alta probabilidade de erro (ie. necessita que o programador lembre de liberar as surfaces), e seus algoritmos são ineficientes.

Porém, agora temos uma estrutura básica em cima da qual poderei explicar e implementar detalhadamente um sistema de empacotamento e gerenciamento de conteúdo de nível profissional. No próximo artigo, irei me aprofundar em algumas técnicas de empacotamento “reais”, para que a partir daí poderemos implementar um empacotador interessante, rápido prático e útil.

Até mais garotada!

2 comments so far

  1. Eduardo on

    Cara muito show parabéns pelo tuto !!! vou aplicar !

  2. Carlos on

    Muito bom, pena que não sabe fazer o tal “modo profissional”, se alguem souber ajude esse pobre coitado…


Deixe um comentário