src/gba/savedata.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/gba/savedata.h>
7
8#include <mgba/internal/arm/macros.h>
9#include <mgba/internal/gba/gba.h>
10#include <mgba/internal/gba/serialize.h>
11
12#include <mgba-util/memory.h>
13#include <mgba-util/vfs.h>
14
15#include <errno.h>
16#include <fcntl.h>
17
18// Some testing was done here...
19// Erase cycles can vary greatly.
20// Some games may vary anywhere between about 2000 cycles to up to 30000 cycles. (Observed on a Macronix (09C2) chip).
21// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip).
22// An average estimation is as follows.
23#define FLASH_ERASE_CYCLES 30000
24#define FLASH_PROGRAM_CYCLES 18000
25// This needs real testing, and is only an estimation currently
26#define EEPROM_SETTLE_CYCLES 1450
27#define CLEANUP_THRESHOLD 15
28
29mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");
30
31static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
32static void _flashErase(struct GBASavedata* savedata);
33static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
34
35void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
36 savedata->type = SAVEDATA_AUTODETECT;
37 savedata->data = 0;
38 savedata->command = EEPROM_COMMAND_NULL;
39 savedata->flashState = FLASH_STATE_RAW;
40 savedata->vf = vf;
41 savedata->realVf = vf;
42 savedata->mapMode = MAP_WRITE;
43 savedata->dirty = 0;
44 savedata->dirtAge = 0;
45}
46
47void GBASavedataDeinit(struct GBASavedata* savedata) {
48 if (savedata->vf) {
49 size_t size = GBASavedataSize(savedata);
50 if (savedata->data) {
51 savedata->vf->unmap(savedata->vf, savedata->data, size);
52 }
53 savedata->vf = NULL;
54 } else {
55 switch (savedata->type) {
56 case SAVEDATA_SRAM:
57 mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
58 break;
59 case SAVEDATA_FLASH512:
60 mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
61 break;
62 case SAVEDATA_FLASH1M:
63 mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
64 break;
65 case SAVEDATA_EEPROM:
66 mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
67 break;
68 case SAVEDATA_FORCE_NONE:
69 case SAVEDATA_AUTODETECT:
70 break;
71 }
72 }
73 savedata->data = 0;
74 savedata->type = SAVEDATA_AUTODETECT;
75}
76
77void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback) {
78 enum SavedataType type = savedata->type;
79 GBASavedataDeinit(savedata);
80 savedata->vf = vf;
81 savedata->mapMode = MAP_READ;
82 savedata->maskWriteback = writeback;
83 GBASavedataForceType(savedata, type, savedata->realisticTiming);
84}
85
86void GBASavedataUnmask(struct GBASavedata* savedata) {
87 if (savedata->vf == savedata->realVf) {
88 return;
89 }
90 enum SavedataType type = savedata->type;
91 struct VFile* vf = savedata->vf;
92 GBASavedataDeinit(savedata);
93 savedata->vf = savedata->realVf;
94 savedata->mapMode = MAP_WRITE;
95 GBASavedataForceType(savedata, type, savedata->realisticTiming);
96 if (savedata->maskWriteback) {
97 GBASavedataLoad(savedata, vf);
98 savedata->maskWriteback = false;
99 }
100 vf->close(vf);
101}
102
103bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
104 if (savedata->data) {
105 switch (savedata->type) {
106 case SAVEDATA_SRAM:
107 return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
108 case SAVEDATA_FLASH512:
109 return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
110 case SAVEDATA_FLASH1M:
111 return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
112 case SAVEDATA_EEPROM:
113 return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
114 case SAVEDATA_AUTODETECT:
115 case SAVEDATA_FORCE_NONE:
116 return true;
117 }
118 } else if (savedata->vf) {
119 off_t read = 0;
120 uint8_t buffer[2048];
121 do {
122 read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
123 out->write(out, buffer, read);
124 } while (read == sizeof(buffer));
125 return read >= 0;
126 }
127 return true;
128}
129
130size_t GBASavedataSize(struct GBASavedata* savedata) {
131 switch (savedata->type) {
132 case SAVEDATA_SRAM:
133 return SIZE_CART_SRAM;
134 case SAVEDATA_FLASH512:
135 return SIZE_CART_FLASH512;
136 case SAVEDATA_FLASH1M:
137 return SIZE_CART_FLASH1M;
138 case SAVEDATA_EEPROM:
139 return SIZE_CART_EEPROM;
140 case SAVEDATA_FORCE_NONE:
141 return 0;
142 case SAVEDATA_AUTODETECT:
143 default:
144 if (savedata->vf) {
145 return savedata->vf->size(savedata->vf);
146 }
147 return 0;
148 }
149}
150
151bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) {
152 if (savedata->vf) {
153 off_t read = 0;
154 uint8_t buffer[2048];
155 memset(buffer, 0xFF, sizeof(buffer));
156 savedata->vf->seek(savedata->vf, 0, SEEK_SET);
157 while (savedata->vf->seek(savedata->vf, 0, SEEK_CUR) < savedata->vf->size(savedata->vf)) {
158 savedata->vf->write(savedata->vf, buffer, sizeof(buffer));
159 }
160 savedata->vf->seek(savedata->vf, 0, SEEK_SET);
161 if (in) {
162 do {
163 read = in->read(in, buffer, sizeof(buffer));
164 read = savedata->vf->write(savedata->vf, buffer, read);
165 } while (read == sizeof(buffer));
166 }
167 return read >= 0;
168 } else if (savedata->data) {
169 if (!in && savedata->type != SAVEDATA_FORCE_NONE) {
170 return false;
171 }
172 switch (savedata->type) {
173 case SAVEDATA_SRAM:
174 return in->read(in, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
175 case SAVEDATA_FLASH512:
176 return in->read(in, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
177 case SAVEDATA_FLASH1M:
178 return in->read(in, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
179 case SAVEDATA_EEPROM:
180 return in->read(in, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
181 case SAVEDATA_AUTODETECT:
182 case SAVEDATA_FORCE_NONE:
183 return true;
184 }
185 }
186 return true;
187}
188
189void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming) {
190 if (savedata->type != SAVEDATA_AUTODETECT) {
191 struct VFile* vf = savedata->vf;
192 GBASavedataDeinit(savedata);
193 GBASavedataInit(savedata, vf);
194 }
195 switch (type) {
196 case SAVEDATA_FLASH512:
197 case SAVEDATA_FLASH1M:
198 savedata->type = type;
199 GBASavedataInitFlash(savedata, realisticTiming);
200 break;
201 case SAVEDATA_EEPROM:
202 GBASavedataInitEEPROM(savedata, realisticTiming);
203 break;
204 case SAVEDATA_SRAM:
205 GBASavedataInitSRAM(savedata);
206 break;
207 case SAVEDATA_FORCE_NONE:
208 savedata->type = SAVEDATA_FORCE_NONE;
209 break;
210 case SAVEDATA_AUTODETECT:
211 break;
212 }
213}
214
215void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
216 if (savedata->type == SAVEDATA_AUTODETECT) {
217 savedata->type = SAVEDATA_FLASH512;
218 }
219 if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
220 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
221 return;
222 }
223 int32_t flashSize = SIZE_CART_FLASH512;
224 if (savedata->type == SAVEDATA_FLASH1M) {
225 flashSize = SIZE_CART_FLASH1M;
226 }
227 off_t end;
228 if (!savedata->vf) {
229 end = 0;
230 savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
231 } else {
232 end = savedata->vf->size(savedata->vf);
233 if (end < flashSize) {
234 savedata->vf->truncate(savedata->vf, flashSize);
235 }
236 savedata->data = savedata->vf->map(savedata->vf, flashSize, savedata->mapMode);
237 }
238
239 savedata->currentBank = savedata->data;
240 savedata->dust = 0;
241 savedata->realisticTiming = realisticTiming;
242 if (end < SIZE_CART_FLASH512) {
243 memset(&savedata->data[end], 0xFF, flashSize - end);
244 }
245}
246
247void GBASavedataInitEEPROM(struct GBASavedata* savedata, bool realisticTiming) {
248 if (savedata->type == SAVEDATA_AUTODETECT) {
249 savedata->type = SAVEDATA_EEPROM;
250 } else {
251 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
252 return;
253 }
254 off_t end;
255 if (!savedata->vf) {
256 end = 0;
257 savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
258 } else {
259 end = savedata->vf->size(savedata->vf);
260 if (end < SIZE_CART_EEPROM) {
261 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
262 }
263 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
264 }
265 savedata->dust = 0;
266 savedata->realisticTiming = realisticTiming;
267 if (end < SIZE_CART_EEPROM) {
268 memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
269 }
270}
271
272void GBASavedataInitSRAM(struct GBASavedata* savedata) {
273 if (savedata->type == SAVEDATA_AUTODETECT) {
274 savedata->type = SAVEDATA_SRAM;
275 } else {
276 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
277 return;
278 }
279 off_t end;
280 if (!savedata->vf) {
281 end = 0;
282 savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
283 } else {
284 end = savedata->vf->size(savedata->vf);
285 if (end < SIZE_CART_SRAM) {
286 savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
287 }
288 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
289 }
290
291 if (end < SIZE_CART_SRAM) {
292 memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
293 }
294}
295
296uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
297 if (savedata->command == FLASH_COMMAND_ID) {
298 if (savedata->type == SAVEDATA_FLASH512) {
299 if (address < 2) {
300 return FLASH_MFG_PANASONIC >> (address * 8);
301 }
302 } else if (savedata->type == SAVEDATA_FLASH1M) {
303 if (address < 2) {
304 return FLASH_MFG_SANYO >> (address * 8);
305 }
306 }
307 }
308 if (savedata->dust > 0 && (address >> 12) == savedata->settling) {
309 // Give some overhead for waitstates and the comparison
310 // This estimation can probably be improved
311 savedata->dust -= 5000;
312 return 0x5F;
313 }
314 return savedata->currentBank[address];
315}
316
317void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
318 switch (savedata->flashState) {
319 case FLASH_STATE_RAW:
320 switch (savedata->command) {
321 case FLASH_COMMAND_PROGRAM:
322 savedata->dirty |= SAVEDATA_DIRT_NEW;
323 savedata->currentBank[address] = value;
324 savedata->command = FLASH_COMMAND_NONE;
325 if (savedata->realisticTiming) {
326 savedata->dust = FLASH_PROGRAM_CYCLES;
327 }
328 break;
329 case FLASH_COMMAND_SWITCH_BANK:
330 if (address == 0 && value < 2) {
331 _flashSwitchBank(savedata, value);
332 } else {
333 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash bank switch");
334 savedata->command = FLASH_COMMAND_NONE;
335 }
336 savedata->command = FLASH_COMMAND_NONE;
337 break;
338 default:
339 if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
340 savedata->flashState = FLASH_STATE_START;
341 } else {
342 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
343 }
344 break;
345 }
346 break;
347 case FLASH_STATE_START:
348 if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
349 savedata->flashState = FLASH_STATE_CONTINUE;
350 } else {
351 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
352 savedata->flashState = FLASH_STATE_RAW;
353 }
354 break;
355 case FLASH_STATE_CONTINUE:
356 savedata->flashState = FLASH_STATE_RAW;
357 if (address == FLASH_BASE_HI) {
358 switch (savedata->command) {
359 case FLASH_COMMAND_NONE:
360 switch (value) {
361 case FLASH_COMMAND_ERASE:
362 case FLASH_COMMAND_ID:
363 case FLASH_COMMAND_PROGRAM:
364 case FLASH_COMMAND_SWITCH_BANK:
365 savedata->command = value;
366 break;
367 default:
368 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash operation: %#02x", value);
369 break;
370 }
371 break;
372 case FLASH_COMMAND_ERASE:
373 switch (value) {
374 case FLASH_COMMAND_ERASE_CHIP:
375 _flashErase(savedata);
376 break;
377 default:
378 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
379 break;
380 }
381 savedata->command = FLASH_COMMAND_NONE;
382 break;
383 case FLASH_COMMAND_ID:
384 if (value == FLASH_COMMAND_TERMINATE) {
385 savedata->command = FLASH_COMMAND_NONE;
386 }
387 break;
388 default:
389 mLOG(GBA_SAVE, ERROR, "Flash entered bad state: %#02x", savedata->command);
390 savedata->command = FLASH_COMMAND_NONE;
391 break;
392 }
393 } else if (savedata->command == FLASH_COMMAND_ERASE) {
394 if (value == FLASH_COMMAND_ERASE_SECTOR) {
395 _flashEraseSector(savedata, address);
396 savedata->command = FLASH_COMMAND_NONE;
397 } else {
398 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
399 }
400 }
401 break;
402 }
403}
404
405void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
406 switch (savedata->command) {
407 // Read header
408 case EEPROM_COMMAND_NULL:
409 default:
410 savedata->command = value & 0x1;
411 break;
412 case EEPROM_COMMAND_PENDING:
413 savedata->command <<= 1;
414 savedata->command |= value & 0x1;
415 if (savedata->command == EEPROM_COMMAND_WRITE) {
416 savedata->writeAddress = 0;
417 } else {
418 savedata->readAddress = 0;
419 }
420 break;
421 // Do commands
422 case EEPROM_COMMAND_WRITE:
423 // Write
424 if (writeSize > 65) {
425 savedata->writeAddress <<= 1;
426 savedata->writeAddress |= (value & 0x1) << 6;
427 } else if (writeSize == 1) {
428 savedata->command = EEPROM_COMMAND_NULL;
429 } else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
430 uint8_t current = savedata->data[savedata->writeAddress >> 3];
431 current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
432 current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
433 savedata->dirty |= SAVEDATA_DIRT_NEW;
434 savedata->data[savedata->writeAddress >> 3] = current;
435 if (savedata->realisticTiming) {
436 savedata->dust = EEPROM_SETTLE_CYCLES;
437 }
438 ++savedata->writeAddress;
439 } else {
440 mLOG(GBA_SAVE, GAME_ERROR, "Writing beyond end of EEPROM: %08X", (savedata->writeAddress >> 3));
441 }
442 break;
443 case EEPROM_COMMAND_READ_PENDING:
444 // Read
445 if (writeSize > 1) {
446 savedata->readAddress <<= 1;
447 if (value & 0x1) {
448 savedata->readAddress |= 0x40;
449 }
450 } else {
451 savedata->readBitsRemaining = 68;
452 savedata->command = EEPROM_COMMAND_READ;
453 }
454 break;
455 }
456}
457
458uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
459 if (savedata->command != EEPROM_COMMAND_READ) {
460 if (!savedata->realisticTiming || savedata->dust <= 0) {
461 return 1;
462 } else {
463 // Give some overhead for waitstates and the comparison
464 // This estimation can probably be improved
465 --savedata->dust;
466 return 0;
467 }
468 }
469 --savedata->readBitsRemaining;
470 if (savedata->readBitsRemaining < 64) {
471 int step = 63 - savedata->readBitsRemaining;
472 uint32_t address = (savedata->readAddress + step) >> 3;
473 if (address >= SIZE_CART_EEPROM) {
474 mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
475 return 0xFF;
476 }
477 uint8_t data = savedata->data[address] >> (0x7 - (step & 0x7));
478 if (!savedata->readBitsRemaining) {
479 savedata->command = EEPROM_COMMAND_NULL;
480 }
481 return data & 0x1;
482 }
483 return 0;
484}
485
486void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
487 if (!savedata->vf) {
488 return;
489 }
490 if (savedata->dirty & SAVEDATA_DIRT_NEW) {
491 savedata->dirtAge = frameCount;
492 savedata->dirty &= ~SAVEDATA_DIRT_NEW;
493 if (!(savedata->dirty & SAVEDATA_DIRT_SEEN)) {
494 savedata->dirty |= SAVEDATA_DIRT_SEEN;
495 }
496 } else if ((savedata->dirty & SAVEDATA_DIRT_SEEN) && frameCount - savedata->dirtAge > CLEANUP_THRESHOLD) {
497 if (savedata->maskWriteback) {
498 GBASavedataUnmask(savedata);
499 }
500 size_t size = GBASavedataSize(savedata);
501 savedata->dirty = 0;
502 if (savedata->data && savedata->vf->sync(savedata->vf, savedata->data, size)) {
503 mLOG(GBA_SAVE, INFO, "Savedata synced");
504 } else {
505 mLOG(GBA_SAVE, INFO, "Savedata failed to sync!");
506 }
507 }
508}
509
510void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
511 state->savedata.type = savedata->type;
512 state->savedata.command = savedata->command;
513 GBASerializedSavedataFlags flags = 0;
514 flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
515 flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]);
516 state->savedata.flags = flags;
517 STORE_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
518 STORE_32(savedata->readAddress, 0, &state->savedata.readAddress);
519 STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
520 STORE_16(savedata->settling, 0, &state->savedata.settlingSector);
521 STORE_16(savedata->dust, 0, &state->savedata.settlingDust);
522}
523
524void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) {
525 if (savedata->type != state->savedata.type) {
526 mLOG(GBA_SAVE, DEBUG, "Switching save types");
527 GBASavedataForceType(savedata, state->savedata.type, savedata->realisticTiming);
528 }
529 savedata->command = state->savedata.command;
530 GBASerializedSavedataFlags flags = state->savedata.flags;
531 savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags);
532 LOAD_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
533 LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress);
534 LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
535 LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
536 LOAD_16(savedata->dust, 0, &state->savedata.settlingDust);
537
538 if (savedata->type == SAVEDATA_FLASH1M) {
539 _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags));
540 }
541}
542
543void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
544 mLOG(GBA_SAVE, DEBUG, "Performing flash bank switch to bank %i", bank);
545 savedata->currentBank = &savedata->data[bank << 16];
546 if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
547 savedata->type = SAVEDATA_FLASH1M;
548 if (savedata->vf) {
549 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
550 memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
551 }
552 }
553}
554
555void _flashErase(struct GBASavedata* savedata) {
556 mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase");
557 savedata->dirty |= SAVEDATA_DIRT_NEW;
558 size_t size = SIZE_CART_FLASH512;
559 if (savedata->type == SAVEDATA_FLASH1M) {
560 size = SIZE_CART_FLASH1M;
561 }
562 memset(savedata->data, 0xFF, size);
563}
564
565void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
566 mLOG(GBA_SAVE, DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
567 savedata->dirty |= SAVEDATA_DIRT_NEW;
568 size_t size = 0x1000;
569 if (savedata->type == SAVEDATA_FLASH1M) {
570 mLOG(GBA_SAVE, DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
571 }
572 savedata->settling = sectorStart >> 12;
573 if (savedata->realisticTiming) {
574 savedata->dust = FLASH_ERASE_CYCLES;
575 }
576 memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
577}