Bug Summary

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