Split cases for prefix and special for dumper targets
[lsnes.git] / src / video / raw.cpp
blobdfb446de925fd9e1c33c1bab48b6b6b76ab66746
1 #include "core/advdumper.hpp"
2 #include "core/dispatch.hpp"
3 #include "video/tcp.hpp"
4 #include "library/serialization.hpp"
6 #include <iomanip>
7 #include <cassert>
8 #include <cstring>
9 #include <cerrno>
10 #include <cstring>
11 #include <sstream>
12 #include <fstream>
13 #include <zlib.h>
15 #define IS_RGB(m) (((m) + ((m) >> 3)) & 2)
16 #define IS_64(m) (m % 5 < 2)
17 #define IS_TCP(m) (((m % 5) * (m % 5)) % 5 == 1)
21 namespace
23 unsigned strhash(const std::string& str)
25 unsigned h = 0;
26 for(size_t i = 0; i < str.length(); i++)
27 h = (2 * h + static_cast<unsigned char>(str[i])) % 11;
28 return h;
31 class raw_avsnoop : public information_dispatch
33 public:
34 raw_avsnoop(const std::string& prefix, bool _swap, bool _bits64, bool socket_mode)
35 : information_dispatch("dump-raw")
37 enable_send_sound();
38 if(socket_mode) {
39 socket_address videoaddr = socket_address(prefix);
40 socket_address audioaddr = videoaddr.next();
41 video = audio = NULL;
42 try {
43 video = &videoaddr.connect();
44 audio = &audioaddr.connect();
45 } catch(...) {
46 delete video;
47 delete audio;
48 throw;
50 } else {
51 video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary);
52 audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary);
54 if(!*video || !*audio)
55 throw std::runtime_error("Can't open output files");
56 have_dumped_frame = false;
57 swap = _swap;
58 bits64 = _bits64;
61 ~raw_avsnoop() throw()
63 delete video;
64 delete audio;
67 void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
69 if(!video)
70 return;
71 unsigned magic;
72 if(bits64)
73 magic = 0x30201000U;
74 else
75 magic = 0x18100800U;
76 unsigned r = (reinterpret_cast<unsigned char*>(&magic))[swap ? 2 : 0];
77 unsigned g = (reinterpret_cast<unsigned char*>(&magic))[1];
78 unsigned b = (reinterpret_cast<unsigned char*>(&magic))[swap ? 0 : 2];
79 uint32_t hscl = (_frame.width < 400) ? 2 : 1;
80 uint32_t vscl = (_frame.height < 400) ? 2 : 1;
81 if(bits64) {
82 render_video_hud(dscr2, _frame, hscl, vscl, r, g, b, 0, 0, 0, 0, NULL);
83 for(size_t i = 0; i < dscr2.height; i++)
84 video->write(reinterpret_cast<char*>(dscr2.rowptr(i)), 8 * dscr2.width);
85 } else {
86 render_video_hud(dscr, _frame, hscl, vscl, r, g, b, 0, 0, 0, 0, NULL);
87 for(size_t i = 0; i < dscr.height; i++)
88 video->write(reinterpret_cast<char*>(dscr.rowptr(i)), 4 * dscr.width);
90 if(!*video)
91 messages << "Video write error" << std::endl;
92 have_dumped_frame = true;
95 void on_sample(short l, short r)
97 if(have_dumped_frame && audio) {
98 char buffer[4];
99 write16sbe(buffer + 0, l);
100 write16sbe(buffer + 2, r);
101 audio->write(buffer, 4);
105 void on_dump_end()
107 delete video;
108 delete audio;
109 video = NULL;
110 audio = NULL;
113 bool get_dumper_flag() throw()
115 return true;
117 private:
118 std::ostream* audio;
119 std::ostream* video;
120 bool have_dumped_frame;
121 struct screen<false> dscr;
122 struct screen<true> dscr2;
123 bool swap;
124 bool bits64;
127 raw_avsnoop* vid_dumper;
129 class adv_raw_dumper : public adv_dumper
131 public:
132 adv_raw_dumper() : adv_dumper("INTERNAL-RAW") {information_dispatch::do_dumper_update(); }
133 ~adv_raw_dumper() throw();
134 std::set<std::string> list_submodes() throw(std::bad_alloc)
136 std::set<std::string> x;
137 for(size_t i = 0; i < (socket_address::supported() ? 2 : 1); i++)
138 for(size_t j = 0; j < 2; j++)
139 for(size_t k = 0; k < 2; k++)
140 x.insert(std::string("") + (i ? "tcp" : "") + (j ? "bgr" : "rgb")
141 + (k ? "64" : "32"));
142 return x;
145 unsigned mode_details(const std::string& mode) throw()
147 return IS_TCP(strhash(mode)) ? target_type_special : target_type_prefix;
150 std::string name() throw(std::bad_alloc)
152 return "RAW";
155 std::string modename(const std::string& mode) throw(std::bad_alloc)
157 unsigned _mode = strhash(mode);
158 std::string x = std::string((IS_RGB(_mode) ? "RGB" : "BGR")) +
159 (IS_64(_mode) ? " 64-bit" : " 32-bit") + (IS_TCP(_mode) ? " over TCP/IP" : "");
160 return x;
163 bool busy()
165 return (vid_dumper != NULL);
168 void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc,
169 std::runtime_error)
171 unsigned _mode = strhash(mode);
172 bool bits64 = IS_64(_mode);
173 bool swap = !IS_RGB(_mode);
174 bool sock = IS_TCP(_mode);
176 if(prefix == "")
177 throw std::runtime_error("Expected prefix");
178 if(vid_dumper)
179 throw std::runtime_error("RAW dumping already in progress");
180 try {
181 vid_dumper = new raw_avsnoop(prefix, swap, bits64, sock);
182 } catch(std::bad_alloc& e) {
183 throw;
184 } catch(std::exception& e) {
185 std::ostringstream x;
186 x << "Error starting RAW dump: " << e.what();
187 throw std::runtime_error(x.str());
189 messages << "Dumping to " << prefix << std::endl;
190 information_dispatch::do_dumper_update();
193 void end() throw()
195 if(!vid_dumper)
196 throw std::runtime_error("No RAW video dump in progress");
197 try {
198 vid_dumper->on_dump_end();
199 messages << "RAW Dump finished" << std::endl;
200 } catch(std::bad_alloc& e) {
201 throw;
202 } catch(std::exception& e) {
203 messages << "Error ending RAW dump: " << e.what() << std::endl;
205 delete vid_dumper;
206 vid_dumper = NULL;
207 information_dispatch::do_dumper_update();
209 } adv;
211 adv_raw_dumper::~adv_raw_dumper() throw()