1 #include "romimage.hpp"
2 #include "framebuffer.hpp"
3 #include "tasdemos.hpp"
5 #include "instance.hpp"
6 #include "library/hex.hpp"
7 #include "library/string.hpp"
8 #include "library/zip.hpp"
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>
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
;
35 memcpy(e
.hash
, ptr
, 32);
38 l
= (l
<< 8) | *(ptr
++);
39 l
= (l
<< 8) | *(ptr
++);
40 l
= (l
<< 8) | *(ptr
++);
42 memcpy(&e
.demodata
[0], ptr
, l
);
48 void load_demos(std::vector
<demoset_entry
>& _demos
, const std::string
& filename
)
50 zip::reader
r(filename
);
53 if(rx
= regex("([0-9A-Fa-f]{64}).rec", i
)) {
55 memset(e
.hash
, 0, 32);
56 for(unsigned j
= 0; j
< 64; j
++) {
58 x
= (x
& 0x1F) ^ 0x10;
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
);
71 void load_rom(struct instance
& inst
, const std::string
& filename
)
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");
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";
104 //Skip nonexistent backgrounds.
106 std::istream
& x
= zip::openrel(filename
+ errfile
, "");
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
);
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
;
125 inst
.dashboard
= _dashboard
;
126 inst
.levelselect
= _levelselect
;
127 inst
.soundfx
= _soundfx
;
128 inst
.builtin_demo
= _builtin_demo
;
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");
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
;
176 sky::level
& l
= sky::levels
[lvl
];
179 std::cerr
<< "Level hash is " << hex::b_to(hash
, 32) << std::endl
;
183 d
= sky::lookup_demo(hash
);
185 d
= sky::builtin_demo
;
186 } catch(std::exception
& e
) {
187 std::cerr
<< "Demo lookup failed" << std::endl
;
190 std::cerr
<< "Found a demo." << std::endl
;
193 uint64_t t1
= sky::get_utime();
195 uint16_t buttons
= d
.fetchkeys(0, p
.lpos
, p
.framecounter
);
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
;
212 std::cerr
<< "LEVEL COMPLETED!" << std::endl
;
213 return (p
.death
== 255) ? 0 : 2;