netlib.narod.ru< Назад | Оглавление | Далее >

Обработка данных приложения

Все приложения, и особенно игры, в той или иной форме используют данные. Помните вашего персонажа из той компьютерной игры, которой вы посвятили последние три недели? Каждый крошечный фрагмент информации о нем это данные приложения — его имя, очки поражений, уровень опыта, доспехи и оружие, которое он несет. Каждый раз, когда вы выходите из игры, данные вашего персонажа сохраняются и ждут, когда вы снова загрузите их в дальнейшем.

Использование упаковки данных

Простейший способ обращаться с данными приложения — создать систему упаковки данных, которая поддерживает сохранение и загрузку данных. Создав объект, который содержит буфер для данных, вы можете добавить несколько функций, которые будут сохранять и загружать его для вас. Чтобы лучше понять, о чем я говорю, взгляните на приведенный ниже класс:

class cDataPackage
{
    protected:
        // Буфер данных и его размер
        void *m_Buf;
        unsigned long m_Size;

    public:
        cDataPackage() { m_Buf = NULL; m_Size = 0; }

        ~cDataPackage() { Free(); }

        void *Create(unsigned long Size)
        {
            // Освобождаем ранее созданный буфер
            Free();

            // Выделяем память и возвращаем указатель
            return (m_Buf = (void*)new char[(m_Size = Size)]);
        }

        // Освобождаем выделенную память
        void Free() { delete m_Buf; m_Buf = NULL; m_Size = 0; }

        // Сохраняем буфер на диске
        BOOL Save(char *Filename)
        {
            FILE *fp;

            // Проверяем, что есть что-нибудь для записи
            if(m_Buf != NULL && m_Size) {
                //Открываем файл, записываем размер и данные
                if((fp=fopen(Filename, "wb")) != NULL) {
                    fwrite(&m_Size, 1, 4, fp);
                    fwrite(m_Buf, 1, m_Size, fp);
                    fclose(fp);
                    return TRUE;
                }
            }
            return FALSE;
        }

        // Загружаем данные в буфер из файла
        void *Load(char *Filename, unsigned long *Size)
        {
            FILE *fp;

            // Освобождаем предыдущий буфер
            Free();

            if((fp=fopen(Filename, "rb")) != NULL) {
                // Читаем размер и данные
                fread(&m_Size, 1, 4, fp);
                if((m_Buf = (void*)new char[m_Size]) != NULL)
                    fread(m_Buf, 1, m_Size, fp);
                fclose(fp);

                // Сохраняем размер, чтобы вернуть
                if(Size != NULL)
                    *Size = m_Size;

                // Возвращаем указатель
                return m_Buf;
            }
            return NULL;
        }
};

Класс cDataPackage содержит всего четыре функции, которые можно использовать (в действительности их шесть — включая конструктор и деструктор). Первая функция, которую вы будете вызывать, Create, выделяет блок памяти указанного вами размера. Функция Free освобождает этот блок памяти. Что касается Save и Load, то они делают следующее — сохраняют блок данных на жестком диске в файле с указанным именем и загружают блок из заданного файла. Обратите внимание, что функции Create и Load возвращают указатели. Это указатели на буфер данных, для которых вы можете выполнить операцию приведения типа, чтобы преобразовать их в указатели на ваши собственные структуры данных.

Тестирование системы упаковки данных

Представьте, что вы создали хранящий список имен пакет данных, и хотите использовать для работы с этими именами собственную структуру данных. Создав пакет данных и выполнив приведение типа возвращенного указателя к вашей структуре, вы можете сразу приступать к работе с именами, как показано ниже:

// Структура для хранения имени
typedef struct {
    char Name[32];
} sName;

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, \
                   LPSTR szCmdLine, int nCmdShow)
{
    cDataPackage DP;
    DWORD Size;

    // Создаем пакет данных (размером 64 байта),
    // получаем указатель на него и выполняем
    // приведение типа к sName
    sName *Names = (sName*)DP.Create(64);

    // Поскольку мы выделили 64 байта, а каждое имя
    // использует 32 байта, можно хранить два имени
    strcpy(Names[0].Name, "Jim");
    strcpy(Names[1].Name, "Adams");

    // Сохраняем имена на диске и освобождаем буфер данных
    DP.Save("names.dat");
    DP.Free();

    // Загружаем имена с диска. После возврата из функции
    // загрузки значение Size будет равно 64
    Names = (sName*)DP.Load("names.dat", &Size);

    // Отображаем имена
    MessageBox(NULL, Names[0].Name, "1st Name", MB_OK);
    MessageBox(NULL, Names[1].Name, "2nd Name", MB_OK);

    // Освобождаем пакет данных
    DP.Free();

    return 0;
}

Взглянув более пристально на использование буфера данных, вы увидите, что в выделенных 64 байтах для хранения имен используются два блока по 32 байта каждый, как показано на рис. 1.12.


Рис. 1.12. Буфер данных достаточно большой, чтобы хранить все имена

Рис. 1.12. Буфер данных достаточно большой, чтобы хранить все имена. В данном случае хранятся два имени по 32 байта каждое, что дает общий размер буфера равный 64 байтам


Варианты использования упаковки данных огромны. Создав несколько небольших объектов пакетов данных вы можете выполнять приведение типов для любых указателей, сохраняя все данные вашего приложения в однотипных объектах-контейнерах, поддерживающих сохранение и загрузку.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz