Fix SA1 open bus
[lsnes.git] / src / core / moviefile-esave.cpp
blobb3f662b5f82f052ce461a49079e60ce2685834b5
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 //FUCK YOU. SERIOUSLY.
11 #define EXTRA_OPENFLAGS O_BINARY
12 #else
13 #define EXTRA_OPENFLAGS 0
14 #endif
16 namespace
18 void emerg_write_bytes(int handle, const uint8_t* d, size_t dsize)
20 while(dsize > 0) {
21 ssize_t r = write(handle, d, dsize);
22 if(r > 0) {
23 d += r;
24 dsize -= r;
28 void emerg_write_number(int handle, uint64_t num)
30 uint8_t data[10];
31 size_t len = 0;
32 do {
33 bool cont = (num > 127);
34 data[len++] = (cont ? 0x80 : 0x00) | (num & 0x7F);
35 num >>= 7;
36 } while(num);
37 emerg_write_bytes(handle, data, len);
39 size_t number_size(uint64_t num)
41 unsigned len = 0;
42 do {
43 num >>= 7;
44 len++;
45 } while(num);
46 return len;
48 void emerg_write_number32(int handle, uint32_t num)
50 char buf[4];
51 serialization::u32b(buf, num);
52 emerg_write_bytes(handle, (const uint8_t*)buf, 4);
54 void emerg_write_member(int handle, uint32_t tag, uint64_t size)
56 emerg_write_number32(handle, 0xaddb2d86);
57 emerg_write_number32(handle, tag);
58 emerg_write_number(handle, size);
60 void emerg_write_blob_implicit(int handle, const std::vector<char>& v)
62 emerg_write_bytes(handle, (const uint8_t*)&v[0], v.size());
64 void emerg_write_byte(int handle, uint8_t byte)
66 emerg_write_bytes(handle, &byte, 1);
68 size_t string_size(const std::string& str)
70 return number_size(str.length()) + str.length();
72 void emerg_write_string_implicit(int handle, const std::string& str)
74 for(size_t i = 0; i < str.length(); i++)
75 emerg_write_byte(handle, str[i]);
77 void emerg_write_string(int handle, const std::string& str)
79 emerg_write_number(handle, str.length());
80 emerg_write_string_implicit(handle, str);
82 void emerg_write_movie(int handle, const controller_frame_vector& v, uint32_t tag)
84 uint64_t stride = v.get_stride();
85 uint64_t pageframes = v.get_frames_per_page();
86 uint64_t vsize = v.size();
87 emerg_write_member(handle, tag, vsize * stride);
88 size_t pagenum = 0;
89 while(vsize > 0) {
90 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
91 size_t bytes = count * stride;
92 const unsigned char* content = v.get_page_buffer(pagenum++);
93 emerg_write_bytes(handle, content, bytes);
94 vsize -= count;
97 uint64_t append_number(char* ptr, uint64_t n)
99 unsigned digits = 0;
100 uint64_t n2 = n;
101 do {
102 digits++;
103 n2 /= 10;
104 } while(n2);
105 for(unsigned i = digits; i > 0; i--) {
106 ptr[i - 1] = (n % 10) + '0';
107 n /= 10;
109 ptr[digits] = 0;
110 return digits;
112 template<typename T>
113 uint64_t map_index(const std::map<std::string, T>& b, const std::string& n)
115 uint64_t idx = 0;
116 for(auto& i : b) {
117 if(i.first == n)
118 return idx;
119 idx++;
121 return 0xFFFFFFFFFFFFFFFFULL;
125 void emerg_save_movie(const moviefile& mv, rrdata_set& rrd)
127 //Whee, assume state of the emulator is totally busted.
128 if(!mv.gametype)
129 return; //No valid movie. Trying to save would segfault.
130 char header[] = {'l', 's', 'm', 'v', '\x1a'};
131 int fd;
132 char filename_buf[512];
133 int number = 1;
134 name_again:
135 filename_buf[0] = 0;
136 strcpy(filename_buf + strlen(filename_buf), "crashsave-");
137 append_number(filename_buf + strlen(filename_buf), time(NULL));
138 strcpy(filename_buf + strlen(filename_buf), "-");
139 append_number(filename_buf + strlen(filename_buf), number++);
140 strcpy(filename_buf + strlen(filename_buf), ".lsmv");
141 fd = open(filename_buf, O_WRONLY | O_CREAT | O_EXCL | EXTRA_OPENFLAGS, 0666);
142 if(fd < 0 && errno == EEXIST) goto name_again;
143 if(fd < 0) return; //Can't open.
144 //Headers.
145 emerg_write_bytes(fd, (const uint8_t*)header, sizeof(header));
146 emerg_write_string(fd, mv.gametype->get_name());
147 for(auto& i : mv.settings) {
148 emerg_write_byte(fd, 1);
149 emerg_write_string(fd, i.first);
150 emerg_write_string(fd, i.second);
152 emerg_write_byte(fd, 0);
153 //The actual movie.
154 for(auto& i : mv.branches) {
155 emerg_write_member(fd, TAG_BRANCH_NAME, i.first.length());
156 emerg_write_string_implicit(fd, i.first);
157 emerg_write_movie(fd, i.second, (&i.second == mv.input) ? TAG_MOVIE : TAG_BRANCH);
159 //Movie starting time.
160 emerg_write_member(fd, TAG_MOVIE_TIME, number_size(mv.movie_rtc_second) +
161 number_size(mv.movie_rtc_subsecond));
162 emerg_write_number(fd, mv.movie_rtc_second);
163 emerg_write_number(fd, mv.movie_rtc_subsecond);
164 //Project id.
165 emerg_write_member(fd, TAG_PROJECT_ID, mv.projectid.length());
166 emerg_write_string_implicit(fd, mv.projectid);
167 //starting SRAM.
168 for(auto& i : mv.movie_sram) {
169 emerg_write_member(fd, TAG_MOVIE_SRAM, string_size(i.first) + i.second.size());
170 emerg_write_string(fd, i.first);
171 emerg_write_blob_implicit(fd, i.second);
173 //Anchor save.
174 emerg_write_member(fd, TAG_ANCHOR_SAVE, mv.anchor_savestate.size());
175 emerg_write_blob_implicit(fd, mv.anchor_savestate);
176 //RRDATA.
177 emerg_write_member(fd, TAG_RRDATA, rrd.size_emerg());
178 rrdata_set::esave_state estate;
179 while(true) {
180 char buf[4096];
181 size_t w = rrd.write_emerg(estate, buf, sizeof(buf));
182 if(!w) break;
183 emerg_write_bytes(fd, (const uint8_t*)buf, w);
185 //Core version.
186 emerg_write_member(fd, TAG_CORE_VERSION, mv.coreversion.length());
187 emerg_write_string_implicit(fd, mv.coreversion);
188 //ROM slots data.
189 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
190 if(mv.romimg_sha256[i].length()) {
191 emerg_write_member(fd, TAG_ROMHASH, mv.romimg_sha256[i].length() + 1);
192 emerg_write_byte(fd, 2 * i);
193 emerg_write_string_implicit(fd, mv.romimg_sha256[i]);
195 if(mv.romxml_sha256[i].length()) {
196 emerg_write_member(fd, TAG_ROMHASH, mv.romxml_sha256[i].length() + 1);
197 emerg_write_byte(fd, 2 * i + 1);
198 emerg_write_string_implicit(fd, mv.romxml_sha256[i]);
200 if(mv.namehint[i].length()) {
201 emerg_write_member(fd, TAG_ROMHINT, mv.namehint[i].length() + 1);
202 emerg_write_byte(fd, i);
203 emerg_write_string_implicit(fd, mv.namehint[i]);
206 //Game name.
207 emerg_write_member(fd, TAG_GAMENAME, mv.gamename.size());
208 emerg_write_string_implicit(fd, mv.gamename);
209 //Subtitles.
210 for(auto& i : mv.subtitles) {
211 emerg_write_member(fd, TAG_SUBTITLE, number_size(i.first.get_frame()) +
212 number_size(i.first.get_length()) + i.second.length());
213 emerg_write_number(fd, i.first.get_frame());
214 emerg_write_number(fd, i.first.get_length());
215 emerg_write_string_implicit(fd, i.second);
217 //Authors.
218 for(auto& i : mv.authors) {
219 emerg_write_member(fd, TAG_AUTHOR, string_size(i.first) + i.second.size());
220 emerg_write_string(fd, i.first);
221 emerg_write_string_implicit(fd, i.second);
224 //RAM contents.
225 for(auto& i : mv.ramcontent) {
226 emerg_write_member(fd, TAG_RAMCONTENT, string_size(i.first) + i.second.size());
227 emerg_write_string(fd, i.first);
228 emerg_write_blob_implicit(fd, i.second);
230 close(fd);