moviefile.cpp is friggin' large, split it up
[lsnes.git] / src / core / moviefile-esave.cpp
blob4ae5cd5a44a9cd742a800314804ba6997366dd34
1 #include "core/moviefile.hpp"
2 #include "core/moviefile-binary.hpp"
3 #include "library/serialization.hpp"
4 #include "interface/romtype.hpp"
6 #include <fcntl.h>
7 #include <unistd.h>
8 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
9 #include <windows.h>
10 #endif
12 namespace
14 void emerg_write_bytes(int handle, const uint8_t* d, size_t dsize)
16 while(dsize > 0) {
17 ssize_t r = write(handle, d, dsize);
18 if(r > 0) {
19 d += r;
20 dsize -= r;
24 void emerg_write_number(int handle, uint64_t num)
26 uint8_t data[10];
27 size_t len = 0;
28 do {
29 bool cont = (num > 127);
30 data[len++] = (cont ? 0x80 : 0x00) | (num & 0x7F);
31 num >>= 7;
32 } while(num);
33 emerg_write_bytes(handle, data, len);
35 size_t number_size(uint64_t num)
37 unsigned len = 0;
38 do {
39 num >>= 7;
40 len++;
41 } while(num);
42 return len;
44 void emerg_write_number32(int handle, uint32_t num)
46 char buf[4];
47 serialization::u32b(buf, num);
48 emerg_write_bytes(handle, (const uint8_t*)buf, 4);
50 void emerg_write_member(int handle, uint32_t tag, uint64_t size)
52 emerg_write_number32(handle, 0xaddb2d86);
53 emerg_write_number32(handle, tag);
54 emerg_write_number(handle, size);
56 void emerg_write_blob_implicit(int handle, const std::vector<char>& v)
58 emerg_write_bytes(handle, (const uint8_t*)&v[0], v.size());
60 void emerg_write_byte(int handle, uint8_t byte)
62 emerg_write_bytes(handle, &byte, 1);
64 size_t string_size(const std::string& str)
66 return number_size(str.length()) + str.length();
68 void emerg_write_string_implicit(int handle, const std::string& str)
70 for(size_t i = 0; i < str.length(); i++)
71 emerg_write_byte(handle, str[i]);
73 void emerg_write_string(int handle, const std::string& str)
75 emerg_write_number(handle, str.length());
76 emerg_write_string_implicit(handle, str);
78 void emerg_write_movie(int handle, const controller_frame_vector& v, uint32_t tag)
80 uint64_t pages = v.get_page_count();
81 uint64_t stride = v.get_stride();
82 uint64_t pageframes = v.get_frames_per_page();
83 uint64_t vsize = v.size();
84 emerg_write_member(handle, tag, vsize * stride);
85 size_t pagenum = 0;
86 while(vsize > 0) {
87 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
88 size_t bytes = count * stride;
89 const unsigned char* content = v.get_page_buffer(pagenum++);
90 emerg_write_bytes(handle, content, bytes);
91 vsize -= count;
94 uint64_t append_number(char* ptr, uint64_t n)
96 unsigned digits = 0;
97 uint64_t n2 = n;
98 do {
99 digits++;
100 n2 /= 10;
101 } while(n2);
102 for(unsigned i = digits; i > 0; i--) {
103 ptr[i - 1] = (n % 10) + '0';
104 n /= 10;
106 ptr[digits] = 0;
108 template<typename T>
109 uint64_t map_index(const std::map<std::string, T>& b, const std::string& n)
111 uint64_t idx = 0;
112 for(auto& i : b) {
113 if(i.first == n)
114 return idx;
115 idx++;
117 return 0xFFFFFFFFFFFFFFFFULL;
121 void emerg_save_movie(const moviefile& mv, rrdata_set& rrd)
123 //Whee, assume state of the emulator is totally busted.
124 if(!mv.gametype)
125 return; //No valid movie. Trying to save would segfault.
126 char header[] = {'l', 's', 'm', 'v', '\x1a'};
127 int fd;
128 char filename_buf[512];
129 int number = 1;
130 name_again:
131 filename_buf[0] = 0;
132 strcpy(filename_buf + strlen(filename_buf), "crashsave-");
133 append_number(filename_buf + strlen(filename_buf), time(NULL));
134 strcpy(filename_buf + strlen(filename_buf), "-");
135 append_number(filename_buf + strlen(filename_buf), number++);
136 strcpy(filename_buf + strlen(filename_buf), ".lsmv");
137 fd = open(filename_buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
138 if(fd < 0 && errno == EEXIST) goto name_again;
139 if(fd < 0) return; //Can't open.
140 //Headers.
141 emerg_write_bytes(fd, (const uint8_t*)header, sizeof(header));
142 emerg_write_string(fd, mv.gametype->get_name());
143 for(auto& i : mv.settings) {
144 emerg_write_byte(fd, 1);
145 emerg_write_string(fd, i.first);
146 emerg_write_string(fd, i.second);
148 emerg_write_byte(fd, 0);
149 //The actual movie.
150 for(auto& i : mv.branches) {
151 emerg_write_member(fd, TAG_BRANCH_NAME, i.first.length());
152 emerg_write_string_implicit(fd, i.first);
153 emerg_write_movie(fd, i.second, (&i.second == mv.input) ? TAG_MOVIE : TAG_BRANCH);
155 //Movie starting time.
156 emerg_write_member(fd, TAG_MOVIE_TIME, number_size(mv.movie_rtc_second) +
157 number_size(mv.movie_rtc_subsecond));
158 emerg_write_number(fd, mv.movie_rtc_second);
159 emerg_write_number(fd, mv.movie_rtc_subsecond);
160 //Project id.
161 emerg_write_member(fd, TAG_PROJECT_ID, mv.projectid.length());
162 emerg_write_string_implicit(fd, mv.projectid);
163 //starting SRAM.
164 for(auto& i : mv.movie_sram) {
165 emerg_write_member(fd, TAG_MOVIE_SRAM, string_size(i.first) + i.second.size());
166 emerg_write_string(fd, i.first);
167 emerg_write_blob_implicit(fd, i.second);
169 //Anchor save.
170 emerg_write_member(fd, TAG_ANCHOR_SAVE, mv.anchor_savestate.size());
171 emerg_write_blob_implicit(fd, mv.anchor_savestate);
172 //RRDATA.
173 emerg_write_member(fd, TAG_RRDATA, rrd.size_emerg());
174 rrdata_set::esave_state estate;
175 while(true) {
176 char buf[4096];
177 size_t w = rrd.write_emerg(estate, buf, sizeof(buf));
178 if(!w) break;
179 emerg_write_bytes(fd, (const uint8_t*)buf, w);
181 //Core version.
182 emerg_write_member(fd, TAG_CORE_VERSION, mv.coreversion.length());
183 emerg_write_string_implicit(fd, mv.coreversion);
184 //ROM slots data.
185 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
186 if(mv.romimg_sha256[i].length()) {
187 emerg_write_member(fd, TAG_ROMHASH, mv.romimg_sha256[i].length() + 1);
188 emerg_write_byte(fd, 2 * i);
189 emerg_write_string_implicit(fd, mv.romimg_sha256[i]);
191 if(mv.romxml_sha256[i].length()) {
192 emerg_write_member(fd, TAG_ROMHASH, mv.romxml_sha256[i].length() + 1);
193 emerg_write_byte(fd, 2 * i + 1);
194 emerg_write_string_implicit(fd, mv.romxml_sha256[i]);
196 if(mv.namehint[i].length()) {
197 emerg_write_member(fd, TAG_ROMHINT, mv.namehint[i].length() + 1);
198 emerg_write_byte(fd, i);
199 emerg_write_string_implicit(fd, mv.namehint[i]);
202 //Game name.
203 emerg_write_member(fd, TAG_GAMENAME, mv.gamename.size());
204 emerg_write_string_implicit(fd, mv.gamename);
205 //Subtitles.
206 for(auto& i : mv.subtitles) {
207 emerg_write_member(fd, TAG_SUBTITLE, number_size(i.first.get_frame()) +
208 number_size(i.first.get_length()) + i.second.length());
209 emerg_write_number(fd, i.first.get_frame());
210 emerg_write_number(fd, i.first.get_length());
211 emerg_write_string_implicit(fd, i.second);
213 //Authors.
214 for(auto& i : mv.authors) {
215 emerg_write_member(fd, TAG_AUTHOR, string_size(i.first) + i.second.size());
216 emerg_write_string(fd, i.first);
217 emerg_write_string_implicit(fd, i.second);
220 //RAM contents.
221 for(auto& i : mv.ramcontent) {
222 emerg_write_member(fd, TAG_RAMCONTENT, string_size(i.first) + i.second.size());
223 emerg_write_string(fd, i.first);
224 emerg_write_blob_implicit(fd, i.second);
226 close(fd);