When I load mesh or BMP I use FILE directly, but I need to do this in other way,
because I need to unpack files from archive files. Method that I know, I think best,
is
read whole file in buffer if file not in archive
unpack file in buffer if file in archive
Then just rewrite OBJ and BMP loaders, using buffer, not FILE.
I don't know how to do all this archive things.
... but I found in one of my old projects code for this.
I need to create a new classes structs and other
// This is not algorithm.
// This is library. If some library will have many algorithms,
// here will be more options like:
// megalib_deflate,
// megalib_lz77,
// megalib_lzma,
// We have:
// - fastlz (LZ77)
enum class slCompressorType : uint32_t
{
// m_level:
// 1 for speed
// 2 compression ratio
fastlz
};
// Minimum iformation for data compression.
struct slCompressionInfo
{
slCompressorType m_compressorType = slCompressorType::fastlz;
// Read the comments below in Compress\Decomress
uint32_t m_sizeUncompressed = 0;
uint8_t* m_dataUncompressed = 0;
uint32_t m_sizeCompressed = 0;
uint8_t* m_dataCompressed = 0;
// See CompressorType. If the value is wrong, the
// default value will be used.
uint32_t m_level = 2;
};
struct slArchiveZipFile
{
slStringA m_fileName;
void* m_implementation = 0;
};
class slArchiveSystem
{
public:
// Add zip file for unzipping files from it.
static slArchiveZipFile* ZipAdd(const char* zipFile);
// Unzip file. If a is NULL first found file will be processed.
// Set a if you have many zip files with same file inside.
// Function will allocate memory using slMemory::malloc, and will write data size in size.
// Pointer will return if everything is OK, use slMemory::free for deallocation.
static uint8_t* ZipUnzip(const char* fileInZip, uint32_t* size, slArchiveZipFile* a = NULL);
// Set all data in `info`. If compression is OK, true will return
// and m_dataCompressed will have address.
// You need to call slMemory::free(m_dataCompressed) for deallocation.
static bool Compress(slCompressionInfo* info);
// Like in `Compress` method.
// Allocate memory m_dataUncompressed by yourself.
// You must know m_sizeUncompressed.
static bool Decompress(slCompressionInfo* info);
// Decompress and store data in std::vector.
// if info.m_dataCompressed is NULL (or !info.m_sizeCompressed)
// then function will try to copy data from info.m_dataUncompressed
// Function will allocate memory, put all data to std::vector and then
// will free memory.
// info.m_dataUncompressed and info.m_sizeUncompressed will be ignored
static bool Decompress(slCompressionInfo* info, std::vector& toVector);
};
For unzip I need minizip and zlib.
All data for archive things will be in slFrameworkImpl
slArchiveZipFile* slArchiveSystem::ZipAdd(const char* fn)
{
SL_ASSERT_ST(fn);
slLog::Print("AddZip: [%s]\n", fn);
if (!std::filesystem::exists(fn))
{
slLog::PrintWarning("AddZip: Unable to find zip file [%s]\n", fn);
return 0;
}
unzFile uf = unzOpen64(fn);
if (uf == NULL)
{
slLog::PrintWarning("AddZip: unzOpen64 failed [%s]\n", fn);
return 0;
}
slArchiveZipFile* zp = slCreate<slArchiveZipFile>();
zp->m_fileName.assign(fn);
zp->m_implementation = uf;
g_framework->m_zipFiles.push_back(zp);
return zp;
}
uint8_t* slArchiveSystem::ZipUnzip(const char* fileInZip, uint32_t* size, slArchiveZipFile* a)
{
if (a)
goto lockate_file;
for (size_t i = 0, sz = g_framework->m_zipFiles.size(); i < sz; ++i)
{
if (unzLocateFile(g_framework->m_zipFiles[i]->m_implementation, fileInZip, 0) == UNZ_OK)
{
a = g_framework->m_zipFiles[i];
goto unzip_file;
}
}
slLog::PrintWarning("file %s not found in the zipfile\n", fileInZip);
return 0;
lockate_file:;
if (unzLocateFile(a->m_implementation, fileInZip, 0) != UNZ_OK)
{
slLog::PrintWarning("file %s not found in the zipfile\n", fileInZip);
return 0;
}
unzip_file:;
char filename_inzip[256];
unz_file_info64 file_info;
int err = unzGetCurrentFileInfo64(a->m_implementation, &file_info,
filename_inzip,
sizeof(filename_inzip),
NULL, 0, NULL, 0);
if (err != UNZ_OK)
{
slLog::PrintWarning("error %d with zipfile in unzGetCurrentFileInfo [%s]\n", err, fileInZip);
return 0;
}
err = unzOpenCurrentFilePassword(a->m_implementation, 0);
if (err != UNZ_OK)
{
slLog::PrintWarning("error %d with zipfile in unzOpenCurrentFilePassword [%s]\n", err, fileInZip);
return 0;
}
uint8_t* data = (uint8_t*)slMemory::malloc(file_info.uncompressed_size);
*size = (uint32_t)file_info.uncompressed_size;
err = unzReadCurrentFile(a->m_implementation, data, file_info.uncompressed_size);
if (err < 0)
{
slLog::PrintWarning("error %d with zipfile in unzReadCurrentFile [%s]\n", err, fileInZip);
free(data);
data = 0;
}
unzCloseCurrentFile(a->m_implementation);
return data;
}
bool slArchiveSystem::Compress(slCompressionInfo* info)
{
SL_ASSERT_ST(info);
SL_ASSERT_ST(info->m_dataUncompressed);
SL_ASSERT_ST(info->m_sizeUncompressed);
info->m_dataCompressed = 0;
info->m_sizeCompressed = 0;
switch (info->m_compressorType)
{
case slCompressorType::fastlz:
return g_framework->_compress_fastlz(info);
}
return false;
}
bool slArchiveSystem::Decompress(slCompressionInfo* info)
{
SL_ASSERT_ST(info);
SL_ASSERT_ST(info->m_dataCompressed);
SL_ASSERT_ST(info->m_sizeCompressed);
SL_ASSERT_ST(info->m_dataUncompressed);
SL_ASSERT_ST(info->m_sizeUncompressed);
switch (info->m_compressorType)
{
case slCompressorType::fastlz:
return g_framework->_decompress_fastlz(info);
}
return false;
}
bool slArchiveSystem::Decompress(slCompressionInfo* info, std::vector& toVector)
{
SL_ASSERT_ST(info);
toVector.clear();
toVector.reserve(info->m_sizeUncompressed);
if (!info->m_dataCompressed || !info->m_sizeCompressed)
{
if (info->m_dataUncompressed && info->m_sizeUncompressed)
{
std::copy(info->m_dataUncompressed,
info->m_dataUncompressed + info->m_sizeUncompressed,
std::back_inserter(toVector));
return true;
}
return false;
}
switch (info->m_compressorType)
{
case slCompressorType::fastlz:
{
slCompressionInfo cmpInf = *info;
cmpInf.m_dataUncompressed = (uint8_t*)slMemory::malloc(cmpInf.m_sizeUncompressed);
bool result = g_framework->_decompress_fastlz(&cmpInf);
if (result)
{
std::copy(cmpInf.m_dataUncompressed,
cmpInf.m_dataUncompressed + cmpInf.m_sizeUncompressed,
std::back_inserter(toVector));
}
slMemory::free(cmpInf.m_dataUncompressed);
return result;
}break;
}
return false;
}
bool slFrameworkImpl::_compress_fastlz(slCompressionInfo* info)
{
if (info->m_level != 1 && info->m_level != 2)
info->m_level = 2;
if (info->m_sizeUncompressed < 32)
return false;
uint8_t* output = (uint8_t*)slMemory::malloc(info->m_sizeUncompressed + (info->m_sizeUncompressed / 3));
int compressed_size = fastlz_compress_level(
(int)info->m_level,
info->m_dataUncompressed,
info->m_sizeUncompressed,
output);
if ((uint32_t)compressed_size >= info->m_sizeUncompressed)
{
slMemory::free(output);
return false;
}
output = (uint8_t*)slMemory::realloc(output, compressed_size);
info->m_dataCompressed = output;
info->m_sizeCompressed = compressed_size;
return true;
}
bool slFrameworkImpl::_decompress_fastlz(slCompressionInfo* info)
{
int decompressed_size = fastlz_decompress(
info->m_dataCompressed,
info->m_sizeCompressed,
info->m_dataUncompressed,
info->m_sizeUncompressed);
if (!decompressed_size)
{
slLog::PrintWarning("%s: %s\n", __FUNCTION__, "can't decompress");
return false;
}
if (info->m_sizeUncompressed != decompressed_size)
{
info->m_sizeUncompressed = decompressed_size;
slLog::PrintWarning("%s: %s\n", __FUNCTION__, "`decompressed size` is not the sme with m_sizeUncompressed");
}
return true;
}
New method in framework
// read or unzip file into buffer
// if isText then will be added ' ' and 0 at the end
static uint8_t* SummonFileBuffer(const char* path, uint32_t* szOut, bool isText);
Implementation
First, try to read file, if something not good, try to unzip.
uint8_t* slFramework::SummonFileBuffer(const char* path, uint32_t* szOut)
{
SL_ASSERT_ST(path);
SL_ASSERT_ST(szOut);
*szOut = 0;
std::filesystem::path p = path;
if (std::filesystem::exists(p))
{
*szOut = (uint32_t)std::filesystem::file_size(p);
if (*szOut)
{
FILE* f = 0;
fopen_s(&f, path, "rb");
if (f)
{
uint8_t* data = (uint8_t*)slMemory::malloc(*szOut);
fread(data, *szOut, 1, f);
fclose(f);
return data;
}
else
{
slLog::PrintError("Unable to open file in %s : %i\n", SL_FUNCTION, SL_LINE);
}
}
}
return slArchiveSystem::ZipUnzip(path, szOut, 0);
}
Now I need to overload methods for loading OBJ and BMP
User may want to `load` file from memory. He may want to use his own buffer.
Second function is larger, and it use FILE. Sometimes it easy to use
functions like fseek ftell fread, and use it for buffers.
In one of my old project I have class that will give you this functuionality
// This class works like FILE but with buffer.
// If you use just FILE, you can use function like setbuf, but ftell will give you 0.
// This class is for reading.
// This class not allocate memory.
class slFileBuffer
{
uint8_t* m_buffer = 0;
size_t m_size = 0;
size_t m_cursor = 0;
public:
slFileBuffer(const uint8_t* buffer, size_t size)
:
m_buffer((uint8_t*)buffer),
m_size(size)
{
}
~slFileBuffer()
{
}
// Use SEEK_SET and other known macros for second parameter
void Seek(size_t offset, int where)
{
switch (where)
{
default:
case SEEK_CUR:
{
m_cursor += offset;
if (m_cursor > m_size)
m_cursor = m_size;
}break;
case SEEK_END:
m_cursor = m_size;
break;
case SEEK_SET:
m_cursor = offset;
break;
}
}
size_t Tell()
{
return m_cursor;
}
size_t Read(void* buffer, size_t size)
{
uint8_t* bytes = (uint8_t*)buffer;
size_t numRead = 0;
for (numRead = 0; numRead < size; ++numRead)
{
if (m_cursor == m_size)
return numRead;
bytes[numRead] = m_buffer[m_cursor];
++m_cursor;
}
return numRead;
}
};
Now I can change BMP loader using this class.
Ok. For loading file from zip archive need to write path without `..\\`
But, I want to load file from folder, and if it not exist, unzip it.
MyModel* mm = slCreate<MyModel>(gs);
if (mm->Load(slFramework::GetPathA("../data/models/box.obj").c_str()))
printf("LOADED!\n");
mm->SetPosition(globalPosition);
I modified GetPathA. If file not exist, remove some chars from string.