Bug Summary

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