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