Make the SNES rate specials methods
[lsnes.git] / src / video / sdmp.cpp
blob455238c672153fdf2ab923ccfc7f705c6df2a9fc
1 #if defined(BSNES_V084) || defined(BSNES_V085) || defined(BSNES_V086) || defined(BSNES_V087)
2 #include "lsnes.hpp"
3 #include <core/emucore.hpp>
4 #include "core/advdumper.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/emucore.hpp"
7 #include "core/moviedata.hpp"
8 #include "library/serialization.hpp"
9 #include "video/tcp.hpp"
11 #include <iomanip>
12 #include <cassert>
13 #include <cstring>
14 #include <sstream>
15 #include <zlib.h>
16 #include <sstream>
17 #include <fstream>
18 #include <stdexcept>
20 #define CUTOFF 2100000000
21 #define SDUMP_FLAG_HIRES 1
22 #define SDUMP_FLAG_INTERLACED 2
23 #define SDUMP_FLAG_OVERSCAN 4
24 #define SDUMP_FLAG_PAL 8
26 namespace
28 void deleter_fn(void* f)
30 delete reinterpret_cast<std::ofstream*>(f);
33 class sdmp_avsnoop : public information_dispatch
35 public:
36 sdmp_avsnoop(const std::string& prefix, const std::string& mode) throw(std::bad_alloc,
37 std::runtime_error)
38 : information_dispatch("dump-sdmp")
40 enable_send_sound();
41 oprefix = prefix;
42 sdump_ss = (mode != "ms");
43 ssize = 0;
44 next_seq = 0;
45 dumped_pic = false;
46 if(mode == "tcp") {
47 out = &(socket_address(prefix).connect());
48 deleter = socket_address::deleter();
49 } else {
50 out = NULL;
51 deleter = deleter_fn;
55 ~sdmp_avsnoop() throw()
57 try {
58 if(out)
59 deleter(out);
60 } catch(...) {
64 void on_raw_frame(const uint32_t* raw, bool hires, bool interlaced, bool overscan, unsigned region)
66 unsigned flags = 0;
67 flags |= (hires ? SDUMP_FLAG_HIRES : 0);
68 flags |= (interlaced ? SDUMP_FLAG_INTERLACED : 0);
69 flags |= (overscan ? SDUMP_FLAG_OVERSCAN : 0);
70 flags |= (region == VIDEO_REGION_PAL ? SDUMP_FLAG_PAL : 0);
71 unsigned char tbuffer[2049];
72 if(!out || (ssize > CUTOFF && !sdump_ss)) {
73 std::cerr << "Starting new segment" << std::endl;
74 if(out)
75 deleter(out);
76 std::ostringstream str;
77 if(sdump_ss)
78 str << oprefix;
79 else
80 str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++)
81 << ".sdmp";
82 std::string str2 = str.str();
83 out = new std::ofstream(str2.c_str(), std::ios::out | std::ios::binary);
84 if(!*out)
85 throw std::runtime_error("Failed to open '" + str2 + "'");
86 write32ube(tbuffer, 0x53444D50U);
87 auto rates = our_rom->rtype->get_snes_rate();
88 write32ube(tbuffer + 4, rates.first);
89 write32ube(tbuffer + 8, rates.second);
90 out->write(reinterpret_cast<char*>(tbuffer), 12);
91 if(!*out)
92 throw std::runtime_error("Failed to write header to '" + str2 + "'");
93 ssize = 12;
95 dumped_pic = true;
96 tbuffer[0] = flags;
97 for(unsigned i = 0; i < 512; i++) {
98 for(unsigned j = 0; j < 512; j++)
99 write32ube(tbuffer + (4 * j + 1), raw[512 * i + j]);
100 out->write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
102 if(!*out)
103 throw std::runtime_error("Failed to write frame");
104 ssize += 1048577;
107 void on_sample(short l, short r)
109 if(!out || !dumped_pic)
110 return;
111 unsigned char pkt[5];
112 pkt[0] = 16;
113 write16sbe(pkt + 1, l);
114 write16sbe(pkt + 3, r);
115 out->write(reinterpret_cast<char*>(pkt), 5);
116 if(!*out)
117 throw std::runtime_error("Failed to write sample");
118 ssize += 5;
121 void on_dump_end()
123 deleter(out);
124 out = NULL;
127 bool get_dumper_flag() throw()
129 return true;
131 private:
132 std::string oprefix;
133 bool sdump_ss;
134 bool dumped_pic;
135 uint64_t ssize;
136 uint64_t next_seq;
137 void (*deleter)(void* f);
138 std::ostream* out;
141 sdmp_avsnoop* vid_dumper;
143 class adv_sdmp_dumper : public adv_dumper
145 public:
146 adv_sdmp_dumper() : adv_dumper("INTERNAL-SDMP") { information_dispatch::do_dumper_update(); }
147 ~adv_sdmp_dumper() throw();
148 std::set<std::string> list_submodes() throw(std::bad_alloc)
150 std::set<std::string> x;
151 x.insert("ss");
152 x.insert("ms");
153 x.insert("tcp");
154 return x;
157 unsigned mode_details(const std::string& mode) throw()
159 if(mode == "ss")
160 return target_type_file;
161 if(mode == "ms")
162 return target_type_prefix;
163 if(mode == "tcp")
164 return target_type_special;
165 return target_type_mask;
168 std::string mode_extension(const std::string& mode) throw()
170 return "sdmp"; //Ignored anyway in non-ss mode.
173 std::string name() throw(std::bad_alloc)
175 return "SDMP";
178 std::string modename(const std::string& mode) throw(std::bad_alloc)
180 if(mode == "ss")
181 return "Single-Segment";
182 if(mode == "ms")
183 return "Multi-Segment";
184 if(mode == "tcp")
185 return "over TCP/IP";
186 return "What?";
189 bool busy()
191 return (vid_dumper != NULL);
194 void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc,
195 std::runtime_error)
197 if(prefix == "")
198 throw std::runtime_error("Expected target");
199 if(vid_dumper)
200 throw std::runtime_error("SDMP Dump already in progress");
201 try {
202 vid_dumper = new sdmp_avsnoop(prefix, mode);
203 } catch(std::bad_alloc& e) {
204 throw;
205 } catch(std::exception& e) {
206 std::ostringstream x;
207 x << "Error starting SDMP dump: " << e.what();
208 throw std::runtime_error(x.str());
210 messages << "Dumping SDMP (" << mode << ") to " << prefix << std::endl;
211 information_dispatch::do_dumper_update();
214 void end() throw()
216 if(!vid_dumper)
217 throw std::runtime_error("No SDMP video dump in progress");
218 try {
219 vid_dumper->on_dump_end();
220 messages << "SDMP Dump finished" << std::endl;
221 } catch(std::bad_alloc& e) {
222 throw;
223 } catch(std::exception& e) {
224 messages << "Error ending SDMP dump: " << e.what() << std::endl;
226 delete vid_dumper;
227 vid_dumper = NULL;
228 information_dispatch::do_dumper_update();
230 } adv;
232 adv_sdmp_dumper::~adv_sdmp_dumper() throw()
236 #endif