1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <memory>
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "Common/Assert.h"
12 #include "Common/FileUtil.h"
13 #include "Common/MsgHandler.h"
14 #include "DiscIO/FileBlob.h"
15 
16 namespace DiscIO
17 {
PlainFileReader(File::IOFile file)18 PlainFileReader::PlainFileReader(File::IOFile file) : m_file(std::move(file))
19 {
20   m_size = m_file.GetSize();
21 }
22 
Create(File::IOFile file)23 std::unique_ptr<PlainFileReader> PlainFileReader::Create(File::IOFile file)
24 {
25   if (file)
26     return std::unique_ptr<PlainFileReader>(new PlainFileReader(std::move(file)));
27 
28   return nullptr;
29 }
30 
Read(u64 offset,u64 nbytes,u8 * out_ptr)31 bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
32 {
33   if (m_file.Seek(offset, SEEK_SET) && m_file.ReadBytes(out_ptr, nbytes))
34   {
35     return true;
36   }
37   else
38   {
39     m_file.Clear();
40     return false;
41   }
42 }
43 
ConvertToPlain(BlobReader * infile,const std::string & infile_path,const std::string & outfile_path,CompressCB callback)44 bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
45                     const std::string& outfile_path, CompressCB callback)
46 {
47   ASSERT(infile->IsDataSizeAccurate());
48 
49   File::IOFile outfile(outfile_path, "wb");
50   if (!outfile)
51   {
52     PanicAlertT("Failed to open the output file \"%s\".\n"
53                 "Check that you have permissions to write the target folder and that the media can "
54                 "be written.",
55                 outfile_path.c_str());
56     return false;
57   }
58 
59   constexpr size_t DESIRED_BUFFER_SIZE = 0x80000;
60   u64 buffer_size = infile->GetBlockSize();
61   if (buffer_size == 0)
62   {
63     buffer_size = DESIRED_BUFFER_SIZE;
64   }
65   else
66   {
67     while (buffer_size < DESIRED_BUFFER_SIZE)
68       buffer_size *= 2;
69   }
70 
71   std::vector<u8> buffer(buffer_size);
72   const u64 num_buffers = (infile->GetDataSize() + buffer_size - 1) / buffer_size;
73   int progress_monitor = std::max<int>(1, num_buffers / 100);
74   bool success = true;
75 
76   for (u64 i = 0; i < num_buffers; i++)
77   {
78     if (i % progress_monitor == 0)
79     {
80       const bool was_cancelled =
81           !callback(Common::GetStringT("Unpacking"), (float)i / (float)num_buffers);
82       if (was_cancelled)
83       {
84         success = false;
85         break;
86       }
87     }
88     const u64 inpos = i * buffer_size;
89     const u64 sz = std::min(buffer_size, infile->GetDataSize() - inpos);
90     if (!infile->Read(inpos, sz, buffer.data()))
91     {
92       PanicAlertT("Failed to read from the input file \"%s\".", infile_path.c_str());
93       success = false;
94       break;
95     }
96     if (!outfile.WriteBytes(buffer.data(), sz))
97     {
98       PanicAlertT("Failed to write the output file \"%s\".\n"
99                   "Check that you have enough space available on the target drive.",
100                   outfile_path.c_str());
101       success = false;
102       break;
103     }
104   }
105 
106   if (!success)
107   {
108     // Remove the incomplete output file.
109     outfile.Close();
110     File::Delete(outfile_path);
111   }
112 
113   return success;
114 }
115 
116 }  // namespace DiscIO
117