AppleWin/source/frontends/retro/bitmaps.cpp
Andrea Odetti b95bcadfe9 Fix bitmap loading.
Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
2020-12-12 18:46:56 +00:00

151 lines
3.7 KiB
C++

#include "frontends/retro/environment.h"
#include <linux/interface.h>
#include <linux/windows/misc.h>
#include <frontends/common2/resources.h>
#include <fstream>
#include <cstring>
namespace
{
void readFileToBuffer(const std::string & filename, std::vector<char> & buffer)
{
std::ifstream file(filename.c_str(), std::ios::binary | std::ios::ate);
const std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
buffer.resize(size);
file.read(buffer.data(), size);
}
template<typename T>
T getAs(const std::vector<char> & buffer, const size_t offset)
{
if (offset + sizeof(T) > buffer.size())
{
throw std::runtime_error("Invalid bitmap");
}
const T * ptr = reinterpret_cast<const T *>(buffer.data() + offset);
return * ptr;
}
// libretro cannot parse BMP with 1 bpp
// simple version implemented here
bool getBitmapData(const std::vector<char> & buffer, int32_t & width, int32_t & height, uint16_t & bpp, const char * & data, uint32_t & size)
{
if (buffer.size() < 2)
{
return false;
}
if (buffer[0] != 0x42 || buffer[1] != 0x4D)
{
return false;
}
const uint32_t fileSize = getAs<uint32_t>(buffer, 2);
if (fileSize != buffer.size())
{
return false;
}
const uint32_t offset = getAs<uint32_t>(buffer, 10);
const uint32_t header = getAs<uint32_t>(buffer, 14);
if (header != 40)
{
return false;
}
width = getAs<int32_t>(buffer, 18);
height = getAs<int32_t>(buffer, 22);
bpp = getAs<uint16_t>(buffer, 28);
const uint32_t imageSize = getAs<uint32_t>(buffer, 34);
if (offset + imageSize > fileSize)
{
return false;
}
data = buffer.data() + offset;
size = imageSize;
return true;
}
}
struct CBITMAP : public CHANDLE
{
std::vector<char> image;
};
std::string getFilename(const std::string & resource)
{
if (resource == "CHARSET40") return "CHARSET4.BMP";
if (resource == "CHARSET82") return "CHARSET82.bmp";
if (resource == "CHARSET8M") return "CHARSET8M.bmp";
if (resource == "CHARSET8C") return "CHARSET8C.bmp";
return resource;
}
HBITMAP LoadBitmap(HINSTANCE hInstance, const char * resource)
{
if (resource)
{
const std::string filename = getFilename(resource);
const std::string path = getResourcePath() + filename;
std::vector<char> buffer;
readFileToBuffer(path, buffer);
if (!buffer.empty())
{
int32_t width, height;
uint16_t bpp;
const char * data;
uint32_t size;
const bool res = getBitmapData(buffer, width, height, bpp, data, size);
log_cb(RETRO_LOG_INFO, "RA2: %s. %s = %dx%d, %dbpp\n", __FUNCTION__, path.c_str(),
width, height, bpp);
if (res && height > 0)
{
CBITMAP * bitmap = new CBITMAP;
bitmap->image.resize(size);
const size_t length = size / height;
// rows are stored upside down
for (size_t row = 0; row < height; ++row)
{
const char * src = data + row * length;
char * dst = bitmap->image.data() + (height - row - 1) * length;
memcpy(dst, src, length);
}
return bitmap;
}
}
else
{
log_cb(RETRO_LOG_INFO, "RA2: %s. Cannot load: %s with path: %s\n", __FUNCTION__, resource, path.c_str());
return nullptr;
}
}
log_cb(RETRO_LOG_INFO, "RA2: %s. Cannot load invaid resource\n", __FUNCTION__);
return nullptr;
}
LONG GetBitmapBits(HBITMAP hbit, LONG cb, LPVOID lpvBits)
{
const CBITMAP & bitmap = dynamic_cast<CBITMAP&>(*hbit);
const char * bits = bitmap.image.data();
const size_t size = bitmap.image.size();
const size_t requested = cb;
const size_t copied = std::min(requested, size);
char * dest = static_cast<char *>(lpvBits);
memcpy(dest, bits, copied);
return copied;
}