1 #include "core/advdumper.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/instance.hpp"
4 #include "core/moviedata.hpp"
5 #include "core/random.hpp"
6 #include "core/messages.hpp"
7 #include "core/rom.hpp"
8 #include "video/sox.hpp"
9 #include "library/serialization.hpp"
10 #include "library/string.hpp"
11 #include "library/hex.hpp"
25 std::string
get_pipedec_command(const std::string
& type
)
27 auto r
= regex("(.*)[\\/][^\\/]+", get_config_path());
29 throw std::runtime_error("Can't read pipedec config file");
30 std::ifstream
cfg(r
[1] + "/pipedec/.pipedec");
32 throw std::runtime_error("Can't read pipedec config file");
34 while(std::getline(cfg
, line
)) {
35 if(line
.length() >= type
.length() && line
.substr(0, type
.length()) == type
)
36 return line
.substr(type
.length());
38 throw std::runtime_error("No command found for " + type
);
41 std::string
substitute_cmd(std::string cmd
, uint32_t w
, uint32_t h
, uint32_t fps_n
, uint32_t fps_d
,
44 bool perc_flag
= false;
46 for(size_t i
= 0; i
< cmd
.length(); i
++) {
51 out
= out
+ (stringfmt() << w
).str();
54 out
= out
+ (stringfmt() << h
).str();
57 out
= out
+ (stringfmt() << fps_n
).str();
60 out
= out
+ (stringfmt() << fps_d
).str();
63 out
= out
+ (stringfmt() << time(NULL
)).str();
66 out
= out
+ (stringfmt() << segid
++).str();
81 class pipedec_dump_obj
: public dumper_base
84 pipedec_dump_obj(master_dumper
& _mdumper
, dumper_factory_base
& _fbase
, const std::string
& mode
,
85 const std::string
& prefix
)
86 : dumper_base(_mdumper
, _fbase
), mdumper(_mdumper
)
88 bool _upsidedown
= (mode
[0] != 'v');
89 bool _swap
= (mode
[2] == 'R');
90 bool _bits32
= (mode
[3] == '3');
93 throw std::runtime_error("Expected filename");
95 cmd
= get_pipedec_command("!" + mode
+ ":");
97 auto r
= mdumper
.get_rate();
98 audio
= new sox_dumper(prefix
, static_cast<double>(r
.first
) / r
.second
, 2);
100 throw std::runtime_error("Can't open output file");
101 have_dumped_frame
= false;
102 upsidedown
= _upsidedown
;
110 segid
= hex::from
<uint32_t>(get_random_hexstring(8));
111 mdumper
.add_dumper(*this);
112 } catch(std::bad_alloc
& e
) {
114 } catch(std::exception
& e
) {
115 std::ostringstream x
;
116 x
<< "Error starting PIPEDEC dump: " << e
.what();
117 throw std::runtime_error(x
.str());
119 messages
<< "Dumping to " << prefix
<< std::endl
;
121 ~pipedec_dump_obj() throw()
123 mdumper
.drop_dumper(*this);
127 messages
<< "PIPEDEC Dump finished" << std::endl
;
129 void on_frame(struct framebuffer::raw
& _frame
, uint32_t fps_n
, uint32_t fps_d
)
131 if(!render_video_hud(dscr
, _frame
, fps_n
, fps_d
, 1, 1, 0, 0, 0, 0, NULL
))
133 size_t w
= dscr
.get_width();
134 size_t h
= dscr
.get_height();
135 uint32_t stride
= dscr
.get_stride();
137 if(!video
|| last_width
!= w
|| last_height
!= h
|| last_fps_n
!= fps_n
||
138 last_fps_d
!= fps_d
) {
140 tmp
.resize(4 * stride
+ 16);
141 std::string rcmd
= substitute_cmd(cmd
, w
, h
, fps_n
, fps_d
, segid
);
144 video
= popen(rcmd
.c_str(), "w");
145 #if defined(_WIN32) || defined(_WIN64)
147 setmode(fileno(video
), O_BINARY
);
151 messages
<< "Error starting a segment (" << err
<< ")" << std::endl
;
161 uint32_t alignment
= (16 - reinterpret_cast<size_t>(&tmp
[0])) % 16;
162 char* data2
= &tmp
[alignment
];
163 for(size_t i
= 0; i
< h
; i
++) {
164 size_t ri
= upsidedown
? (h
- i
- 1) : i
;
165 char* data
= reinterpret_cast<char*>(dscr
.rowptr(ri
));
168 framebuffer::copy_swap4(reinterpret_cast<uint8_t*>(data2
),
169 reinterpret_cast<uint32_t*>(data
), stride
);
171 memcpy(data2
, data
, 4 * stride
);
174 framebuffer::copy_drop4s(reinterpret_cast<uint8_t*>(data2
),
175 reinterpret_cast<uint32_t*>(data
), stride
);
177 framebuffer::copy_drop4(reinterpret_cast<uint8_t*>(data2
),
178 reinterpret_cast<uint32_t*>(data
), stride
);
180 if(fwrite(data2
, bits32
? 4 : 3, w
, video
) < w
)
181 messages
<< "Video write error" << std::endl
;
183 have_dumped_frame
= true;
186 void on_sample(short l
, short r
)
188 if(have_dumped_frame
&& audio
)
191 void on_rate_change(uint32_t n
, uint32_t d
)
193 messages
<< "Pipedec: Changing sound rate mid-dump not supported." << std::endl
;
195 void on_gameinfo_change(const master_dumper::gameinfo
& gi
)
204 master_dumper
& mdumper
;
207 bool have_dumped_frame
;
208 struct framebuffer::fb
<false> dscr
;
213 std::vector
<char> tmp
;
216 uint32_t last_height
;
221 class adv_pipedec_dumper
: public dumper_factory_base
224 adv_pipedec_dumper() : dumper_factory_base("INTERNAL-PIPEDEC")
228 ~adv_pipedec_dumper() throw();
229 std::set
<std::string
> list_submodes() throw(std::bad_alloc
)
231 std::set
<std::string
> x
;
242 unsigned mode_details(const std::string
& mode
) throw()
244 return target_type_file
;
246 std::string
mode_extension(const std::string
& mode
) throw()
250 std::string
name() throw(std::bad_alloc
)
254 std::string
modename(const std::string
& mode
) throw(std::bad_alloc
)
258 pipedec_dump_obj
* start(master_dumper
& _mdumper
, const std::string
& mode
, const std::string
& prefix
)
259 throw(std::bad_alloc
, std::runtime_error
)
261 return new pipedec_dump_obj(_mdumper
, *this, mode
, prefix
);
265 adv_pipedec_dumper::~adv_pipedec_dumper() throw()