1 // Copyright 2019 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/HW/GCMemcard/GCIFile.h"
6 
7 #include <cinttypes>
8 
9 #include "Common/ChunkFile.h"
10 #include "Common/CommonTypes.h"
11 #include "Common/File.h"
12 #include "Common/Logging/Log.h"
13 
14 namespace Memcard
15 {
LoadHeader()16 bool GCIFile::LoadHeader()
17 {
18   if (m_filename.empty())
19     return false;
20 
21   File::IOFile save_file(m_filename, "rb");
22   if (!save_file)
23     return false;
24 
25   INFO_LOG(EXPANSIONINTERFACE, "Reading header from disk for %s", m_filename.c_str());
26   if (!save_file.ReadBytes(&m_gci_header, sizeof(m_gci_header)))
27   {
28     ERROR_LOG(EXPANSIONINTERFACE, "Failed to read header for %s", m_filename.c_str());
29     return false;
30   }
31 
32   return true;
33 }
34 
LoadSaveBlocks()35 bool GCIFile::LoadSaveBlocks()
36 {
37   if (m_save_data.empty())
38   {
39     if (m_filename.empty())
40       return false;
41 
42     File::IOFile save_file(m_filename, "rb");
43     if (!save_file)
44       return false;
45 
46     INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str());
47     u16 num_blocks = m_gci_header.m_block_count;
48 
49     const u32 size = num_blocks * BLOCK_SIZE;
50     u64 file_size = save_file.GetSize();
51     if (file_size != size + DENTRY_SIZE)
52     {
53       ERROR_LOG(EXPANSIONINTERFACE,
54                 "%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64
55                 ") does not match the size recorded in the header (0x%x)",
56                 m_filename.c_str(), file_size, size + DENTRY_SIZE);
57       return false;
58     }
59 
60     m_save_data.resize(num_blocks);
61     save_file.Seek(DENTRY_SIZE, SEEK_SET);
62     if (!save_file.ReadBytes(m_save_data.data(), size))
63     {
64       ERROR_LOG(EXPANSIONINTERFACE, "Failed to read data from GCI file %s", m_filename.c_str());
65       m_save_data.clear();
66       return false;
67     }
68   }
69   return true;
70 }
71 
HasCopyProtection() const72 bool GCIFile::HasCopyProtection() const
73 {
74   if ((strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO_SYSTEM") == 0) ||
75       (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO3_SYSTEM") == 0) ||
76       (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "f_zero.dat") == 0))
77     return true;
78   return false;
79 }
80 
UsesBlock(u16 block_num)81 int GCIFile::UsesBlock(u16 block_num)
82 {
83   for (u16 i = 0; i < m_used_blocks.size(); ++i)
84   {
85     if (m_used_blocks[i] == block_num)
86       return i;
87   }
88   return -1;
89 }
90 
DoState(PointerWrap & p)91 void GCIFile::DoState(PointerWrap& p)
92 {
93   p.DoPOD<DEntry>(m_gci_header);
94   p.Do(m_dirty);
95   p.Do(m_filename);
96   int num_blocks = (int)m_save_data.size();
97   p.Do(num_blocks);
98   m_save_data.resize(num_blocks);
99   for (auto itr = m_save_data.begin(); itr != m_save_data.end(); ++itr)
100   {
101     p.DoPOD<GCMBlock>(*itr);
102   }
103   p.Do(m_used_blocks);
104 }
105 }  // namespace Memcard
106