Bug Summary

File:Source/Core/Core/Src/HW/GCMemcard.cpp
Location:line 1207, column 17
Description:The left operand of '!=' is a garbage value

Annotated Source Code

1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "GCMemcard.h"
6#include "ColorUtil.h"
7static void ByteSwap(u8 *valueA, u8 *valueB)
8{
9 u8 tmp = *valueA;
10 *valueA = *valueB;
11 *valueB = tmp;
12}
13
14void decode5A3image(u32* dst, u16* src, int width, int height)
15{
16 for (int y = 0; y < height; y += 4)
17 {
18 for (int x = 0; x < width; x += 4)
19 {
20 for (int iy = 0; iy < 4; iy++, src += 4)
21 {
22 for (int ix = 0; ix < 4; ix++)
23 {
24 u32 RGBA = ColorUtil::Decode5A3(Common::swap16(src[ix]));
25 dst[(y + iy) * width + (x + ix)] = RGBA;
26 }
27 }
28 }
29 }
30}
31
32void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height)
33{
34 for (int y = 0; y < height; y += 4)
35 {
36 for (int x = 0; x < width; x += 8)
37 {
38 for (int iy = 0; iy < 4; iy++, src += 8)
39 {
40 u32 *tdst = dst+(y+iy)*width+x;
41 for (int ix = 0; ix < 8; ix++)
42 {
43 // huh, this seems wrong. CI8, not 5A3, no?
44 tdst[ix] = ColorUtil::Decode5A3(Common::swap16(pal[src[ix]]));
45 }
46 }
47 }
48 }
49}
50
51GCMemcard::GCMemcard(const char *filename, bool forceCreation, bool sjis)
52 : m_valid(false)
53 , m_fileName(filename)
54{
55 File::IOFile mcdFile(m_fileName, "r+b");
56 if (!mcdFile.IsOpen())
57 {
58 if (!forceCreation && !AskYesNoT("\"%s\" does not exist.\n Create a new 16MB Memcard?", filename)MsgAlert(true, QUESTION, "\"%s\" does not exist.\n Create a new 16MB Memcard?"
, filename)
)
59 {
60 return;
61 }
62 Format(forceCreation ? sjis : !AskYesNoT("Format as ascii (NTSC\\PAL)?\nChoose no for sjis (NTSC-J)")MsgAlert(true, QUESTION, "Format as ascii (NTSC\\PAL)?\nChoose no for sjis (NTSC-J)"
)
);
63 return;
64 }
65 else
66 {
67 //This function can be removed once more about hdr is known and we can check for a valid header
68 std::string fileType;
69 SplitPath(filename, NULL__null, NULL__null, &fileType);
70 if (strcasecmp(fileType.c_str(), ".raw") && strcasecmp(fileType.c_str(), ".gcp"))
71 {
72 PanicAlertT("File has the extension \"%s\"\nvalid extensions are (.raw/.gcp)", fileType.c_str())MsgAlert(false, WARNING, "File has the extension \"%s\"\nvalid extensions are (.raw/.gcp)"
, fileType.c_str())
;
73 return;
74 }
75 u32 size = mcdFile.GetSize();
76 if (size < MC_FST_BLOCKS*BLOCK_SIZE)
77 {
78 PanicAlertT("%s failed to load as a memorycard \nfile is not large enough to be a valid memory card file (0x%x bytes)", filename, size)MsgAlert(false, WARNING, "%s failed to load as a memorycard \nfile is not large enough to be a valid memory card file (0x%x bytes)"
, filename, size)
;
79 return;
80 }
81 if (size % BLOCK_SIZE)
82 {
83 PanicAlertT("%s failed to load as a memorycard \n Card file size is invalid (0x%x bytes)", filename, size)MsgAlert(false, WARNING, "%s failed to load as a memorycard \n Card file size is invalid (0x%x bytes)"
, filename, size)
;
84 return;
85 }
86
87 m_sizeMb = (size/BLOCK_SIZE) / MBIT_TO_BLOCKS;
88 switch (m_sizeMb)
89 {
90 case MemCard59Mb:
91 case MemCard123Mb:
92 case MemCard251Mb:
93 case Memcard507Mb:
94 case MemCard1019Mb:
95 case MemCard2043Mb:
96 break;
97 default:
98 PanicAlertT("%s failed to load as a memorycard \n Card size is invalid (0x%x bytes)", filename, size)MsgAlert(false, WARNING, "%s failed to load as a memorycard \n Card size is invalid (0x%x bytes)"
, filename, size)
;
99 return;
100 }
101 }
102
103
104 mcdFile.Seek(0, SEEK_SET0);
105 if (!mcdFile.ReadBytes(&hdr, BLOCK_SIZE))
106 {
107 PanicAlertT("Failed to read header correctly\n(0x0000-0x1FFF)")MsgAlert(false, WARNING, "Failed to read header correctly\n(0x0000-0x1FFF)"
)
;
108 return;
109 }
110 if (m_sizeMb != BE16(hdr.SizeMb)(Common::swap16(hdr.SizeMb)))
111 {
112 PanicAlertT("Memorycard filesize does not match the header size")MsgAlert(false, WARNING, "Memorycard filesize does not match the header size"
)
;
113 return;
114 }
115
116 if (!mcdFile.ReadBytes(&dir, BLOCK_SIZE))
117 {
118 PanicAlertT("Failed to read directory correctly\n(0x2000-0x3FFF)")MsgAlert(false, WARNING, "Failed to read directory correctly\n(0x2000-0x3FFF)"
)
;
119 return;
120 }
121
122 if (!mcdFile.ReadBytes(&dir_backup, BLOCK_SIZE))
123 {
124 PanicAlertT("Failed to read directory backup correctly\n(0x4000-0x5FFF)")MsgAlert(false, WARNING, "Failed to read directory backup correctly\n(0x4000-0x5FFF)"
)
;
125 return;
126 }
127
128 if (!mcdFile.ReadBytes(&bat, BLOCK_SIZE))
129 {
130 PanicAlertT("Failed to read block allocation table correctly\n(0x6000-0x7FFF)")MsgAlert(false, WARNING, "Failed to read block allocation table correctly\n(0x6000-0x7FFF)"
)
;
131 return;
132 }
133
134 if (!mcdFile.ReadBytes(&bat_backup, BLOCK_SIZE))
135 {
136 PanicAlertT("Failed to read block allocation table backup correctly\n(0x8000-0x9FFF)")MsgAlert(false, WARNING, "Failed to read block allocation table backup correctly\n(0x8000-0x9FFF)"
)
;
137 return;
138 }
139
140 u32 csums = TestChecksums();
141
142 if (csums & 0x1)
143 {
144 // header checksum error!
145 // invalid files do not always get here
146 PanicAlertT("Header checksum failed")MsgAlert(false, WARNING, "Header checksum failed");
147 return;
148 }
149
150 if (csums & 0x2) // directory checksum error!
151 {
152 if (csums & 0x4)
153 {
154 // backup is also wrong!
155 PanicAlertT("Directory checksum failed\n and Directory backup checksum failed")MsgAlert(false, WARNING, "Directory checksum failed\n and Directory backup checksum failed"
)
;
156 return;
157 }
158 else
159 {
160 // backup is correct, restore
161 dir = dir_backup;
162 bat = bat_backup;
163
164 // update checksums
165 csums = TestChecksums();
166 }
167 }
168
169 if (csums & 0x8) // BAT checksum error!
170 {
171 if (csums & 0x10)
172 {
173 // backup is also wrong!
174 PanicAlertT("Block Allocation Table checksum failed")MsgAlert(false, WARNING, "Block Allocation Table checksum failed"
)
;
175 return;
176 }
177 else
178 {
179 // backup is correct, restore
180 dir = dir_backup;
181 bat = bat_backup;
182
183 // update checksums
184 csums = TestChecksums();
185 }
186// It seems that the backup having a larger counter doesn't necessarily mean
187// the backup should be copied?
188// }
189//
190// if(BE16(dir_backup.UpdateCounter) > BE16(dir.UpdateCounter)) //check if the backup is newer
191// {
192// dir = dir_backup;
193// bat = bat_backup; // needed?
194 }
195
196 mcdFile.Seek(0xa000, SEEK_SET0);
197
198 maxBlock = (u32)m_sizeMb * MBIT_TO_BLOCKS;
199 mc_data_blocks.reserve(maxBlock - MC_FST_BLOCKS);
200
201 m_valid = true;
202 for (u32 i = MC_FST_BLOCKS; i < maxBlock; ++i)
203 {
204 GCMBlock b;
205 if (mcdFile.ReadBytes(b.block, BLOCK_SIZE))
206 {
207 mc_data_blocks.push_back(b);
208 }
209 else
210 {
211 PanicAlertT("Failed to read block %d of the save data\nMemcard may be truncated\nFilePosition:%llx", i, mcdFile.Tell())MsgAlert(false, WARNING, "Failed to read block %d of the save data\nMemcard may be truncated\nFilePosition:%llx"
, i, mcdFile.Tell())
;
212 m_valid = false;
213 break;
214 }
215 }
216
217 mcdFile.Close();
218
219 initDirBatPointers();
220}
221
222void GCMemcard::initDirBatPointers()
223{
224 if (BE16(dir.UpdateCounter)(Common::swap16(dir.UpdateCounter)) > (BE16(dir_backup.UpdateCounter)(Common::swap16(dir_backup.UpdateCounter))))
225 {
226 CurrentDir = &dir;
227 PreviousDir = &dir_backup;
228 }
229 else
230 {
231 CurrentDir = &dir_backup;
232 PreviousDir = &dir;
233 }
234 if (BE16(bat.UpdateCounter)(Common::swap16(bat.UpdateCounter)) > BE16(bat_backup.UpdateCounter)(Common::swap16(bat_backup.UpdateCounter)))
235 {
236 CurrentBat = &bat;
237 PreviousBat = &bat_backup;
238 }
239 else
240 {
241 CurrentBat = &bat_backup;
242 PreviousBat = &bat;
243 }
244}
245
246bool GCMemcard::IsAsciiEncoding() const
247{
248 return hdr.Encoding == 0;
249}
250
251bool GCMemcard::Save()
252{
253 File::IOFile mcdFile(m_fileName, "wb");
254 mcdFile.Seek(0, SEEK_SET0);
255
256 mcdFile.WriteBytes(&hdr, BLOCK_SIZE);
257 mcdFile.WriteBytes(&dir, BLOCK_SIZE);
258 mcdFile.WriteBytes(&dir_backup, BLOCK_SIZE);
259 mcdFile.WriteBytes(&bat, BLOCK_SIZE);
260 mcdFile.WriteBytes(&bat_backup, BLOCK_SIZE);
261 for (unsigned int i = 0; i < maxBlock - MC_FST_BLOCKS; ++i)
262 {
263 mcdFile.WriteBytes(mc_data_blocks[i].block, BLOCK_SIZE);
264 }
265
266 return mcdFile.Close();
267}
268
269void GCMemcard::calc_checksumsBE(u16 *buf, u32 length, u16 *csum, u16 *inv_csum)
270{
271 *csum = *inv_csum = 0;
272
273 for (u32 i = 0; i < length; ++i)
274 {
275 //weird warnings here
276 *csum += BE16(buf[i])(Common::swap16(buf[i]));
277 *inv_csum += BE16((u16)(buf[i] ^ 0xffff))(Common::swap16((u16)(buf[i] ^ 0xffff)));
278 }
279 *csum = BE16(*csum)(Common::swap16(*csum));
280 *inv_csum = BE16(*inv_csum)(Common::swap16(*inv_csum));
281 if (*csum == 0xffff)
282 {
283 *csum = 0;
284 }
285 if (*inv_csum == 0xffff)
286 {
287 *inv_csum = 0;
288 }
289}
290
291u32 GCMemcard::TestChecksums() const
292{
293 u16 csum=0,
294 csum_inv=0;
295
296 u32 results = 0;
297
298 calc_checksumsBE((u16*)&hdr, 0xFE , &csum, &csum_inv);
299 if ((hdr.Checksum != csum) || (hdr.Checksum_Inv != csum_inv))
300 results |= 1;
301
302 calc_checksumsBE((u16*)&dir, 0xFFE, &csum, &csum_inv);
303 if ((dir.Checksum != csum) || (dir.Checksum_Inv != csum_inv))
304 results |= 2;
305
306 calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum, &csum_inv);
307 if ((dir_backup.Checksum != csum) || (dir_backup.Checksum_Inv != csum_inv))
308 results |= 4;
309
310 calc_checksumsBE((u16*)(((u8*)&bat)+4), 0xFFE, &csum, &csum_inv);
311 if ((bat.Checksum != csum) || (bat.Checksum_Inv != csum_inv))
312 results |= 8;
313
314 calc_checksumsBE((u16*)(((u8*)&bat_backup)+4), 0xFFE, &csum, &csum_inv);
315 if ((bat_backup.Checksum != csum) || (bat_backup.Checksum_Inv != csum_inv))
316 results |= 16;
317
318 return results;
319}
320
321bool GCMemcard::FixChecksums()
322{
323 if (!m_valid)
324 return false;
325
326 calc_checksumsBE((u16*)&hdr, 0xFE, &hdr.Checksum, &hdr.Checksum_Inv);
327 calc_checksumsBE((u16*)&dir, 0xFFE, &dir.Checksum, &dir.Checksum_Inv);
328 calc_checksumsBE((u16*)&dir_backup, 0xFFE, &dir_backup.Checksum, &dir_backup.Checksum_Inv);
329 calc_checksumsBE((u16*)&bat+2, 0xFFE, &bat.Checksum, &bat.Checksum_Inv);
330 calc_checksumsBE((u16*)&bat_backup+2, 0xFFE, &bat_backup.Checksum, &bat_backup.Checksum_Inv);
331
332 return true;
333}
334
335u8 GCMemcard::GetNumFiles() const
336{
337 if (!m_valid)
338 return 0;
339
340 u8 j = 0;
341 for (int i = 0; i < DIRLEN; i++)
342 {
343 if (BE32(CurrentDir->Dir[i].Gamecode)(Common::swap32(CurrentDir->Dir[i].Gamecode))!= 0xFFFFFFFF)
344 j++;
345 }
346 return j;
347}
348
349u8 GCMemcard::GetFileIndex(u8 fileNumber) const
350{
351 if (m_valid)
352 {
353 u8 j = 0;
354 for (u8 i = 0; i < DIRLEN; i++)
355 {
356 if (BE32(CurrentDir->Dir[i].Gamecode)(Common::swap32(CurrentDir->Dir[i].Gamecode))!= 0xFFFFFFFF)
357 {
358 if (j == fileNumber)
359 {
360 return i;
361 }
362 j++;
363 }
364 }
365 }
366 return 0xFF;
367}
368
369u16 GCMemcard::GetFreeBlocks() const
370{
371 if (!m_valid)
372 return 0;
373
374 return BE16(CurrentBat->FreeBlocks)(Common::swap16(CurrentBat->FreeBlocks));
375}
376
377u8 GCMemcard::TitlePresent(DEntry d) const
378{
379 if (!m_valid)
380 return DIRLEN;
381
382 u8 i = 0;
383 while(i < DIRLEN)
384 {
385 if ((BE32(CurrentDir->Dir[i].Gamecode)(Common::swap32(CurrentDir->Dir[i].Gamecode)) == BE32(d.Gamecode)(Common::swap32(d.Gamecode))) &&
386 (!memcmp(CurrentDir->Dir[i].Filename, d.Filename, 32)))
387 {
388 break;
389 }
390 i++;
391 }
392 return i;
393}
394
395bool GCMemcard::GCI_FileName(u8 index, std::string &filename) const
396{
397 if (!m_valid || index > DIRLEN || (BE32(CurrentDir->Dir[index].Gamecode)(Common::swap32(CurrentDir->Dir[index].Gamecode)) == 0xFFFFFFFF))
398 return false;
399
400 filename = std::string((char*)CurrentDir->Dir[index].Gamecode, 4) + '_' + (char*)CurrentDir->Dir[index].Filename + ".gci";
401 return true;
402}
403
404// DEntry functions, all take u8 index < DIRLEN (127)
405// Functions that have ascii output take a char *buffer
406
407std::string GCMemcard::DEntry_GameCode(u8 index) const
408{
409 if (!m_valid || index > DIRLEN)
410 return "";
411
412 return std::string((const char*)CurrentDir->Dir[index].Gamecode, 4);
413}
414
415std::string GCMemcard::DEntry_Makercode(u8 index) const
416{
417 if (!m_valid || index > DIRLEN)
418 return "";
419 return std::string((const char*)CurrentDir->Dir[index].Makercode, 2);
420}
421
422std::string GCMemcard::DEntry_BIFlags(u8 index) const
423{
424 if (!m_valid || index > DIRLEN)
425 return "";
426
427 std::string flags;
428 int x = CurrentDir->Dir[index].BIFlags;
429 for (int i = 0; i < 8; i++)
430 {
431 flags.push_back((x & 0x80) ? '1' : '0');
432 x = x << 1;
433 }
434 return flags;
435}
436
437std::string GCMemcard::DEntry_FileName(u8 index) const
438{
439 if (!m_valid || index > DIRLEN)
440 return "";
441
442 return std::string((const char*)CurrentDir->Dir[index].Filename, DENTRY_STRLEN);
443}
444
445u32 GCMemcard::DEntry_ModTime(u8 index) const
446{
447 if (!m_valid || index > DIRLEN)
448 return 0xFFFFFFFF;
449
450 return BE32(CurrentDir->Dir[index].ModTime)(Common::swap32(CurrentDir->Dir[index].ModTime));
451}
452
453u32 GCMemcard::DEntry_ImageOffset(u8 index) const
454{
455 if (!m_valid || index > DIRLEN)
456 return 0xFFFFFFFF;
457
458 return BE32(CurrentDir->Dir[index].ImageOffset)(Common::swap32(CurrentDir->Dir[index].ImageOffset));
459}
460
461std::string GCMemcard::DEntry_IconFmt(u8 index) const
462{
463 if (!m_valid || index > DIRLEN)
464 return "";
465
466 int x = CurrentDir->Dir[index].IconFmt[0];
467 std::string format;
468 for(int i = 0; i < 16; i++)
469 {
470 if (i == 8) x = CurrentDir->Dir[index].IconFmt[1];
471 format.push_back((x & 0x80) ? '1' : '0');
472 x = x << 1;
473 }
474 return format;
475}
476
477std::string GCMemcard::DEntry_AnimSpeed(u8 index) const
478{
479 if (!m_valid || index > DIRLEN)
480 return "";
481
482 int x = CurrentDir->Dir[index].AnimSpeed[0];
483 std::string speed;
484 for(int i = 0; i < 16; i++)
485 {
486 if (i == 8) x = CurrentDir->Dir[index].AnimSpeed[1];
487 speed.push_back((x & 0x80) ? '1' : '0');
488 x = x << 1;
489 }
490 return speed;
491}
492
493std::string GCMemcard::DEntry_Permissions(u8 index) const
494{
495 if (!m_valid || index > DIRLEN)
496 return "";
497
498 u8 Permissions = CurrentDir->Dir[index].Permissions;
499 std::string permissionsString;
500 permissionsString.push_back((Permissions & 16) ? 'x' : 'M');
501 permissionsString.push_back((Permissions & 8) ? 'x' : 'C');
502 permissionsString.push_back((Permissions & 4) ? 'P' : 'x');
503 return permissionsString;
504}
505
506u8 GCMemcard::DEntry_CopyCounter(u8 index) const
507{
508 if (!m_valid || index > DIRLEN)
509 return 0xFF;
510
511 return CurrentDir->Dir[index].CopyCounter;
512}
513
514u16 GCMemcard::DEntry_FirstBlock(u8 index) const
515{
516 if (!m_valid || index > DIRLEN)
517 return 0xFFFF;
518
519 u16 block = BE16(CurrentDir->Dir[index].FirstBlock)(Common::swap16(CurrentDir->Dir[index].FirstBlock));
520 if (block > (u16) maxBlock) return 0xFFFF;
521 return block;
522}
523
524u16 GCMemcard::DEntry_BlockCount(u8 index) const
525{
526 if (!m_valid || index > DIRLEN)
527 return 0xFFFF;
528
529 u16 blocks = BE16(CurrentDir->Dir[index].BlockCount)(Common::swap16(CurrentDir->Dir[index].BlockCount));
530 if (blocks > (u16) maxBlock) return 0xFFFF;
531 return blocks;
532}
533
534u32 GCMemcard::DEntry_CommentsAddress(u8 index) const
535{
536 if (!m_valid || index > DIRLEN)
537 return 0xFFFF;
538
539 return BE32(CurrentDir->Dir[index].CommentsAddr)(Common::swap32(CurrentDir->Dir[index].CommentsAddr));
540}
541
542std::string GCMemcard::GetSaveComment1(u8 index) const
543{
544 if (!m_valid || index > DIRLEN)
545 return "";
546
547 u32 Comment1 = BE32(CurrentDir->Dir[index].CommentsAddr)(Common::swap32(CurrentDir->Dir[index].CommentsAddr));
548 u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock)(Common::swap16(CurrentDir->Dir[index].FirstBlock)) - MC_FST_BLOCKS;
549 if ((DataBlock > maxBlock) || (Comment1 == 0xFFFFFFFF))
550 {
551 return "";
552 }
553 return std::string((const char *)mc_data_blocks[DataBlock].block + Comment1, DENTRY_STRLEN);
554}
555
556std::string GCMemcard::GetSaveComment2(u8 index) const
557{
558 if (!m_valid || index > DIRLEN)
559 return "";
560
561 u32 Comment1 = BE32(CurrentDir->Dir[index].CommentsAddr)(Common::swap32(CurrentDir->Dir[index].CommentsAddr));
562 u32 Comment2 = Comment1 + DENTRY_STRLEN;
563 u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock)(Common::swap16(CurrentDir->Dir[index].FirstBlock)) - MC_FST_BLOCKS;
564 if ((DataBlock > maxBlock) || (Comment1 == 0xFFFFFFFF))
565 {
566 return "";
567 }
568 return std::string((const char *)mc_data_blocks[DataBlock].block + Comment2, DENTRY_STRLEN);
569}
570
571bool GCMemcard::GetDEntry(u8 index, DEntry &dest) const
572{
573 if (!m_valid || index > DIRLEN)
574 return false;
575
576 dest = CurrentDir->Dir[index];
577 return true;
578}
579
580u16 GCMemcard::BlockAlloc::GetNextBlock(u16 Block) const
581{
582 if ((Block < MC_FST_BLOCKS) || (Block > 4091))
583 return 0;
584
585 return Common::swap16(Map[Block-MC_FST_BLOCKS]);
586}
587
588u16 GCMemcard::BlockAlloc::NextFreeBlock(u16 StartingBlock) const
589{
590 if (FreeBlocks)
591 {
592 for (u16 i = StartingBlock; i < BAT_SIZE; ++i)
593 if (Map[i-MC_FST_BLOCKS] == 0)
594 return i;
595
596 for (u16 i = MC_FST_BLOCKS; i < StartingBlock; ++i)
597 if (Map[i-MC_FST_BLOCKS] == 0)
598 return i;
599 }
600 return 0xFFFF;
601}
602
603bool GCMemcard::BlockAlloc::ClearBlocks(u16 FirstBlock, u16 BlockCount)
604{
605 std::vector<u16> blocks;
606 while (FirstBlock != 0xFFFF && FirstBlock != 0)
607 {
608 blocks.push_back(FirstBlock);
609 FirstBlock = GetNextBlock(FirstBlock);
610 }
611 if (FirstBlock > 0)
612 {
613 size_t length = blocks.size();
614 if (length != BlockCount)
615 {
616 return false;
617 }
618 for (unsigned int i = 0; i < length; ++i)
619 Map[blocks.at(i)-MC_FST_BLOCKS] = 0;
620 FreeBlocks = BE16(BE16(FreeBlocks) + BlockCount)(Common::swap16((Common::swap16(FreeBlocks)) + BlockCount));
621
622 return true;
623 }
624 return false;
625}
626
627u32 GCMemcard::GetSaveData(u8 index, std::vector<GCMBlock> & Blocks) const
628{
629 if (!m_valid)
630 return NOMEMCARD;
631
632 u16 block = DEntry_FirstBlock(index);
633 u16 BlockCount = DEntry_BlockCount(index);
634 //u16 memcardSize = BE16(hdr.SizeMb) * MBIT_TO_BLOCKS;
635
636 if ((block == 0xFFFF) || (BlockCount == 0xFFFF))
637 {
638 return FAIL;
639 }
640
641 u16 nextBlock = block;
642 for (int i = 0; i < BlockCount; ++i)
643 {
644 if ((!nextBlock) || (nextBlock == 0xFFFF))
645 return FAIL;
646 Blocks.push_back(mc_data_blocks[nextBlock-MC_FST_BLOCKS]);
647 nextBlock = CurrentBat->GetNextBlock(nextBlock);
648 }
649 return SUCCESS;
650}
651// End DEntry functions
652
653u32 GCMemcard::ImportFile(DEntry& direntry, std::vector<GCMBlock> &saveBlocks)
654{
655 if (!m_valid)
656 return NOMEMCARD;
657
658 if (GetNumFiles() >= DIRLEN)
659 {
660 return OUTOFDIRENTRIES;
661 }
662 if (BE16(CurrentBat->FreeBlocks)(Common::swap16(CurrentBat->FreeBlocks)) < BE16(direntry.BlockCount)(Common::swap16(direntry.BlockCount)))
663 {
664 return OUTOFBLOCKS;
665 }
666 if (TitlePresent(direntry) != DIRLEN)
667 {
668 return TITLEPRESENT;
669 }
670
671 // find first free data block
672 u16 firstBlock = CurrentBat->NextFreeBlock(BE16(CurrentBat->LastAllocated)(Common::swap16(CurrentBat->LastAllocated)));
673 if (firstBlock == 0xFFFF)
674 return OUTOFBLOCKS;
675 Directory UpdatedDir = *CurrentDir;
676
677 // find first free dir entry
678 for (int i=0; i < DIRLEN; i++)
679 {
680 if (BE32(UpdatedDir.Dir[i].Gamecode)(Common::swap32(UpdatedDir.Dir[i].Gamecode)) == 0xFFFFFFFF)
681 {
682 UpdatedDir.Dir[i] = direntry;
683 *(u16*)&UpdatedDir.Dir[i].FirstBlock = BE16(firstBlock)(Common::swap16(firstBlock));
684 UpdatedDir.Dir[i].CopyCounter = UpdatedDir.Dir[i].CopyCounter+1;
685 break;
686 }
687 }
688 UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1)(Common::swap16((Common::swap16(UpdatedDir.UpdateCounter)) + 1
))
;
689 *PreviousDir = UpdatedDir;
690 if (PreviousDir == &dir )
691 {
692 CurrentDir = &dir;
693 PreviousDir = &dir_backup;
694 }
695 else
696 {
697 CurrentDir = &dir_backup;
698 PreviousDir = &dir;
699 }
700
701 int fileBlocks = BE16(direntry.BlockCount)(Common::swap16(direntry.BlockCount));
702
703 FZEROGX_MakeSaveGameValid(direntry, saveBlocks);
704 PSO_MakeSaveGameValid(direntry, saveBlocks);
705
706 BlockAlloc UpdatedBat = *CurrentBat;
707 u16 nextBlock;
708 // keep assuming no freespace fragmentation, and copy over all the data
709 for (int i = 0; i < fileBlocks; ++i)
710 {
711 if (firstBlock == 0xFFFF)
712 PanicAlert("Fatal Error")MsgAlert(false, WARNING, "Fatal Error");
713 mc_data_blocks[firstBlock - MC_FST_BLOCKS] = saveBlocks[i];
714 if (i == fileBlocks-1)
715 nextBlock = 0xFFFF;
716 else
717 nextBlock = UpdatedBat.NextFreeBlock(firstBlock+1);
718 UpdatedBat.Map[firstBlock - MC_FST_BLOCKS] = BE16(nextBlock)(Common::swap16(nextBlock));
719 UpdatedBat.LastAllocated = BE16(firstBlock)(Common::swap16(firstBlock));
720 firstBlock = nextBlock;
721 }
722
723 UpdatedBat.FreeBlocks = BE16(BE16(UpdatedBat.FreeBlocks) - fileBlocks)(Common::swap16((Common::swap16(UpdatedBat.FreeBlocks)) - fileBlocks
))
;
724 UpdatedBat.UpdateCounter = BE16(BE16(UpdatedBat.UpdateCounter) + 1)(Common::swap16((Common::swap16(UpdatedBat.UpdateCounter)) + 1
))
;
725 *PreviousBat = UpdatedBat;
726 if (PreviousBat == &bat )
727 {
728 CurrentBat = &bat;
729 PreviousBat = &bat_backup;
730 }
731 else
732 {
733 CurrentBat = &bat_backup;
734 PreviousBat = &bat;
735 }
736
737 return SUCCESS;
738}
739
740u32 GCMemcard::RemoveFile(u8 index) //index in the directory array
741{
742 if (!m_valid)
743 return NOMEMCARD;
744 if (index >= DIRLEN)
745 return DELETE_FAIL;
746
747 u16 startingblock = BE16(dir.Dir[index].FirstBlock)(Common::swap16(dir.Dir[index].FirstBlock));
748 u16 numberofblocks = BE16(dir.Dir[index].BlockCount)(Common::swap16(dir.Dir[index].BlockCount));
749
750 BlockAlloc UpdatedBat = *CurrentBat;
751 if (!UpdatedBat.ClearBlocks(startingblock, numberofblocks))
752 return DELETE_FAIL;
753 UpdatedBat.UpdateCounter = BE16(BE16(UpdatedBat.UpdateCounter) + 1)(Common::swap16((Common::swap16(UpdatedBat.UpdateCounter)) + 1
))
;
754 *PreviousBat = UpdatedBat;
755 if (PreviousBat == &bat )
756 {
757 CurrentBat = &bat;
758 PreviousBat = &bat_backup;
759 }
760 else
761 {
762 CurrentBat = &bat_backup;
763 PreviousBat = &bat;
764 }
765
766 Directory UpdatedDir = *CurrentDir;
767 /*
768 // TODO: determine when this is used, even on the same memory card I have seen
769 // both update to broken file, and not updated
770 *(u32*)&UpdatedDir.Dir[index].Gamecode = 0;
771 *(u16*)&UpdatedDir.Dir[index].Makercode = 0;
772 memset(UpdatedDir.Dir[index].Filename, 0, 0x20);
773 strcpy((char*)UpdatedDir.Dir[index].Filename, "Broken File000");
774 *(u16*)&UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1);
775
776 *PreviousDir = UpdatedDir;
777 if (PreviousDir == &dir )
778 {
779 CurrentDir = &dir;
780 PreviousDir = &dir_backup;
781 }
782 else
783 {
784 CurrentDir = &dir_backup;
785 PreviousDir = &dir;
786 }
787 */
788 memset(&(UpdatedDir.Dir[index]), 0xFF, DENTRY_SIZE);
789 UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1)(Common::swap16((Common::swap16(UpdatedDir.UpdateCounter)) + 1
))
;
790 *PreviousDir = UpdatedDir;
791 if (PreviousDir == &dir )
792 {
793 CurrentDir = &dir;
794 PreviousDir = &dir_backup;
795 }
796 else
797 {
798 CurrentDir = &dir_backup;
799 PreviousDir = &dir;
800 }
801
802 return SUCCESS;
803}
804
805u32 GCMemcard::CopyFrom(const GCMemcard& source, u8 index)
806{
807 if (!m_valid || !source.m_valid)
808 return NOMEMCARD;
809
810 DEntry tempDEntry;
811 if (!source.GetDEntry(index, tempDEntry))
812 return NOMEMCARD;
813
814 u32 size = source.DEntry_BlockCount(index);
815 if (size == 0xFFFF) return INVALIDFILESIZE;
816
817 std::vector<GCMBlock> saveData;
818 saveData.reserve(size);
819 switch (source.GetSaveData(index, saveData))
820 {
821 case FAIL:
822 return FAIL;
823 case NOMEMCARD:
824 return NOMEMCARD;
825 default:
826 return ImportFile(tempDEntry, saveData);
827 }
828}
829
830u32 GCMemcard::ImportGci(const char *inputFile, const std::string &outputFile)
831{
832 if (outputFile.empty() && !m_valid)
833 return OPENFAIL;
834
835 File::IOFile gci(inputFile, "rb");
836 if (!gci)
837 return OPENFAIL;
838
839 u32 result = ImportGciInternal(gci.ReleaseHandle(), inputFile, outputFile);
840
841 return result;
842}
843
844u32 GCMemcard::ImportGciInternal(FILE* gcih, const char *inputFile, const std::string &outputFile)
845{
846 File::IOFile gci(gcih);
847 unsigned int offset;
848 char tmp[0xD];
849 std::string fileType;
850 SplitPath(inputFile, NULL__null, NULL__null, &fileType);
851
852 if (!strcasecmp(fileType.c_str(), ".gci"))
853 offset = GCI;
854 else
855 {
856 gci.ReadBytes(tmp, 0xD);
857 if (!strcasecmp(fileType.c_str(), ".gcs"))
858 {
859 if (!memcmp(tmp, "GCSAVE", 6)) // Header must be uppercase
860 offset = GCS;
861 else
862 return GCSFAIL;
863 }
864 else if (!strcasecmp(fileType.c_str(), ".sav"))
865 {
866 if (!memcmp(tmp, "DATELGC_SAVE", 0xC)) // Header must be uppercase
867 offset = SAV;
868 else
869 return SAVFAIL;
870 }
871 else
872 return OPENFAIL;
873 }
874 gci.Seek(offset, SEEK_SET0);
875
876 DEntry tempDEntry;
877 gci.ReadBytes(&tempDEntry, DENTRY_SIZE);
878 const int fStart = (int)gci.Tell();
879 gci.Seek(0, SEEK_END2);
880 const int length = (int)gci.Tell() - fStart;
881 gci.Seek(offset + DENTRY_SIZE, SEEK_SET0);
882
883 Gcs_SavConvert(tempDEntry, offset, length);
884
885 if (length != BE16(tempDEntry.BlockCount)(Common::swap16(tempDEntry.BlockCount)) * BLOCK_SIZE)
886 return LENGTHFAIL;
887 if (gci.Tell() != offset + DENTRY_SIZE) // Verify correct file position
888 return OPENFAIL;
889
890 u32 size = BE16((tempDEntry.BlockCount))(Common::swap16((tempDEntry.BlockCount)));
891 std::vector<GCMBlock> saveData;
892 saveData.reserve(size);
893
894 for (unsigned int i = 0; i < size; ++i)
895 {
896 GCMBlock b;
897 gci.ReadBytes(b.block, BLOCK_SIZE);
898 saveData.push_back(b);
899 }
900 u32 ret;
901 if (!outputFile.empty())
902 {
903 File::IOFile gci2(outputFile, "wb");
904 bool completeWrite = true;
905 if (!gci2)
906 {
907 return OPENFAIL;
908 }
909 gci2.Seek(0, SEEK_SET0);
910
911 if (!gci2.WriteBytes(&tempDEntry, DENTRY_SIZE))
912 completeWrite = false;
913 int fileBlocks = BE16(tempDEntry.BlockCount)(Common::swap16(tempDEntry.BlockCount));
914 gci2.Seek(DENTRY_SIZE, SEEK_SET0);
915
916 for (int i = 0; i < fileBlocks; ++i)
917 {
918 if (!gci2.WriteBytes(saveData[i].block, BLOCK_SIZE))
919 completeWrite = false;
920 }
921
922 if (completeWrite)
923 ret = GCS;
924 else
925 ret = WRITEFAIL;
926 }
927 else
928 ret = ImportFile(tempDEntry, saveData);
929
930 return ret;
931}
932
933u32 GCMemcard::ExportGci(u8 index, const char *fileName, const std::string &directory) const
934{
935 File::IOFile gci;
936 int offset = GCI;
937 if (!fileName)
938 {
939 std::string gciFilename;
940 if (!GCI_FileName(index, gciFilename)) return SUCCESS;
941 gci.Open(directory + DIR_SEP"/" + gciFilename, "wb");
942 }
943 else
944 {
945 gci.Open(fileName, "wb");
946
947 std::string fileType;
948 SplitPath(fileName, NULL__null, NULL__null, &fileType);
949 if (!strcasecmp(fileType.c_str(), ".gcs"))
950 {
951 offset = GCS;
952 }
953 else if (!strcasecmp(fileType.c_str(), ".sav"))
954 {
955 offset = SAV;
956 }
957 }
958
959 if (!gci)
960 return OPENFAIL;
961
962 gci.Seek(0, SEEK_SET0);
963
964 switch(offset)
965 {
966 case GCS:
967 u8 gcsHDR[GCS];
968 memset(gcsHDR, 0, GCS);
969 memcpy(gcsHDR, "GCSAVE", 6);
970 gci.WriteArray(gcsHDR, GCS);
971 break;
972
973 case SAV:
974 u8 savHDR[SAV];
975 memset(savHDR, 0, SAV);
976 memcpy(savHDR, "DATELGC_SAVE", 0xC);
977 gci.WriteArray(savHDR, SAV);
978 break;
979 }
980
981 DEntry tempDEntry;
982 if (!GetDEntry(index, tempDEntry))
983 {
984 return NOMEMCARD;
985 }
986
987 Gcs_SavConvert(tempDEntry, offset);
988 gci.WriteBytes(&tempDEntry, DENTRY_SIZE);
989
990 u32 size = DEntry_BlockCount(index);
991 if (size == 0xFFFF)
992 {
993 return FAIL;
994 }
995
996 std::vector<GCMBlock> saveData;
997 saveData.reserve(size);
998
999 switch(GetSaveData(index, saveData))
1000 {
1001 case FAIL:
1002 return FAIL;
1003 case NOMEMCARD:
1004 return NOMEMCARD;
1005 }
1006 gci.Seek(DENTRY_SIZE + offset, SEEK_SET0);
1007 for (unsigned int i = 0; i < size; ++i)
1008 {
1009 gci.WriteBytes(saveData[i].block, BLOCK_SIZE);
1010 }
1011
1012 if (gci.IsGood())
1013 return SUCCESS;
1014 else
1015 return WRITEFAIL;
1016}
1017
1018void GCMemcard::Gcs_SavConvert(DEntry &tempDEntry, int saveType, int length)
1019{
1020 switch(saveType)
1021 {
1022 case GCS:
1023 { // field containing the Block count as displayed within
1024 // the GameSaves software is not stored in the GCS file.
1025 // It is stored only within the corresponding GSV file.
1026 // If the GCS file is added without using the GameSaves software,
1027 // the value stored is always "1"
1028 *(u16*)&tempDEntry.BlockCount = BE16(length / BLOCK_SIZE)(Common::swap16(length / BLOCK_SIZE));
1029 }
1030 break;
1031 case SAV:
1032 // swap byte pairs
1033 // 0x2C and 0x2D, 0x2E and 0x2F, 0x30 and 0x31, 0x32 and 0x33,
1034 // 0x34 and 0x35, 0x36 and 0x37, 0x38 and 0x39, 0x3A and 0x3B,
1035 // 0x3C and 0x3D,0x3E and 0x3F.
1036 // It seems that sav files also swap the BIFlags...
1037 ByteSwap(&tempDEntry.Unused1, &tempDEntry.BIFlags);
1038 ArrayByteSwap((tempDEntry.ImageOffset))(ByteSwap((tempDEntry.ImageOffset), (tempDEntry.ImageOffset)+
sizeof(u8)));
;
1039 ArrayByteSwap(&(tempDEntry.ImageOffset[2]))(ByteSwap(&(tempDEntry.ImageOffset[2]), &(tempDEntry.
ImageOffset[2])+sizeof(u8)));
;
1040 ArrayByteSwap((tempDEntry.IconFmt))(ByteSwap((tempDEntry.IconFmt), (tempDEntry.IconFmt)+sizeof(u8
)));
;
1041 ArrayByteSwap((tempDEntry.AnimSpeed))(ByteSwap((tempDEntry.AnimSpeed), (tempDEntry.AnimSpeed)+sizeof
(u8)));
;
1042 ByteSwap(&tempDEntry.Permissions, &tempDEntry.CopyCounter);
1043 ArrayByteSwap((tempDEntry.FirstBlock))(ByteSwap((tempDEntry.FirstBlock), (tempDEntry.FirstBlock)+sizeof
(u8)));
;
1044 ArrayByteSwap((tempDEntry.BlockCount))(ByteSwap((tempDEntry.BlockCount), (tempDEntry.BlockCount)+sizeof
(u8)));
;
1045 ArrayByteSwap((tempDEntry.Unused2))(ByteSwap((tempDEntry.Unused2), (tempDEntry.Unused2)+sizeof(u8
)));
;
1046 ArrayByteSwap((tempDEntry.CommentsAddr))(ByteSwap((tempDEntry.CommentsAddr), (tempDEntry.CommentsAddr
)+sizeof(u8)));
;
1047 ArrayByteSwap(&(tempDEntry.CommentsAddr[2]))(ByteSwap(&(tempDEntry.CommentsAddr[2]), &(tempDEntry
.CommentsAddr[2])+sizeof(u8)));
;
1048 break;
1049 default:
1050 break;
1051 }
1052}
1053
1054bool GCMemcard::ReadBannerRGBA8(u8 index, u32* buffer) const
1055{
1056 if (!m_valid)
1057 return false;
1058
1059 int flags = CurrentDir->Dir[index].BIFlags;
1060 // Timesplitters 2 is the only game that I see this in
1061 // May be a hack
1062 if (flags == 0xFB) flags = ~flags;
1063
1064 int bnrFormat = (flags&3);
1065
1066 if (bnrFormat == 0)
1067 return false;
1068
1069 u32 DataOffset = BE32(CurrentDir->Dir[index].ImageOffset)(Common::swap32(CurrentDir->Dir[index].ImageOffset));
1070 u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock)(Common::swap16(CurrentDir->Dir[index].FirstBlock)) - MC_FST_BLOCKS;
1071
1072 if ((DataBlock > maxBlock) || (DataOffset == 0xFFFFFFFF))
1073 {
1074 return false;
1075 }
1076
1077 const int pixels = 96*32;
1078
1079 if (bnrFormat&1)
1080 {
1081 u8 *pxdata = (u8* )(mc_data_blocks[DataBlock].block + DataOffset);
1082 u16 *paldata = (u16*)(mc_data_blocks[DataBlock].block + DataOffset + pixels);
1083
1084 decodeCI8image(buffer, pxdata, paldata, 96, 32);
1085 }
1086 else
1087 {
1088 u16 *pxdata = (u16*)(mc_data_blocks[DataBlock].block + DataOffset);
1089
1090 decode5A3image(buffer, pxdata, 96, 32);
1091 }
1092 return true;
1093}
1094
1095u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
1096{
1097 if (!m_valid)
1
Taking false branch
1098 return 0;
1099
1100 // To ensure only one type of icon is used
1101 // Sonic Heroes it the only game I have seen that tries to use a CI8 and RGB5A3 icon
1102 //int fmtCheck = 0;
1103
1104 int formats = BE16(CurrentDir->Dir[index].IconFmt)(Common::swap16(CurrentDir->Dir[index].IconFmt));
1105 int fdelays = BE16(CurrentDir->Dir[index].AnimSpeed)(Common::swap16(CurrentDir->Dir[index].AnimSpeed));
1106
1107 int flags = CurrentDir->Dir[index].BIFlags;
1108 // Timesplitters 2 and 3 is the only game that I see this in
1109 // May be a hack
1110 //if (flags == 0xFB) flags = ~flags;
1111 // Batten Kaitos has 0x65 as flag too. Everything but the first 3 bytes seems irrelevant.
1112 // Something similar happens with Wario Ware Inc. AnimSpeed
1113
1114 int bnrFormat = (flags&3);
1115
1116 u32 DataOffset = BE32(CurrentDir->Dir[index].ImageOffset)(Common::swap32(CurrentDir->Dir[index].ImageOffset));
1117 u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock)(Common::swap16(CurrentDir->Dir[index].FirstBlock)) - MC_FST_BLOCKS;
1118
1119 if ((DataBlock > maxBlock) || (DataOffset == 0xFFFFFFFF))
2
Assuming 'DataOffset' is not equal to -1
3
Taking false branch
1120 {
1121 return 0;
1122 }
1123
1124 u8* animData = (u8*)(mc_data_blocks[DataBlock].block + DataOffset);
1125
1126 switch (bnrFormat)
4
'Default' branch taken. Execution continues on line 1136
1127 {
1128 case 1:
1129 animData += 96*32 + 2*256; // image+palette
1130 break;
1131 case 2:
1132 animData += 96*32*2;
1133 break;
1134 }
1135
1136 int fmts[8];
1137 u8* data[8];
1138 int frames = 0;
1139
1140 for (int i = 0; i < 8; i++)
5
Loop condition is true. Entering loop body
8
Loop condition is true. Entering loop body
1141 {
1142 fmts[i] = (formats >> (2*i))&3;
1143 delays[i] = ((fdelays >> (2*i))&3);
1144 data[i] = animData;
1145
1146 if (!delays[i])
6
Taking false branch
9
Taking true branch
1147 {
1148 //First icon_speed = 0 indicates there aren't any more icons
1149 break;
10
Execution continues on line 1170
1150 }
1151 //If speed is set there is an icon (it can be a "blank frame")
1152 frames++;
1153 if (fmts[i] != 0)
7
Taking false branch
1154 {
1155 switch (fmts[i])
1156 {
1157 case CI8SHARED: // CI8 with shared palette
1158 animData += 32*32;
1159 break;
1160 case RGB5A3: // RGB5A3
1161 animData += 32*32*2;
1162 break;
1163 case CI8: // CI8 with own palette
1164 animData += 32*32 + 2*256;
1165 break;
1166 }
1167 }
1168 }
1169
1170 u16* sharedPal = (u16*)(animData);
1171 int j = 0;
1172
1173 for (int i = 0; i < 8; i++)
11
Loop condition is true. Entering loop body
1174 {
1175
1176 if (!delays[i])
12
Taking false branch
1177 {
1178 //First icon_speed = 0 indicates there aren't any more icons
1179 break;
1180 }
1181 if (fmts[i] != 0)
13
Taking false branch
1182 {
1183 switch (fmts[i])
1184 {
1185 case CI8SHARED: // CI8 with shared palette
1186 decodeCI8image(buffer,data[i],sharedPal,32,32);
1187 buffer += 32*32;
1188 break;
1189 case RGB5A3: // RGB5A3
1190 decode5A3image(buffer, (u16*)(data[i]), 32, 32);
1191 buffer += 32*32;
1192 break;
1193 case CI8: // CI8 with own palette
1194 u16 *paldata = (u16*)(data[i] + 32*32);
1195 decodeCI8image(buffer, data[i], paldata, 32, 32);
1196 buffer += 32*32;
1197 break;
1198 }
1199 }
1200 else
1201 {
1202 //Speed is set but there's no actual icon
1203 //This is used to reduce animation speed in Pikmin and Luigi's Mansion for example
1204 //These "blank frames" show the next icon
1205 for(j=i; j<8;++j)
14
Loop condition is true. Entering loop body
16
Loop condition is true. Entering loop body
18
Loop condition is true. Entering loop body
1206 {
1207 if (fmts[j] != 0)
15
Taking false branch
17
Taking false branch
19
The left operand of '!=' is a garbage value
1208 {
1209 switch (fmts[j])
1210 {
1211 case CI8SHARED: // CI8 with shared palette
1212 decodeCI8image(buffer,data[j],sharedPal,32,32);
1213 break;
1214 case RGB5A3: // RGB5A3
1215 decode5A3image(buffer, (u16*)(data[j]), 32, 32);
1216 buffer += 32*32;
1217 break;
1218 case CI8: // CI8 with own palette
1219 u16 *paldata = (u16*)(data[j] + 32*32);
1220 decodeCI8image(buffer, data[j], paldata, 32, 32);
1221 buffer += 32*32;
1222 break;
1223 }
1224 }
1225 }
1226 }
1227 }
1228
1229 return frames;
1230}
1231
1232
1233bool GCMemcard::Format(u8 * card_data, bool sjis, u16 SizeMb)
1234{
1235 if (!card_data)
1236 return false;
1237 memset(card_data, 0xFF, BLOCK_SIZE*3);
1238 memset(card_data + BLOCK_SIZE*3, 0, BLOCK_SIZE*2);
1239
1240 GCMC_Header gcp;
1241 gcp.hdr = (Header*)card_data;
1242 gcp.dir = (Directory *)(card_data + BLOCK_SIZE);
1243 gcp.dir_backup = (Directory *)(card_data + BLOCK_SIZE*2);
1244 gcp.bat = (BlockAlloc *)(card_data + BLOCK_SIZE*3);
1245 gcp.bat_backup = (BlockAlloc *)(card_data + BLOCK_SIZE*4);
1246
1247 *(u16*)gcp.hdr->SizeMb = BE16(SizeMb)(Common::swap16(SizeMb));
1248 gcp.hdr->Encoding = BE16(sjis ? 1 : 0)(Common::swap16(sjis ? 1 : 0));
1249
1250 FormatInternal(gcp);
1251 return true;
1252}
1253
1254bool GCMemcard::Format(bool sjis, u16 SizeMb)
1255{
1256 memset(&hdr, 0xFF, BLOCK_SIZE);
1257 memset(&dir, 0xFF, BLOCK_SIZE);
1258 memset(&dir_backup, 0xFF, BLOCK_SIZE);
1259 memset(&bat, 0, BLOCK_SIZE);
1260 memset(&bat_backup, 0, BLOCK_SIZE);
1261
1262 GCMC_Header gcp;
1263 gcp.hdr = &hdr;
1264 gcp.dir = &dir;
1265 gcp.dir_backup = &dir_backup;
1266 gcp.bat = &bat;
1267 gcp.bat_backup = &bat_backup;
1268
1269 *(u16*)hdr.SizeMb = BE16(SizeMb)(Common::swap16(SizeMb));
1270 hdr.Encoding = BE16(sjis ? 1 : 0)(Common::swap16(sjis ? 1 : 0));
1271 FormatInternal(gcp);
1272
1273 m_sizeMb = SizeMb;
1274 maxBlock = (u32)m_sizeMb * MBIT_TO_BLOCKS;
1275 mc_data_blocks.reserve(maxBlock - MC_FST_BLOCKS);
1276 for (u32 i = 0; i < (maxBlock - MC_FST_BLOCKS); ++i)
1277 {
1278 GCMBlock b;
1279 mc_data_blocks.push_back(b);
1280 }
1281
1282 initDirBatPointers();
1283 m_valid = true;
1284
1285 return Save();
1286}
1287
1288void GCMemcard::FormatInternal(GCMC_Header &GCP)
1289{
1290 Header *p_hdr = GCP.hdr;
1291 u64 rand = CEXIIPL::GetGCTime();
1292 p_hdr->formatTime = Common::swap64(rand);
1293
1294 for(int i = 0; i < 12; i++)
1295 {
1296 rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
1297 p_hdr->serial[i] = (u8)(g_SRAM.flash_id[0][i] + (u32)rand);
1298 rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
1299 rand &= (u64)0x0000000000007fffULL;
1300 }
1301 p_hdr->SramBias = g_SRAM.counter_bias;
1302 p_hdr->SramLang = g_SRAM.lang;
1303 // TODO: determine the purpose of Unk2 1 works for slot A, 0 works for both slot A and slot B
1304 *(u32*)&p_hdr->Unk2 = 0; // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
1305 *(u16*)&p_hdr->deviceID = 0;
1306 calc_checksumsBE((u16*)p_hdr, 0xFE, &p_hdr->Checksum, &p_hdr->Checksum_Inv);
1307
1308 Directory *p_dir = GCP.dir,
1309 *p_dir_backup = GCP.dir_backup;
1310 *(u16*)&p_dir->UpdateCounter = 0;
1311 p_dir_backup->UpdateCounter = BE16(1)(Common::swap16(1));
1312 calc_checksumsBE((u16*)p_dir, 0xFFE, &p_dir->Checksum, &p_dir->Checksum_Inv);
1313 calc_checksumsBE((u16*)p_dir_backup, 0xFFE, &p_dir_backup->Checksum, &p_dir_backup->Checksum_Inv);
1314
1315 BlockAlloc *p_bat = GCP.bat,
1316 *p_bat_backup = GCP.bat_backup;
1317 p_bat_backup->UpdateCounter = BE16(1)(Common::swap16(1));
1318 p_bat->FreeBlocks = *(u16*)&p_bat_backup->FreeBlocks = BE16(( BE16(p_hdr->SizeMb) * MBIT_TO_BLOCKS) - MC_FST_BLOCKS)(Common::swap16(( (Common::swap16(p_hdr->SizeMb)) * MBIT_TO_BLOCKS
) - MC_FST_BLOCKS))
;
1319 p_bat->LastAllocated = p_bat_backup->LastAllocated = BE16(4)(Common::swap16(4));
1320 calc_checksumsBE((u16*)p_bat+2, 0xFFE, &p_bat->Checksum, &p_bat->Checksum_Inv);
1321 calc_checksumsBE((u16*)p_bat_backup+2, 0xFFE, &p_bat_backup->Checksum, &p_bat_backup->Checksum_Inv);
1322}
1323
1324void GCMemcard::CARD_GetSerialNo(u32 *serial1,u32 *serial2)
1325{
1326 u32 serial[8];
1327 int i;
1328 for (i = 0; i < 8; i++)
1329 {
1330 memcpy(&serial[i], (u8 *) &hdr+(i*4), 4);
1331 }
1332
1333 *serial1 = serial[0]^serial[2]^serial[4]^serial[6];
1334 *serial2 = serial[1]^serial[3]^serial[5]^serial[7];
1335}
1336
1337
1338/*************************************************************/
1339/* FZEROGX_MakeSaveGameValid */
1340/* (use just before writing a F-Zero GX system .gci file) */
1341/* */
1342/* Parameters: */
1343/* direntry: [Description needed] */
1344/* FileBuffer: [Description needed] */
1345/* */
1346/* Returns: Error code */
1347/*************************************************************/
1348
1349s32 GCMemcard::FZEROGX_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer)
1350{
1351 u32 i,j;
1352 u32 serial1,serial2;
1353 u16 chksum = 0xFFFF;
1354 int block = 0;
1355
1356 // check for F-Zero GX system file
1357 if (strcmp((char*)direntry.Filename,"f_zero.dat")!=0) return 0;
1358
1359 // get encrypted destination memory card serial numbers
1360 CARD_GetSerialNo(&serial1,&serial2);
1361
1362 // set new serial numbers
1363 *(u16*)&FileBuffer[1].block[0x0066] = BE16(BE32(serial1) >> 16)(Common::swap16((Common::swap32(serial1)) >> 16));
1364 *(u16*)&FileBuffer[3].block[0x1580] = BE16(BE32(serial2) >> 16)(Common::swap16((Common::swap32(serial2)) >> 16));
1365 *(u16*)&FileBuffer[1].block[0x0060] = BE16(BE32(serial1) & 0xFFFF)(Common::swap16((Common::swap32(serial1)) & 0xFFFF));
1366 *(u16*)&FileBuffer[1].block[0x0200] = BE16(BE32(serial2) & 0xFFFF)(Common::swap16((Common::swap32(serial2)) & 0xFFFF));
1367
1368 // calc 16-bit checksum
1369 for (i=0x02;i<0x8000;i++)
1370 {
1371 chksum ^= (FileBuffer[block].block[i-(block*0x2000)]&0xFF);
1372 for (j=8; j > 0; j--)
1373 {
1374 if (chksum&1) chksum = (chksum>>1)^0x8408;
1375 else chksum >>= 1;
1376 }
1377 if (!(i%0x2000)) block ++;
1378 }
1379
1380 // set new checksum
1381 *(u16*)&FileBuffer[0].block[0x00] = BE16(~chksum)(Common::swap16(~chksum));
1382
1383 return 1;
1384}
1385
1386/***********************************************************/
1387/* PSO_MakeSaveGameValid */
1388/* (use just before writing a PSO system .gci file) */
1389/* */
1390/* Parameters: */
1391/* direntry: [Description needed] */
1392/* FileBuffer: [Description needed] */
1393/* */
1394/* Returns: Error code */
1395/***********************************************************/
1396
1397s32 GCMemcard::PSO_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer)
1398{
1399 u32 i,j;
1400 u32 chksum;
1401 u32 crc32LUT[256];
1402 u32 serial1,serial2;
1403 u32 pso3offset = 0x00;
1404
1405 // check for PSO1&2 system file
1406 if (strcmp((char*)direntry.Filename,"PSO_SYSTEM")!=0)
1407 {
1408 // check for PSO3 system file
1409 if (strcmp((char*)direntry.Filename,"PSO3_SYSTEM")==0)
1410 {
1411 // PSO3 data block size adjustment
1412 pso3offset = 0x10;
1413 }
1414 else
1415 {
1416 // nothing to do
1417 return 0;
1418 }
1419 }
1420
1421 // get encrypted destination memory card serial numbers
1422 CARD_GetSerialNo(&serial1,&serial2);
1423
1424 // set new serial numbers
1425 *(u32*)&FileBuffer[1].block[0x0158] = serial1;
1426 *(u32*)&FileBuffer[1].block[0x015C] = serial2;
1427
1428 // generate crc32 LUT
1429 for (i=0; i < 256; i++)
1430 {
1431 chksum = i;
1432 for (j=8; j > 0; j--)
1433 {
1434 if (chksum & 1)
1435 chksum = (chksum>>1)^0xEDB88320;
1436 else
1437 chksum >>= 1;
1438 }
1439
1440 crc32LUT[i] = chksum;
1441 }
1442
1443 // PSO initial crc32 value
1444 chksum = 0xDEBB20E3;
1445
1446 // calc 32-bit checksum
1447 for (i=0x004C; i < 0x0164+pso3offset; i++)
1448 {
1449 chksum = ((chksum>>8)&0xFFFFFF)^crc32LUT[(chksum^FileBuffer[1].block[i])&0xFF];
1450 }
1451
1452 // set new checksum
1453 *(u32*)&FileBuffer[1].block[0x0048] = BE32(chksum^0xFFFFFFFF)(Common::swap32(chksum^0xFFFFFFFF));
1454
1455 return 1;
1456}