lsnes rr2-β24
[lsnes.git] / src / emulation / sky / romimage.cpp
blob9849dc8e761a373c8b083c3d85790ce471632627
1 #include "romimage.hpp"
2 #include "framebuffer.hpp"
3 #include "tasdemos.hpp"
4 #include "physics.hpp"
5 #include "instance.hpp"
6 #include "library/hex.hpp"
7 #include "library/string.hpp"
8 #include "library/zip.hpp"
9 #include <sys/time.h>
10 #include <boost/iostreams/categories.hpp>
11 #include <boost/iostreams/copy.hpp>
12 #include <boost/iostreams/stream.hpp>
13 #include <boost/iostreams/stream_buffer.hpp>
14 #include <boost/iostreams/filter/symmetric.hpp>
15 #include <boost/iostreams/filter/zlib.hpp>
16 #include <boost/iostreams/filtering_stream.hpp>
17 #include <boost/iostreams/device/back_inserter.hpp>
19 namespace sky
21 uint64_t get_utime()
23 struct timeval tv;
24 gettimeofday(&tv, NULL);
25 return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
29 void load_builtin_demos(std::vector<demoset_entry>& _demos)
31 const unsigned char* ptr = tasdemos_data;
32 while(*ptr) {
33 ptr++;
34 demoset_entry e;
35 memcpy(e.hash, ptr, 32);
36 ptr += 32;
37 uint32_t l = 0;
38 l = (l << 8) | *(ptr++);
39 l = (l << 8) | *(ptr++);
40 l = (l << 8) | *(ptr++);
41 e.demodata.resize(l);
42 memcpy(&e.demodata[0], ptr, l);
43 ptr += l;
44 _demos.push_back(e);
48 void load_demos(std::vector<demoset_entry>& _demos, const std::string& filename)
50 zip::reader r(filename);
51 for(auto i : r) {
52 regex_results rx;
53 if(rx = regex("([0-9A-Fa-f]{64}).rec", i)) {
54 demoset_entry e;
55 memset(e.hash, 0, 32);
56 for(unsigned j = 0; j < 64; j++) {
57 unsigned x = i[j];
58 x = (x & 0x1F) ^ 0x10;
59 x = x - (x >> 4) * 7;
60 e.hash[j / 2] = 16 * e.hash[j / 2] + x;
62 std::istream& z = r[i];
63 boost::iostreams::back_insert_device<std::vector<char>> rd(e.demodata);
64 boost::iostreams::copy(z, rd);
65 delete &z;
66 _demos.push_back(e);
71 void load_rom(struct instance& inst, const std::string& filename)
73 std::string errfile;
74 try {
75 errfile = "/SPEED.DAT";
76 gauge _speed_dat(zip::readrel(filename + errfile, ""), 0x22);
77 errfile = "/OXY_DISP.DAT";
78 gauge _oxydisp_dat(zip::readrel(filename + errfile, ""), 0x0a);
79 errfile = "/FUL_DISP.DAT";
80 gauge _fueldisp_dat(zip::readrel(filename + errfile, ""), 0x0a);
81 errfile = "/ROADS.LZS";
82 roads_lzs _levels(zip::readrel(filename + errfile, ""));
83 errfile = "/CARS.LZS";
84 image _ship(zip::readrel(filename + errfile, ""));
85 errfile = "/DASHBRD.LZS";
86 image _dashboard(zip::readrel(filename + errfile, ""));
87 if(_dashboard.width != 320 || _dashboard.height > 200) {
88 std::cerr << _dashboard.width << "x" << _dashboard.height << std::endl;
89 throw std::runtime_error("Must be 320 wide and at most 200 high");
91 errfile = "/GOMENU.LZS";
92 image _levelselect(zip::readrel(filename + errfile, ""));
93 if(_levelselect.width != 320 || _levelselect.height != 200)
94 throw std::runtime_error("Must be 320x200");
95 errfile = "/SFX.SND";
96 sounds _soundfx(zip::readrel(filename + errfile, ""), 5);
97 errfile = "/DEMO.REC";
98 demo _builtin_demo(zip::readrel(filename + errfile, ""), true);
99 image _backgrounds[10];
100 for(unsigned i = 0; i < 10; i++) {
101 std::string n = "/WORLDx.LZS";
102 n[6] = '0' + i;
103 errfile = n;
104 //Skip nonexistent backgrounds.
105 try {
106 std::istream& x = zip::openrel(filename + errfile, "");
107 delete &x;
108 } catch(...) {
109 continue;
111 _backgrounds[i] = image(zip::readrel(filename + errfile, ""));
112 if(_backgrounds[i].width != 320 || _backgrounds[i].height > 200)
113 throw std::runtime_error("Must be 320 wide and at most 200 high");
115 std::vector<demoset_entry> _demos;
116 load_builtin_demos(_demos);
117 errfile = "<demos>";
118 load_demos(_demos, filename);
120 inst.speed_dat = _speed_dat;
121 inst.oxydisp_dat = _oxydisp_dat;
122 inst.fueldisp_dat = _fueldisp_dat;
123 inst.levels = _levels;
124 inst.ship = _ship;
125 inst.dashboard = _dashboard;
126 inst.levelselect = _levelselect;
127 inst.soundfx = _soundfx;
128 inst.builtin_demo = _builtin_demo;
129 inst.demos = _demos;
130 memcpy(inst.dashpalette, inst.dashboard.palette, sizeof(inst.dashpalette));
131 for(unsigned i = 0; i < 10; i++)
132 inst.backgrounds[i] = _backgrounds[i];
133 inst.rom_filename = filename;
134 } catch(std::exception& e) { throw std::runtime_error(errfile + ": " + e.what()); }
137 //Combine background and dashboard into origbuffer and render.
138 void combine_background(struct instance& inst, size_t back)
140 memset(inst.origbuffer, 0, sizeof(inst.origbuffer));
141 image& bg = inst.backgrounds[back];
142 if(bg.width && bg.height) {
143 size_t pixels = 320 * bg.height;
144 for(unsigned i = 0; i < pixels; i++)
145 inst.origbuffer[i] = bg[i] & 0x00FFFFFFU;
148 size_t pixels = 320 * inst.dashboard.height;
149 size_t writestart = 64000 - 320 * inst.dashboard.height;
150 for(unsigned i = 0; i < pixels; i++)
151 if(inst.dashboard.decode[i])
152 inst.origbuffer[i + writestart] = inst.dashboard[i] | 0xFF000000U;
154 render_backbuffer(inst);
157 demo lookup_demo(struct instance& inst, const uint8_t* levelhash)
159 for(auto i = inst.demos.rbegin(); i != inst.demos.rend(); i++) {
160 if(!memcmp(i->hash, levelhash, 32))
161 return demo(i->demodata, false);
163 throw std::runtime_error("No demo found for level");
167 #ifdef DEMO_PLAYER
168 int main(int argc, char** argv)
170 sky::load_rom(argv[1]);
171 int lvl = atoi(argv[2]);
172 if(!sky::levels.present(lvl)) {
173 std::cerr << "Level " << lvl << " not found" << std::endl;
174 return 1;
176 sky::level& l = sky::levels[lvl];
177 uint8_t hash[32];
178 l.sha256_hash(hash);
179 std::cerr << "Level hash is " << hex::b_to(hash, 32) << std::endl;
180 sky::demo d;
181 try {
182 if(lvl)
183 d = sky::lookup_demo(hash);
184 else
185 d = sky::builtin_demo;
186 } catch(std::exception& e) {
187 std::cerr << "Demo lookup failed" << std::endl;
188 return 1;
190 std::cerr << "Found a demo." << std::endl;
191 sky::physics p;
192 p.level_init(l);
193 uint64_t t1 = sky::get_utime();
194 while(!p.death) {
195 uint16_t buttons = d.fetchkeys(0, p.lpos, p.framecounter);
196 int lr = 0, ad = 0;
197 bool jump = ((buttons & 16) != 0);
198 if((buttons & 1) != 0) lr--;
199 if((buttons & 2) != 0) lr++;
200 if((buttons & 4) != 0) ad++;
201 if((buttons & 8) != 0) ad--;
202 if((buttons & 256) != 0) lr = 2; //Cheat for demo.
203 if((buttons & 512) != 0) ad = 2; //Cheat for demo.
204 p.simulate_frame(l, lr, ad, jump);
205 //std::cerr << p.framecounter << " pos: l=" << p.lpos << " h=" << p.hpos
206 // << " v=" << p.vpos << " death=" << (int)p.death << std::endl;
208 uint64_t t2 = sky::get_utime();
209 std::cerr << "Simulated " << p.framecounter << " frames in " << (t2 - t1) << "usec ("
210 << 1000000 * p.framecounter / (t2 - t1) << " fps)" << std::endl;
211 if(p.death == 255)
212 std::cerr << "LEVEL COMPLETED!" << std::endl;
213 return (p.death == 255) ? 0 : 2;
215 #endif