Fix SA1 open bus
[lsnes.git] / src / core / moviefile-text-save.cpp
blob07e4906131c6caa5f9a0820dd2fb4050129afae5
1 #include "core/misc.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/moviefile.hpp"
5 #include "core/moviefile-common.hpp"
6 #include "library/zip.hpp"
7 #include "library/string.hpp"
8 #include "library/minmax.hpp"
9 #include "library/serialization.hpp"
10 #include "library/binarystream.hpp"
11 #include "interface/romtype.hpp"
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <iostream>
16 #include <algorithm>
17 #include <sstream>
18 #include <boost/iostreams/copy.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
20 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
21 #include <windows.h>
22 #endif
24 namespace
26 std::map<std::string, uint64_t> read_active_macros(zip::reader& r, const std::string& member)
28 std::map<std::string, uint64_t> x;
29 if(!r.has_member(member))
30 return x;
31 std::istream& m = r[member];
32 try {
33 while(m) {
34 std::string out;
35 std::getline(m, out);
36 istrip_CR(out);
37 if(out == "")
38 continue;
39 regex_results rx = regex("([0-9]+) +(.*)", out);
40 if(!rx) {
41 messages << "Warning: Bad macro state: '" << out << "'" << std::endl;
42 continue;
44 try {
45 uint64_t f = parse_value<uint64_t>(rx[1]);
46 x[rx[2]] = f;
47 } catch(...) {
50 delete &m;
51 } catch(...) {
52 delete &m;
53 throw;
55 return x;
58 void write_active_macros(zip::writer& w, const std::string& member, const std::map<std::string, uint64_t>& ma)
60 if(ma.empty())
61 return;
62 std::ostream& m = w.create_file(member);
63 try {
64 for(auto i : ma)
65 m << i.second << " " << i.first << std::endl;
66 if(!m)
67 throw std::runtime_error("Can't write ZIP file member");
68 w.close_file();
69 } catch(...) {
70 w.close_file();
71 throw;
75 template<typename T> std::string pick_a_name(const std::map<std::string, T>& map, bool prefer_unnamed)
77 if(prefer_unnamed && !map.count(""))
78 return "";
79 size_t count = 1;
80 while(true) {
81 std::string c = (stringfmt() << "(unnamed branch #" << count++ << ")").str();
82 if(!map.count(c))
83 return c;
87 void write_rrdata(zip::writer& w, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
89 uint64_t count;
90 std::vector<char> out;
91 count = rrd.write(out);
92 w.write_raw_file("rrdata", out);
93 std::ostream& m2 = w.create_file("rerecords");
94 try {
95 m2 << count << std::endl;
96 if(!m2)
97 throw std::runtime_error("Can't write ZIP file member");
98 w.close_file();
99 } catch(...) {
100 w.close_file();
101 throw;
105 void write_authors_file(zip::writer& w, std::vector<std::pair<std::string, std::string>>& authors)
106 throw(std::bad_alloc, std::runtime_error)
108 std::ostream& m = w.create_file("authors");
109 try {
110 for(auto i : authors)
111 if(i.second == "")
112 m << i.first << std::endl;
113 else
114 m << i.first << "|" << i.second << std::endl;
115 if(!m)
116 throw std::runtime_error("Can't write ZIP file member");
117 w.close_file();
118 } catch(...) {
119 w.close_file();
120 throw;
124 void write_input(zip::writer& w, const std::string& mname, controller_frame_vector& input)
125 throw(std::bad_alloc, std::runtime_error)
127 std::ostream& m = w.create_file(mname);
128 try {
129 char buffer[MAX_SERIALIZED_SIZE];
130 for(size_t i = 0; i < input.size(); i++) {
131 input[i].serialize(buffer);
132 m << buffer << std::endl;
134 if(!m)
135 throw std::runtime_error("Can't write ZIP file member");
136 w.close_file();
137 } catch(...) {
138 w.close_file();
139 throw;
143 void write_subtitles(zip::writer& w, const std::string& file, std::map<moviefile_subtiming, std::string>& x)
145 std::ostream& m = w.create_file(file);
146 try {
147 for(auto i : x)
148 m << i.first.get_frame() << " " << i.first.get_length() << " " << s_escape(i.second)
149 << std::endl;
150 if(!m)
151 throw std::runtime_error("Can't write ZIP file member");
152 w.close_file();
153 } catch(...) {
154 w.close_file();
155 throw;
159 void write_pollcounters(zip::writer& w, const std::string& file, const std::vector<uint32_t>& pctr)
161 std::ostream& m = w.create_file(file);
162 try {
163 for(auto i : pctr) {
164 int32_t x = i & 0x7FFFFFFFUL;
165 if((i & 0x80000000UL) == 0)
166 x = -x - 1;
167 m << x << std::endl;
169 if(!m)
170 throw std::runtime_error("Can't write ZIP file member");
171 w.close_file();
172 } catch(...) {
173 w.close_file();
174 throw;
179 void moviefile::save(zip::writer& w, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
181 w.write_linefile("gametype", gametype->get_name());
182 moviefile_write_settings<zip::writer>(w, settings, gametype->get_type().get_settings(), [](zip::writer& w,
183 const std::string& name, const std::string& value) -> void {
184 if(regex_match("port[0-9]+", name))
185 w.write_linefile(name, value);
186 else
187 w.write_linefile("setting." + name, value);
189 w.write_linefile("gamename", gamename, true);
190 w.write_linefile("systemid", "lsnes-rr1");
191 w.write_linefile("controlsversion", "0");
192 coreversion = gametype->get_type().get_core_identifier();
193 w.write_linefile("coreversion", coreversion);
194 w.write_linefile("projectid", projectid);
195 write_rrdata(w, rrd);
196 w.write_linefile("rom.sha256", romimg_sha256[0], true);
197 w.write_linefile("romxml.sha256", romxml_sha256[0], true);
198 w.write_linefile("rom.hint", namehint[0], true);
199 for(size_t i = 1; i < ROM_SLOT_COUNT; i++) {
200 w.write_linefile((stringfmt() << "slot" << (char)(96 + i) << ".sha256").str(), romimg_sha256[i],
201 true);
202 w.write_linefile((stringfmt() << "slot" << (char)(96 + i) << "xml.sha256").str(), romxml_sha256[i],
203 true);
204 w.write_linefile((stringfmt() << "slot" << (char)(96 + i) << ".hint").str(), namehint[i],
205 true);
207 write_subtitles(w, "subtitles", subtitles);
208 for(auto i : movie_sram)
209 w.write_raw_file("moviesram." + i.first, i.second);
210 w.write_numeric_file("starttime.second", movie_rtc_second);
211 w.write_numeric_file("starttime.subsecond", movie_rtc_subsecond);
212 if(!anchor_savestate.empty())
213 w.write_raw_file("savestate.anchor", anchor_savestate);
214 if(is_savestate) {
215 w.write_numeric_file("saveframe", save_frame);
216 w.write_numeric_file("lagcounter", lagged_frames);
217 write_pollcounters(w, "pollcounters", pollcounters);
218 w.write_raw_file("hostmemory", host_memory);
219 w.write_raw_file("savestate", savestate);
220 w.write_raw_file("screenshot", screenshot);
221 for(auto i : sram)
222 w.write_raw_file("sram." + i.first, i.second);
223 w.write_numeric_file("savetime.second", rtc_second);
224 w.write_numeric_file("savetime.subsecond", rtc_subsecond);
225 w.write_numeric_file("pollflag", poll_flag);
226 write_active_macros(w, "macros", active_macros);
228 for(auto i : ramcontent)
229 w.write_raw_file("initram." + i.first, i.second);
230 write_authors_file(w, authors);
232 std::map<std::string, uint64_t> branch_table;
233 uint64_t next_branch = 1;
234 for(auto& i : branches) {
235 uint64_t id;
236 if(&i.second == input)
237 id = 0;
238 else
239 id = next_branch++;
240 branch_table[i.first] = id;
241 w.write_linefile((stringfmt() << "branchname." << id).str(), i.first);
242 if(id)
243 write_input(w, (stringfmt() << "input." << id).str(), i.second);
244 else
245 write_input(w, "input", i.second);
248 w.commit();