Namespace library port-controller stuff
[lsnes.git] / src / core / moviefile-esave.cpp
blobc2c66ce1915a60e160dfea87fb6659adef55c719
1 #include "core/moviefile-binary.hpp"
2 #include "core/moviefile.hpp"
3 #include "library/serialization.hpp"
5 #include <fcntl.h>
6 #include <unistd.h>
7 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
8 #include <windows.h>
9 //FUCK YOU. SERIOUSLY.
10 #define EXTRA_OPENFLAGS O_BINARY
11 #else
12 #define EXTRA_OPENFLAGS 0
13 #endif
15 namespace
17 void emerg_write_bytes(int handle, const uint8_t* d, size_t dsize)
19 while(dsize > 0) {
20 ssize_t r = write(handle, d, dsize);
21 if(r > 0) {
22 d += r;
23 dsize -= r;
27 void emerg_write_number(int handle, uint64_t num)
29 uint8_t data[10];
30 size_t len = 0;
31 do {
32 bool cont = (num > 127);
33 data[len++] = (cont ? 0x80 : 0x00) | (num & 0x7F);
34 num >>= 7;
35 } while(num);
36 emerg_write_bytes(handle, data, len);
38 size_t number_size(uint64_t num)
40 unsigned len = 0;
41 do {
42 num >>= 7;
43 len++;
44 } while(num);
45 return len;
47 void emerg_write_number32(int handle, uint32_t num)
49 char buf[4];
50 serialization::u32b(buf, num);
51 emerg_write_bytes(handle, (const uint8_t*)buf, 4);
53 void emerg_write_member(int handle, uint32_t tag, uint64_t size)
55 emerg_write_number32(handle, 0xaddb2d86);
56 emerg_write_number32(handle, tag);
57 emerg_write_number(handle, size);
59 void emerg_write_blob_implicit(int handle, const std::vector<char>& v)
61 emerg_write_bytes(handle, (const uint8_t*)&v[0], v.size());
63 void emerg_write_byte(int handle, uint8_t byte)
65 emerg_write_bytes(handle, &byte, 1);
67 size_t string_size(const std::string& str)
69 return number_size(str.length()) + str.length();
71 void emerg_write_string_implicit(int handle, const std::string& str)
73 for(size_t i = 0; i < str.length(); i++)
74 emerg_write_byte(handle, str[i]);
76 void emerg_write_string(int handle, const std::string& str)
78 emerg_write_number(handle, str.length());
79 emerg_write_string_implicit(handle, str);
81 void emerg_write_movie(int handle, const portctrl::frame_vector& v, uint32_t tag)
83 uint64_t stride = v.get_stride();
84 uint64_t pageframes = v.get_frames_per_page();
85 uint64_t vsize = v.size();
86 emerg_write_member(handle, tag, vsize * stride);
87 size_t pagenum = 0;
88 while(vsize > 0) {
89 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
90 size_t bytes = count * stride;
91 const unsigned char* content = v.get_page_buffer(pagenum++);
92 emerg_write_bytes(handle, content, bytes);
93 vsize -= count;
96 uint64_t append_number(char* ptr, uint64_t n)
98 unsigned digits = 0;
99 uint64_t n2 = n;
100 do {
101 digits++;
102 n2 /= 10;
103 } while(n2);
104 for(unsigned i = digits; i > 0; i--) {
105 ptr[i - 1] = (n % 10) + '0';
106 n /= 10;
108 ptr[digits] = 0;
109 return digits;
111 template<typename T>
112 uint64_t map_index(const std::map<std::string, T>& b, const std::string& n)
114 uint64_t idx = 0;
115 for(auto& i : b) {
116 if(i.first == n)
117 return idx;
118 idx++;
120 return 0xFFFFFFFFFFFFFFFFULL;
124 void emerg_save_movie(const moviefile& mv, rrdata_set& rrd)
126 //Whee, assume state of the emulator is totally busted.
127 if(!mv.gametype)
128 return; //No valid movie. Trying to save would segfault.
129 char header[] = {'l', 's', 'm', 'v', '\x1a'};
130 int fd;
131 char filename_buf[512];
132 int number = 1;
133 name_again:
134 filename_buf[0] = 0;
135 strcpy(filename_buf + strlen(filename_buf), "crashsave-");
136 append_number(filename_buf + strlen(filename_buf), time(NULL));
137 strcpy(filename_buf + strlen(filename_buf), "-");
138 append_number(filename_buf + strlen(filename_buf), number++);
139 strcpy(filename_buf + strlen(filename_buf), ".lsmv");
140 fd = open(filename_buf, O_WRONLY | O_CREAT | O_EXCL | EXTRA_OPENFLAGS, 0666);
141 if(fd < 0 && errno == EEXIST) goto name_again;
142 if(fd < 0) return; //Can't open.
143 //Headers.
144 emerg_write_bytes(fd, (const uint8_t*)header, sizeof(header));
145 emerg_write_string(fd, mv.gametype->get_name());
146 for(auto& i : mv.settings) {
147 emerg_write_byte(fd, 1);
148 emerg_write_string(fd, i.first);
149 emerg_write_string(fd, i.second);
151 emerg_write_byte(fd, 0);
152 //The actual movie.
153 for(auto& i : mv.branches) {
154 emerg_write_member(fd, TAG_BRANCH_NAME, i.first.length());
155 emerg_write_string_implicit(fd, i.first);
156 emerg_write_movie(fd, i.second, (&i.second == mv.input) ? TAG_MOVIE : TAG_BRANCH);
158 //Movie starting time.
159 emerg_write_member(fd, TAG_MOVIE_TIME, number_size(mv.movie_rtc_second) +
160 number_size(mv.movie_rtc_subsecond));
161 emerg_write_number(fd, mv.movie_rtc_second);
162 emerg_write_number(fd, mv.movie_rtc_subsecond);
163 //Project id.
164 emerg_write_member(fd, TAG_PROJECT_ID, mv.projectid.length());
165 emerg_write_string_implicit(fd, mv.projectid);
166 //starting SRAM.
167 for(auto& i : mv.movie_sram) {
168 emerg_write_member(fd, TAG_MOVIE_SRAM, string_size(i.first) + i.second.size());
169 emerg_write_string(fd, i.first);
170 emerg_write_blob_implicit(fd, i.second);
172 //Anchor save.
173 emerg_write_member(fd, TAG_ANCHOR_SAVE, mv.anchor_savestate.size());
174 emerg_write_blob_implicit(fd, mv.anchor_savestate);
175 //RRDATA.
176 emerg_write_member(fd, TAG_RRDATA, rrd.size_emerg());
177 rrdata_set::esave_state estate;
178 while(true) {
179 char buf[4096];
180 size_t w = rrd.write_emerg(estate, buf, sizeof(buf));
181 if(!w) break;
182 emerg_write_bytes(fd, (const uint8_t*)buf, w);
184 //Core version.
185 emerg_write_member(fd, TAG_CORE_VERSION, mv.coreversion.length());
186 emerg_write_string_implicit(fd, mv.coreversion);
187 //ROM slots data.
188 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
189 if(mv.romimg_sha256[i].length()) {
190 emerg_write_member(fd, TAG_ROMHASH, mv.romimg_sha256[i].length() + 1);
191 emerg_write_byte(fd, 2 * i);
192 emerg_write_string_implicit(fd, mv.romimg_sha256[i]);
194 if(mv.romxml_sha256[i].length()) {
195 emerg_write_member(fd, TAG_ROMHASH, mv.romxml_sha256[i].length() + 1);
196 emerg_write_byte(fd, 2 * i + 1);
197 emerg_write_string_implicit(fd, mv.romxml_sha256[i]);
199 if(mv.namehint[i].length()) {
200 emerg_write_member(fd, TAG_ROMHINT, mv.namehint[i].length() + 1);
201 emerg_write_byte(fd, i);
202 emerg_write_string_implicit(fd, mv.namehint[i]);
205 //Game name.
206 emerg_write_member(fd, TAG_GAMENAME, mv.gamename.size());
207 emerg_write_string_implicit(fd, mv.gamename);
208 //Subtitles.
209 for(auto& i : mv.subtitles) {
210 emerg_write_member(fd, TAG_SUBTITLE, number_size(i.first.get_frame()) +
211 number_size(i.first.get_length()) + i.second.length());
212 emerg_write_number(fd, i.first.get_frame());
213 emerg_write_number(fd, i.first.get_length());
214 emerg_write_string_implicit(fd, i.second);
216 //Authors.
217 for(auto& i : mv.authors) {
218 emerg_write_member(fd, TAG_AUTHOR, string_size(i.first) + i.second.size());
219 emerg_write_string(fd, i.first);
220 emerg_write_string_implicit(fd, i.second);
223 //RAM contents.
224 for(auto& i : mv.ramcontent) {
225 emerg_write_member(fd, TAG_RAMCONTENT, string_size(i.first) + i.second.size());
226 emerg_write_string(fd, i.first);
227 emerg_write_blob_implicit(fd, i.second);
229 close(fd);