1 #include "load.h"
2 
3 #include "nes/cartridge/rom_file.h"
4 #include "nes/cartridge/parse_rom.h"
5 
6 #include <fstream>
7 #include <iostream>
8 #include <string>
9 
10 #include <miniz_zip.h>
11 
12 /*----------  Utlities  ----------*/
13 
14 #include <algorithm>
get_file_ext(const char * filename)15 static inline std::string get_file_ext(const char* filename) {
16   std::string filename_str (filename);
17   std::string ext = filename_str.substr(filename_str.find_last_of("."));
18   std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
19   return ext;
20 }
21 
22 /*----------  File Data Loaders  ----------*/
23 
24 // Loads file directly into memory
load_file(const char * filepath,u8 * & data,uint & data_len)25 bool ANESE_fs::load::load_file(const char* filepath, u8*& data, uint& data_len) {
26   if (!filepath) {
27     fprintf(stderr, "[Load] filepath == nullptr in load_file!\n");
28     assert(false);
29   }
30 
31   std::ifstream rom_file (std::string(filepath), std::ios::binary);
32 
33   if (!rom_file.is_open()) {
34     fprintf(stderr, "[Load] Could not open '%s'\n", filepath);
35     return false;
36   }
37 
38   // get length of file
39   rom_file.seekg(0, rom_file.end);
40   std::streamoff rom_file_size = rom_file.tellg();
41   rom_file.seekg(0, rom_file.beg);
42 
43   if (rom_file_size == -1) {
44     fprintf(stderr, "[Load] Could not read '%s'\n", filepath);
45     return false;
46   }
47 
48   data_len = rom_file_size;
49   data = new u8 [data_len];
50   rom_file.read((char*) data, data_len);
51 
52   fprintf(stderr, "[Load] Successfully read '%s'\n", filepath);
53 
54   return true;
55 }
56 
57 // Searches for valid roms inside .zip files, and loads them into memory
load_zipped_nes_file(const char * filepath,u8 * & data,uint & data_len)58 static bool load_zipped_nes_file(const char* filepath, u8*& data, uint& data_len) {
59   if (!filepath) {
60     fprintf(stderr, "[Load] filepath == nullptr in load_zipped_nes_file!\n");
61     assert(false);
62   }
63 
64   mz_zip_archive zip_archive;
65   memset(&zip_archive, 0, sizeof zip_archive);
66   mz_bool status = mz_zip_reader_init_file(
67     &zip_archive,
68     filepath,
69     0
70   );
71   if (!status) {
72     fprintf(stderr, "[Load][.zip] Could not read '%s'\n", filepath);
73     mz_zip_reader_end(&zip_archive);
74     return false;
75   }
76 
77   // Try to find a .nes file in the archive
78   for (uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) {
79     mz_zip_archive_file_stat file_stat;
80     mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
81 
82     std::string file_ext = get_file_ext(file_stat.m_filename);
83 
84     if (file_ext == ".nes") {
85       fprintf(stderr, "[Load][.zip][UnZip] Found .nes file in archive: '%s'\n",
86         file_stat.m_filename);
87 
88       size_t uncomp_size;
89       void* p = mz_zip_reader_extract_file_to_heap(
90         &zip_archive,
91         file_stat.m_filename,
92         &uncomp_size,
93         0
94       );
95 
96       if (!p) {
97         fprintf(stderr, "[Load][.zip][UnZip] Could not decompress '%s'\n",
98           filepath);
99         mz_free(p);
100         mz_zip_reader_end(&zip_archive);
101         return false;
102       }
103 
104       // Nice! We got data!
105       data_len = uncomp_size;
106       data = new u8 [data_len];
107       memcpy(data, p, data_len);
108 
109       fprintf(stderr, "[Load][.zip] Successfully read '%s'\n", filepath);
110 
111       // Free the redundant decompressed file mem
112       mz_free(p);
113 
114       // Close the archive, freeing any resources it was using
115       mz_zip_reader_end(&zip_archive);
116     }
117   }
118 
119   return true;
120 }
121 
122 // Given a filepath, tries to open and parse it as a NES ROM.
123 // Returns a valid ROM_File, or a nullptr if something went wrong
load_rom_file(const char * filepath)124 ROM_File* ANESE_fs::load::load_rom_file(const char* filepath) {
125   if (!filepath) {
126     fprintf(stderr, "[Load] filepath == nullptr in load_rom_file!\n");
127     assert(false);
128   }
129 
130   // Be lazy, and just load the entire file into memory
131   u8*  data = nullptr;
132   uint data_len = 0;
133 
134   std::string rom_ext = get_file_ext(filepath);
135   /**/ if (rom_ext == ".nes") load_file(filepath, data, data_len);
136   else if (rom_ext == ".zip") load_zipped_nes_file(filepath, data, data_len);
137   else {
138     fprintf(stderr, "[Load] Invalid file extension.\n");
139     return nullptr;
140   }
141 
142   return parseROM(data, data_len);
143 }
144