bsnes: Add Lua function to dump 2bpp sprites (and allow 4-color palettes)
[lsnes.git] / src / emulation / bsnes-legacy / bitmap.cpp
blobf87fd0e15ad6c48f3636327d18992c08731ec4ee
1 #include "lua/bitmap.hpp"
2 #include "lua/internal.hpp"
3 #include "library/serialization.hpp"
4 #include "library/memoryspace.hpp"
5 #include "core/instance.hpp"
6 #include "core/memorymanip.hpp"
8 namespace
10 template<bool create, bool bpp2>
11 int dump_sprite(lua::state& L, lua::parameters& P)
13 auto& core = CORE();
14 lua_bitmap* b;
15 uint64_t addr;
16 uint32_t width, height;
17 size_t stride1 = bpp2 ? 16 : 32;
18 size_t stride2;
20 if(!create) {
21 P(b);
22 width = b->width / 8;
23 height = b->height / 8;
24 if((width | height) & 8)
25 throw std::runtime_error("The image size must be multiple of 8x8");
27 addr = lua_get_read_address(P);
28 if(create)
29 P(width, height);
30 P(P.optional(stride2, bpp2 ? 256 : 512));
32 if(create)
33 b = lua::_class<lua_bitmap>::create(L, width * 8, height * 8);
35 bool map = false;
36 uint64_t rangebase;
37 uint64_t rangesize;
38 if(stride1 < (1 << 30) / (width ? width : 1) && stride2 < (1 << 30) / (height ? height : 1)) {
39 map = true;
40 rangebase = addr;
41 rangesize = (width - 1) * stride1 + (height - 1) * stride2 + 32;
44 char* mem = map ? core.memory->get_physical_mapping(rangebase, rangesize) : NULL;
45 if(mem) {
46 for(unsigned j = 0; j < height; j++)
47 for(unsigned i = 0; i < width; i++) {
48 uint64_t sbase = addr + stride2 * j + stride1 * i - rangebase;
49 for(unsigned k = 0; k < 8; k++) {
50 uint8_t byte1 = mem[sbase + 2 * k];
51 uint8_t byte2 = mem[sbase + 2 * k + 1];
52 uint8_t byte3 = bpp2 ? 0 : mem[sbase + 2 * k + 16];
53 uint8_t byte4 = bpp2 ? 0 : mem[sbase + 2 * k + 17];
54 uint32_t soff = (j * 8 + k) * (8 * width) + i * 8;
55 for(unsigned l = 0; l < 8; l++) {
56 uint32_t v = 0;
57 //No harm including the nonexistent planes (they are 0).
58 if((byte1 >> (7 - l)) & 1) v |= 1;
59 if((byte2 >> (7 - l)) & 1) v |= 2;
60 if((byte3 >> (7 - l)) & 1) v |= 4;
61 if((byte4 >> (7 - l)) & 1) v |= 8;
62 b->pixels[soff + l] = v;
66 } else {
67 for(unsigned j = 0; j < height; j++)
68 for(unsigned i = 0; i < width; i++) {
69 uint64_t sbase = addr + stride2 * j + stride1 * i;
70 for(unsigned k = 0; k < 8; k++) {
71 uint8_t byte1 = core.memory->read<uint8_t>(sbase + 2 * k);
72 uint8_t byte2 = core.memory->read<uint8_t>(sbase + 2 * k + 1);
73 uint8_t byte3 = bpp2 ? 0 : core.memory->read<uint8_t>(sbase + 2 * k +
74 16);
75 uint8_t byte4 = bpp2 ? 0 : core.memory->read<uint8_t>(sbase + 2 * k +
76 17);
77 uint32_t soff = (j * 8 + k) * (8 * width) + i * 8;
78 for(unsigned l = 0; l < 8; l++) {
79 //No harm including the nonexistent planes (they are 0).
80 uint32_t v = 0;
81 if((byte1 >> (7 - l)) & 1) v |= 1;
82 if((byte2 >> (7 - l)) & 1) v |= 2;
83 if((byte3 >> (7 - l)) & 1) v |= 4;
84 if((byte4 >> (7 - l)) & 1) v |= 8;
85 b->pixels[soff + l] = v;
90 return create ? 1 : 0;
93 template<bool create>
94 int dump_palette(lua::state& L, lua::parameters& P)
96 auto& core = CORE();
97 uint64_t addr;
98 bool full = false, ftrans;
99 bool fourcc = false;
100 lua_palette* p;
102 if(!create) {
103 P(p);
104 size_t ccount = p->color_count;
105 if(ccount != 4 && ccount != 16 && ccount != 256)
106 throw std::runtime_error("Palette to read must be 4, 16 or 256 colors");
107 full = (ccount == 256);
108 fourcc = (ccount == 4);
110 addr = lua_get_read_address(P);
111 if(create) {
112 //Hacky way to do integers.
113 if(P.is_number()) {
114 uint64_t col;
115 P(col);
116 if(col == 4) fourcc = true;
117 else if(col == 16);
118 else if(col == 256) full = true;
119 else
120 throw std::runtime_error("Palette to read must be 4, 16 or 256 colors");
121 } else {
122 P(full);
125 P(ftrans);
127 size_t ps = full ? 256 : (fourcc ? 4 : 16);
128 if(create) {
129 p = lua::_class<lua_palette>::create(L);
130 p->adjust_palette_size(ps);
132 uint8_t* mem = reinterpret_cast<uint8_t*>(core.memory->get_physical_mapping(addr, 2 * ps));
133 if(mem) {
134 for(unsigned j = 0; j < ps; j++) {
135 if(j == 0 && ftrans)
136 p->colors[j] = framebuffer::color(-1);
137 else {
138 uint64_t val = 0;
139 uint16_t c = serialization::u16l(mem + 2 * j);
140 uint64_t r = (c >> 0) & 0x1F;
141 uint64_t g = (c >> 5) & 0x1F;
142 uint64_t b = (c >> 10) & 0x1F;
143 val = (r << 19) | ((r << 14) & 0xFF0000) | (g << 11) | ((g << 6) & 0xFF00) |
144 (b << 3) | (b >> 2);
145 p->colors[j] = framebuffer::color(val);
148 } else {
149 for(unsigned j = 0; j < ps; j++) {
150 if(j == 0 && ftrans)
151 p->colors[j] = framebuffer::color(-1);
152 else {
153 uint64_t val = 0;
154 uint16_t c = core.memory->read<uint16_t>(addr + j * 2);
155 uint64_t r = (c >> 0) & 0x1F;
156 uint64_t g = (c >> 5) & 0x1F;
157 uint64_t b = (c >> 10) & 0x1F;
158 val = (r << 19) | ((r << 14) & 0xFF0000) | (g << 11) | ((g << 6) & 0xFF00) |
159 (b << 3) | (b >> 2);
160 p->colors[j] = framebuffer::color(val);
164 return create ? 1 : 0;
167 lua::functions bitmap_fns_snes(lua_func_misc, "bsnes", {
168 {"dump_palette", dump_palette<true>},
169 {"redump_palette", dump_palette<false>},
170 {"dump_sprite", dump_sprite<true, false>},
171 {"redump_sprite", dump_sprite<false, false>},
172 {"dump_sprite2", dump_sprite<true, true>},
173 {"redump_sprite2", dump_sprite<false, true>},