Loading from archives

    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

    
    bool _compress_fastlz(slCompressionInfo* info);
    bool _decompress_fastlz(slCompressionInfo* info);
    std::vector<slArchiveZipFile*> m_zipFiles;
    

    Methods

    
    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.

    
    // meshloader
    virtual void Load(const char* path, slMeshLoaderCallback*, uint8_t* buffer, uint32_t bufferSz) = 0;
    // imageloader
    virtual slImage* Load(const char* path, uint8_t* buffer, uint32_t bufferSz) = 0;
    

    For loading from memory user must write something(anything) in path with right extension

    In meshloaders I added methods for getting the extension of the file

    For example mesh loader.

    
    slMeshLoaderImpl::extension slMeshLoaderImpl::_GetExtension(const char* path)
    {
    	size_t len = strlen(path);
    	if (len > 4)
    	{
    		// find last '.'
    		size_t last_dot = 0;
    		for (size_t i = 0; i < len; ++i)
    		{
    			if (path[i] == '.')
    				last_dot = i;
    		}
    		if (last_dot)
    		{
    			if (strcmp(".obj", &path[last_dot]) == 0)
    				return extension::obj;
    		}
    	}
    	return extension::_bad;
    }
    
    void slMeshLoaderImpl::Load(const char* path, slMeshLoaderCallback* cb)
    {
    	auto e = _GetExtension(path);
    	switch (e)
    	{
    	case slMeshLoaderImpl::extension::_bad:
    		break;
    	case slMeshLoaderImpl::extension::obj:
    		LoadOBJ(path, cb);
    		break;
    	}
    }
    
    void slMeshLoaderImpl::Load(const char* path, slMeshLoaderCallback* cb, uint8_t* buffer, uint32_t bufferSz)
    {
    	auto e = _GetExtension(path);
    	switch (e)
    	{
    	case slMeshLoaderImpl::extension::_bad:
    		break;
    	case slMeshLoaderImpl::extension::obj:
    		LoadOBJ(path, cb, buffer, bufferSz);
    		break;
    	}
    }
    
    

    First function for BMP

    
    slImage* slImageLoaderImpl::LoadBMP(const char* path)
    {
    	SL_ASSERT_ST(path);
    
    	slImage* img = 0;
    	uint32_t file_size = 0;
    	uint8_t* ptr = slFramework::SummonFileBuffer(path, &file_size, false);
    	if (ptr)
    	{
    		img = LoadBMP(path, ptr, (uint32_t)file_size);
    		slDestroy(ptr);
    	}
    	return img;
    }
    

    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.

    
    slStringA slFramework::GetPathA(const slString& v)
    {
    	slString p = g_framework->m_appPath;
    	p.append(v);
    	slStringA stra;
    	p.to_utf8(stra);
    
    	if (!std::filesystem::exists(stra.c_str()))
    	{
    		p.assign(v);
    		while(p.size())
    		{
    			if (p[0] == U'.'
    				|| p[0] == U'\\'
    				|| p[0] == U'/')
    				p.pop_front();
    			else
    				break;
    		}
    		p.to_utf8(stra);
    	}
    	return stra;
    }
    

    Now everyting works.

    In framework I added. for future.

    
    void slFramework::SetImageLoaderConvertToRGBA8(bool v){
    	g_framework->m_imageLoaderConverToRGBA8 = v;
    }
    bool slFramework::GetImageLoaderConvertToRGBA8(){
    	return g_framework->m_imageLoaderConverToRGBA8;
    }
    

    User may want to use original image data.

    PNG

    
    #include <png.h>
    #include <zlib.h>
    
    static void __cdecl user_error_fn(png_structp png_ptr, png_const_charp msg) {
    	fprintf(stderr, "%s\n", msg);
    	longjmp(png_jmpbuf(png_ptr), 1);
    }
    static void __cdecl user_warning_fn(png_structp /*png_ptr*/, png_const_charp msg) {
    	fprintf(stderr, "%s\n", msg);
    }
    class PNG
    {
    public:
    
    	PNG() :
    		png_ptr(nullptr),
    		info_ptr(nullptr)
    	{}
    
    	~PNG() {
    		if (info_ptr) {
    			png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    		}
    		else if (png_ptr)
    			png_destroy_read_struct(&png_ptr, NULL, NULL);
    	}
    
    	png_structp png_ptr;
    	png_infop info_ptr;
    };
    
    void PNGAPI user_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
    {
    	slFileBuffer* f = (slFileBuffer*)png_get_io_ptr(png_ptr);
    	f->Read((char*)data, (uint32_t)length);
    }
    
    slImage* slImageLoaderImpl::LoadPNG(const char* path)
    {
    	SL_ASSERT_ST(path);
    
    	slImage* img = 0;
    	uint32_t file_size = 0;
    	uint8_t* ptr = slFramework::SummonFileBuffer(path, &file_size, false);
    	if (ptr)
    	{
    		img = LoadPNG(path, ptr, (uint32_t)file_size);
    		slDestroy(ptr);
    	}
    	return img;
    }
    
    slImage* slImageLoaderImpl::LoadPNG(const char* path, uint8_t* buffer, uint32_t bufferSz)
    {
    	SL_ASSERT_ST(path);
    	SL_ASSERT_ST(buffer);
    	SL_ASSERT_ST(bufferSz);
    
    	slFileBuffer file(buffer, bufferSz);
    
    	uint8_t buf[4];
    	file.Read((char*)buf, 4);
    	if (png_sig_cmp(buf, (png_size_t)0, 4)) {
    		slLog::PrintWarning("PNG: unsupported format\n");
    		return 0;
    	}
    
    	PNG png;
    	png.png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, user_error_fn, user_warning_fn);
    	if (!png.png_ptr) {
    		slLog::PrintWarning("PNG: unsupported format\n");
    		return 0;
    	}
    
    	png.info_ptr = png_create_info_struct(png.png_ptr);
    	if (!png.info_ptr) {
    		slLog::PrintWarning("PNG: unsupported format\n");
    		return 0;
    	}
    
    	if (setjmp(png_jmpbuf(png.png_ptr))) {
    		slLog::PrintWarning("PNG: unsupported format\n");
    		return 0;
    	}
    
    	png_set_read_fn(png.png_ptr, (void*)&file, user_read_fn);
    	png_set_sig_bytes(png.png_ptr, 4);
    	png_read_info(png.png_ptr, png.info_ptr);
    
    	uint32_t w, h;
    	int bits;
    	int color_type;
    	png_get_IHDR(png.png_ptr, png.info_ptr, &w, &h, &bits, &color_type, NULL, NULL, NULL);
    
    	if (color_type == PNG_COLOR_TYPE_PALETTE)
    		png_set_palette_to_rgb(png.png_ptr);
    
    	if (bits < 8) {
    		if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
    			png_set_expand_gray_1_2_4_to_8(png.png_ptr);
    		else
    			png_set_packing(png.png_ptr);
    	}
    
    	if (png_get_valid(png.png_ptr, png.info_ptr, PNG_INFO_tRNS))
    		png_set_tRNS_to_alpha(png.png_ptr);
    
    	if (bits == 16)
    		png_set_strip_16(png.png_ptr);
    
    	if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
    		png_set_gray_to_rgb(png.png_ptr);
    
    	int intent;
    	const double screen_gamma = 2.2;
    
    	if (png_get_sRGB(png.png_ptr, png.info_ptr, &intent))
    		png_set_gamma(png.png_ptr, screen_gamma, 0.45455);
    	else {
    		double image_gamma;
    		if (png_get_gAMA(png.png_ptr, png.info_ptr, &image_gamma))
    			png_set_gamma(png.png_ptr, screen_gamma, image_gamma);
    		else
    			png_set_gamma(png.png_ptr, screen_gamma, 0.45455);
    	}
    
    	png_read_update_info(png.png_ptr, png.info_ptr);
    	png_get_IHDR(png.png_ptr, png.info_ptr, &w, &h, &bits, &color_type,
    		NULL, NULL, NULL);
    
    	slImage* image = slCreate<slImage>();
    	image->m_info.m_width = w;
    	image->m_info.m_height = h;
    	if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
    		image->m_info.m_format = slImageFormat::r8g8b8a8;
    		image->m_info.m_pitch = image->m_info.m_width * 4;
    	}
    	else {
    		image->m_info.m_format = slImageFormat::r8g8b8;
    		image->m_info.m_pitch = image->m_info.m_width * 3;
    	}
    
    	image->m_dataSize = image->m_info.m_pitch * image->m_info.m_height;
    	image->m_data = (uint8_t*)slMemory::malloc(image->m_dataSize);
    
    	png_bytep* row_pointers = new png_bytep[image->m_info.m_height];
    	for (uint32_t row = 0, p = 0; row < image->m_info.m_height; ++row) {
    		row_pointers[row] = &image->m_data[p];
    		p += image->m_info.m_pitch;
    	}
    
    	png_read_image(png.png_ptr, row_pointers);
    	png_read_end(png.png_ptr, NULL);
    	
    	if (slFramework::GetImageLoaderConvertToRGBA8())
    		image->ConvertTo(slImageFormat::r8g8b8a8);
    
    	return image;
    }
    

    JPG

    
    #include "jpeglib.h"
    #include <setjmp.h>
    
    struct my_error_mgr {
    	struct jpeg_error_mgr pub;
    	jmp_buf setjmp_buffer;
    };
    typedef struct my_error_mgr* my_error_ptr;
    
    void my_error_exit(j_common_ptr cinfo)
    {
    	my_error_ptr myerr = (my_error_ptr)cinfo->err;
    	(*cinfo->err->output_message) (cinfo);
    	longjmp(myerr->setjmp_buffer, 1);
    }
    
    class JPG_Loader {
    public:
    	JPG_Loader() {}
    	~JPG_Loader() {
    		jpeg_destroy_decompress(&cinfo);
    	}
    
    	slImage* Load(const char* p, uint8_t* buffer, uint32_t bufferSz)
    	{
    		slFileBuffer file(buffer, bufferSz);
    
    		cinfo.err = jpeg_std_error(&jerr.pub);
    		jerr.pub.error_exit = my_error_exit;
    		if (setjmp(jerr.setjmp_buffer)) {
    			return 0;
    		}
    		jpeg_create_decompress(&cinfo);
    		jpeg_mem_src(&cinfo, buffer, bufferSz);
    		jpeg_read_header(&cinfo, TRUE);
    		jpeg_start_decompress(&cinfo);
    
    
    		slImage* img = slCreate<slImage>();
    
    		img->m_info.m_width = cinfo.image_width;
    		img->m_info.m_height = cinfo.image_height;
    		img->m_info.m_bits = 24;
    		img->m_info.m_format = slImageFormat::r8g8b8;
    		img->m_info.m_pitch = cinfo.output_width * cinfo.output_components;
    		img->m_dataSize = img->m_info.m_pitch * img->m_info.m_height;
    		img->m_data = (uint8_t*)slMemory::malloc(img->m_dataSize);
    
    		uint8_t* imageDataPtr = img->m_data;
    
    		JSAMPARRAY jbuffer = (*cinfo.mem->alloc_sarray)
    			((j_common_ptr)&cinfo, JPOOL_IMAGE, img->m_info.m_pitch, 1);
    
    		while (cinfo.output_scanline < cinfo.output_height) {
    			jpeg_read_scanlines(&cinfo, jbuffer, 1);
    			memcpy(&imageDataPtr[0], jbuffer[0], img->m_info.m_pitch);
    			imageDataPtr += img->m_info.m_pitch;
    		}
    		jpeg_finish_decompress(&cinfo);
    
    		if (slFramework::GetImageLoaderConvertToRGBA8())
    			img->ConvertTo(slImageFormat::r8g8b8a8);;
    
    		return img;
    	}
    
    	jpeg_decompress_struct cinfo;
    	my_error_mgr jerr;
    };
    
    slImage* slImageLoaderImpl::LoadJPG(const char* path)
    {
    	SL_ASSERT_ST(path);
    
    	slImage* img = 0;
    	uint32_t file_size = 0;
    	uint8_t* ptr = slFramework::SummonFileBuffer(path, &file_size, false);
    	if (ptr)
    	{
    		img = LoadJPG(path, ptr, (uint32_t)file_size);
    		slDestroy(ptr);
    	}
    	return img;
    }
    
    slImage* slImageLoaderImpl::LoadJPG(const char* path, uint8_t* buffer, uint32_t bufferSz)
    {
    	SL_ASSERT_ST(path);
    	SL_ASSERT_ST(buffer);
    	SL_ASSERT_ST(bufferSz);
    
    	JPG_Loader loader;
    	return loader.Load(path, buffer, bufferSz);
    }
    

    TGA

    
    //========================================================================
    // GLFW - An OpenGL framework
    // Platform:    Any
    // API version: 2.7
    // WWW:         http://www.glfw.org/
    //------------------------------------------------------------------------
    // Copyright (c) 2002-2006 Marcus Geelnard
    // Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.org>
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //    claim that you wrote the original software. If you use this software
    //    in a product, an acknowledgment in the product documentation would
    //    be appreciated but is not required.
    //
    // 2. Altered source versions must be plainly marked as such, and must not
    //    be misrepresented as being the original software.
    //
    // 3. This notice may not be removed or altered from any source
    //    distribution.
    //
    //========================================================================
    
    //========================================================================
    // Description:
    //
    // TGA format image file loader. This module supports version 1 Targa
    // images, with these restrictions:
    //  - Pixel format may only be 8, 24 or 32 bits
    //  - Colormaps must be no longer than 256 entries
    //
    //=====================================================================
    
    
    #include "slowlib.h"
    
    #include "slowlib.imageloaderimpl.h"
    #include "slowlib.base/common/slFileBuffer.h"
    
    typedef struct {
    	int idlen;                 // 1 byte
    	int cmaptype;              // 1 byte
    	int imagetype;             // 1 byte
    	int cmapfirstidx;          // 2 bytes
    	int cmaplen;               // 2 bytes
    	int cmapentrysize;         // 1 byte
    	int xorigin;               // 2 bytes
    	int yorigin;               // 2 bytes
    	int width;                 // 2 bytes
    	int height;                // 2 bytes
    	int bitsperpixel;          // 1 byte
    	int imageinfo;             // 1 byte
    	int _alphabits;            // (derived from imageinfo)
    	int _origin;               // (derived from imageinfo)
    } _tga_header_t;
    
    #define _TGA_CMAPTYPE_NONE      0
    #define _TGA_CMAPTYPE_PRESENT   1
    
    #define _TGA_IMAGETYPE_NONE     0
    #define _TGA_IMAGETYPE_CMAP     1
    #define _TGA_IMAGETYPE_TC       2
    #define _TGA_IMAGETYPE_GRAY     3
    #define _TGA_IMAGETYPE_CMAP_RLE 9
    #define _TGA_IMAGETYPE_TC_RLE   10
    #define _TGA_IMAGETYPE_GRAY_RLE 11
    
    #define _TGA_IMAGEINFO_ALPHA_MASK   0x0f
    #define _TGA_IMAGEINFO_ALPHA_SHIFT  0
    #define _TGA_IMAGEINFO_ORIGIN_MASK  0x30
    #define _TGA_IMAGEINFO_ORIGIN_SHIFT 4
    
    #define _TGA_ORIGIN_BL 0
    #define _TGA_ORIGIN_BR 1
    #define _TGA_ORIGIN_UL 2
    #define _TGA_ORIGIN_UR 3
    
    static int ReadTGAHeader(slFileBuffer& f, _tga_header_t* h) {
    	unsigned char buf[18];
    	int pos;
    
    	// Read TGA file header from file
    	pos = f.Tell();
    	f.Read(buf, 18);
    
    	// Interpret header (endian independent parsing)
    	h->idlen = (int)buf[0];
    	h->cmaptype = (int)buf[1];
    	h->imagetype = (int)buf[2];
    	h->cmapfirstidx = (int)buf[3] | (((int)buf[4]) << 8);
    	h->cmaplen = (int)buf[5] | (((int)buf[6]) << 8);
    	h->cmapentrysize = (int)buf[7];
    	h->xorigin = (int)buf[8] | (((int)buf[9]) << 8);
    	h->yorigin = (int)buf[10] | (((int)buf[11]) << 8);
    	h->width = (int)buf[12] | (((int)buf[13]) << 8);
    	h->height = (int)buf[14] | (((int)buf[15]) << 8);
    	h->bitsperpixel = (int)buf[16];
    	h->imageinfo = (int)buf[17];
    
    	// Extract alphabits and origin information
    	h->_alphabits = (int)(h->imageinfo & _TGA_IMAGEINFO_ALPHA_MASK) >>
    		_TGA_IMAGEINFO_ALPHA_SHIFT;
    	h->_origin = (int)(h->imageinfo & _TGA_IMAGEINFO_ORIGIN_MASK) >>
    		_TGA_IMAGEINFO_ORIGIN_SHIFT;
    
    	// Validate TGA header (is this a TGA file?)
    	if ((h->cmaptype == 0 || h->cmaptype == 1) &&
    		((h->imagetype >= 1 && h->imagetype <= 3) ||
    			(h->imagetype >= 9 && h->imagetype <= 11)) &&
    		(h->bitsperpixel == 8 || h->bitsperpixel == 24 ||
    			h->bitsperpixel == 32))
    	{
    		// Skip the ID field
    		f.Seek(h->idlen, SEEK_CUR);
    
    		// Indicate that the TGA header was valid
    		return 1;
    	}
    	else
    	{
    		// Restore file position
    		f.Seek(pos, SEEK_SET);
    
    		// Indicate that the TGA header was invalid
    		return 0;
    	}
    }
    
    static void ReadTGA_RLE(unsigned char* buf, int size, int bpp, slFileBuffer& f) {
    	int repcount, bytes, k, n;
    	unsigned char pixel[4];
    	char c;
    
    	// Dummy check
    	if (bpp > 4)
    	{
    		return;
    	}
    
    	while (size > 0)
    	{
    		// Get repetition count
    		f.Read(&c, 1);
    		repcount = (unsigned int)c;
    		bytes = ((repcount & 127) + 1) * bpp;
    		if (size < bytes)
    		{
    			bytes = size;
    		}
    
    		// Run-Length packet?
    		if (repcount & 128)
    		{
    			f.Read(pixel, bpp);
    			for (n = 0; n < (repcount & 127) + 1; n++)
    			{
    				for (k = 0; k < bpp; k++)
    				{
    					*buf++ = pixel[k];
    				}
    			}
    		}
    		else
    		{
    			// It's a Raw packet
    			f.Read(buf, bytes);
    			buf += bytes;
    		}
    
    		size -= bytes;
    	}
    }
    
    slImage* slImageLoaderImpl::LoadTGA(const char* path)
    {
    	SL_ASSERT_ST(path);
    
    	slImage* img = 0;
    	uint32_t file_size = 0;
    	uint8_t* ptr = slFramework::SummonFileBuffer(path, &file_size, false);
    	if (ptr)
    	{
    		img = LoadTGA(path, ptr, (uint32_t)file_size);
    		slDestroy(ptr);
    	}
    	return img;
    }
    
    slImage* slImageLoaderImpl::LoadTGA(const char* path, uint8_t* buffer, uint32_t bufferSz)
    {
    	SL_ASSERT_ST(path);
    	SL_ASSERT_ST(buffer);
    	SL_ASSERT_ST(bufferSz);
    
    	_tga_header_t h;
    	unsigned char* cmap, * pix, tmp, * src, * dst;
    	int cmapsize, pixsize, pixsize2;
    	int bpp, bpp2, k, m, n, swapx, swapy;
    
    	slFileBuffer f(buffer, bufferSz);
    
    	// Read TGA header
    	if (!ReadTGAHeader(f, &h))
    	{
    		slLog::PrintWarning("TGA: unsupported format\n");
    		return 0;
    	}
    
    	// Is there a colormap?
    	cmapsize = (h.cmaptype == _TGA_CMAPTYPE_PRESENT ? 1 : 0) * h.cmaplen *
    		((h.cmapentrysize + 7) / 8);
    	if (cmapsize > 0)
    	{
    		// Is it a colormap that we can handle?
    		if ((h.cmapentrysize != 24 && h.cmapentrysize != 32) ||
    			h.cmaplen == 0 || h.cmaplen > 256)
    		{
    			slLog::PrintWarning("TGA: unsupported format\n");
    			return 0;
    		}
    
    		// Allocate memory for colormap
    		cmap = (unsigned char*)slMemory::malloc(cmapsize);
    
    		// Read colormap from file
    		f.Read(cmap, cmapsize);
    	}
    	else
    	{
    		cmap = NULL;
    	}
    
    	// Size of pixel data
    	pixsize = h.width * h.height * ((h.bitsperpixel + 7) / 8);
    
    	// Bytes per pixel (pixel data - unexpanded)
    	bpp = (h.bitsperpixel + 7) / 8;
    
    	// Bytes per pixel (expanded pixels - not colormap indeces)
    	if (cmap)
    	{
    		bpp2 = (h.cmapentrysize + 7) / 8;
    	}
    	else
    	{
    		bpp2 = bpp;
    	}
    
    	// For colormaped images, the RGB/RGBA image data may use more memory
    	// than the stored pixel data
    	pixsize2 = h.width * h.height * bpp2;
    
    	// Allocate memory for pixel data
    	pix = (unsigned char*)slMemory::malloc(pixsize2);
    	if (pix == NULL)
    	{
    		if (cmap)
    		{
    			free(cmap);
    		}
    		return 0;
    	}
    
    	// Read pixel data from file
    	if (h.imagetype >= _TGA_IMAGETYPE_CMAP_RLE)
    	{
    		ReadTGA_RLE(pix, pixsize, bpp, f);
    	}
    	else
    	{
    		f.Read(pix, pixsize);
    	}
    
    	// If the image origin is not what we want, re-arrange the pixels
    	switch (h._origin)
    	{
    	default:
    	case _TGA_ORIGIN_UL:
    		swapx = 0;
    		swapy = 1;
    		break;
    
    	case _TGA_ORIGIN_BL:
    		swapx = 0;
    		swapy = 0;
    		break;
    
    	case _TGA_ORIGIN_UR:
    		swapx = 1;
    		swapy = 1;
    		break;
    
    	case _TGA_ORIGIN_BR:
    		swapx = 1;
    		swapy = 0;
    		break;
    	}
    
    	if (swapy)
    	{
    		src = pix;
    		dst = &pix[(h.height - 1) * h.width * bpp];
    		for (n = 0; n < h.height / 2; n++)
    		{
    			for (m = 0; m < h.width; m++)
    			{
    				for (k = 0; k < bpp; k++)
    				{
    					tmp = *src;
    					*src++ = *dst;
    					*dst++ = tmp;
    				}
    			}
    			dst -= 2 * h.width * bpp;
    		}
    	}
    
    	if (swapx)
    	{
    		src = pix;
    		dst = &pix[(h.width - 1) * bpp];
    		for (n = 0; n < h.height; n++)
    		{
    			for (m = 0; m < h.width / 2; m++)
    			{
    				for (k = 0; k < bpp; k++)
    				{
    					tmp = *src;
    					*src++ = *dst;
    					*dst++ = tmp;
    				}
    				dst -= 2 * bpp;
    			}
    			src += ((h.width + 1) / 2) * bpp;
    			dst += ((3 * h.width + 1) / 2) * bpp;
    		}
    	}
    
    	// Convert BGR/BGRA to RGB/RGBA, and optionally colormap indeces to
    	// RGB/RGBA values
    	if (cmap)
    	{
    		// Convert colormap pixel format (BGR -> RGB or BGRA -> RGBA)
    		if (bpp2 == 3 || bpp2 == 4)
    		{
    			for (n = 0; n < h.cmaplen; n++)
    			{
    				tmp = cmap[n * bpp2];
    				cmap[n * bpp2] = cmap[n * bpp2 + 2];
    				cmap[n * bpp2 + 2] = tmp;
    			}
    		}
    
    		// Convert pixel data to RGB/RGBA data
    		for (m = h.width * h.height - 1; m >= 0; m--)
    		{
    			n = pix[m];
    			for (k = 0; k < bpp2; k++)
    			{
    				pix[m * bpp2 + k] = cmap[n * bpp2 + k];
    			}
    		}
    
    		// Free memory for colormap (it's not needed anymore)
    		slMemory::free(cmap);
    	}
    	else
    	{
    		// Convert image pixel format (BGR -> RGB or BGRA -> RGBA)
    		if (bpp2 == 3 || bpp2 == 4)
    		{
    			src = pix;
    			dst = &pix[2];
    			for (n = 0; n < h.height * h.width; n++)
    			{
    				tmp = *src;
    				*src = *dst;
    				*dst = tmp;
    				src += bpp2;
    				dst += bpp2;
    			}
    		}
    	}
    
    	slImage* image = slCreate<slImage>();
    	image->m_info.m_width = h.width;
    	image->m_info.m_height = h.height;
    	image->m_info.m_bits = bpp2 * 8;
    	image->m_data = pix;
    
    	if (image->m_info.m_bits == 32)
    	{
    		image->m_info.m_format = slImageFormat::r8g8b8a8;
    		image->m_info.m_pitch = image->m_info.m_width * 4;
    	}
    	else if (image->m_info.m_bits == 24)
    	{
    		image->m_info.m_format = slImageFormat::r8g8b8;
    		image->m_info.m_pitch = image->m_info.m_width * 3;
    	}
    
    	image->m_dataSize = image->m_info.m_pitch * image->m_info.m_height;
    	
    	if (slFramework::GetImageLoaderConvertToRGBA8())
    	{
    		image->ConvertTo(slImageFormat::r8g8b8a8); 
    		image->FlipVertical();
    	}
    
    	return image;
    }
    
    Download