1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (config_file_test.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #include <string/stdstring.h>
32 #include <file/file_path.h>
33 #include <streams/interface_stream.h>
34 #include <streams/file_stream.h>
35 #include <streams/rzip_stream.h>
36 #include <retro_miscellaneous.h>
38 #define FILE_TRANSFER_CHUNK_SIZE 4096
42 RZIP_ACTION_QUERY = 0,
47 static void rand_str(char *dst, size_t len)
49 char charset[] = "0123456789"
50 "abcdefghijklmnopqrstuvwxyz"
51 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
55 size_t i = (double)rand() / RAND_MAX * (sizeof(charset) - 1);
61 int main(int argc, char *argv[])
63 char in_file_path[PATH_MAX_LENGTH];
64 char out_file_path[PATH_MAX_LENGTH];
65 enum rzip_action_type action = RZIP_ACTION_QUERY;
66 intfstream_t *in_file = NULL;
67 intfstream_t *out_file = NULL;
68 int64_t in_file_size = 0;
69 int64_t in_file_raw_size = 0;
70 int64_t out_file_size = 0;
71 int64_t file_size_diff = 0;
72 int64_t total_data_read = 0;
73 bool in_file_compressed = false;
74 bool valid_args = false;
75 bool in_place = false;
78 in_file_path[0] = '\0';
79 out_file_path[0] = '\0';
82 if ((argc > 1) && !string_is_empty(argv[1]))
86 if (string_is_equal(argv[1], "i"))
87 action = RZIP_ACTION_QUERY;
88 else if (string_is_equal(argv[1], "a"))
89 action = RZIP_ACTION_COMPRESS;
90 else if (string_is_equal(argv[1], "x"))
91 action = RZIP_ACTION_EXTRACT;
96 /* Get input file path */
97 if (valid_args && (argc > 2) && !string_is_empty(argv[2]))
99 strlcpy(in_file_path, argv[2], sizeof(in_file_path));
100 path_resolve_realpath(in_file_path, sizeof(in_file_path), true);
101 valid_args = valid_args && !string_is_empty(in_file_path);
106 /* Ensure arguments are valid */
109 fprintf(stderr, "Usage:\n");
110 fprintf(stderr, "- Query file status: %s i <input file>\n", argv[0]);
111 fprintf(stderr, "- Compress file: %s a <input file> <output file (optional)>\n", argv[0]);
112 fprintf(stderr, "- Extract file: %s x <input file> <output file (optional)>\n", argv[0]);
113 fprintf(stderr, "Omitting <output file> will overwrite <input file>\n");
117 /* Ensure that input file exists */
118 if (!path_is_valid(in_file_path))
120 fprintf(stderr, "ERROR: Input file does not exist: %s\n", in_file_path);
124 /* Get output file path, if specified */
125 if ((argc > 3) && !string_is_empty(argv[3]))
127 strlcpy(out_file_path, argv[3], sizeof(out_file_path));
128 path_resolve_realpath(out_file_path, sizeof(out_file_path), true);
131 /* If we are compressing/extracting and an
132 * output file was not specified, generate a
133 * temporary output file path */
134 if ((action != RZIP_ACTION_QUERY) &&
135 string_is_empty(out_file_path))
137 const char *in_file_name = path_basename(in_file_path);
138 char in_file_dir[PATH_MAX_LENGTH];
140 in_file_dir[0] = '\0';
142 fill_pathname_parent_dir(in_file_dir, in_file_path, sizeof(in_file_dir));
144 if (string_is_empty(in_file_name))
146 fprintf(stderr, "ERROR: Invalid input file: %s\n", in_file_path);
150 srand((unsigned int)time(NULL));
154 char tmp_str[10] = {0};
156 /* Generate 'random' file name */
157 rand_str(tmp_str, sizeof(tmp_str) - 1);
160 if (!string_is_empty(in_file_dir))
161 fill_pathname_join_special(out_file_path, in_file_dir,
162 tmp_str, sizeof(out_file_path));
164 strlcpy(out_file_path, tmp_str, sizeof(out_file_path));
166 strlcat(out_file_path, ".", sizeof(out_file_path));
167 strlcat(out_file_path, in_file_name, sizeof(out_file_path));
168 path_resolve_realpath(out_file_path, sizeof(out_file_path), true);
170 if (!path_is_valid(out_file_path))
177 /* Ensure that input and output files
179 if (string_is_equal(in_file_path, out_file_path))
181 fprintf(stderr, "ERROR: Input and output are the same file: %s\n", in_file_path);
185 /* Get input file size */
186 in_file_size = (int64_t)path_get_size(in_file_path);
188 if (in_file_size < 1)
190 fprintf(stderr, "ERROR: Input file is empty: %s\n", in_file_path);
195 * > Always use RZIP interface */
196 in_file = intfstream_open_rzip_file(
197 in_file_path, RETRO_VFS_FILE_ACCESS_READ);
201 fprintf(stderr, "ERROR: Failed to open input file: %s\n", in_file_path);
205 /* Get input file compression status */
206 in_file_compressed = intfstream_is_compressed(in_file);
208 /* Get raw (uncompressed) input file size */
209 in_file_raw_size = intfstream_get_size(in_file);
211 /* If this is a query operation, just
212 * print current state */
213 if (action == RZIP_ACTION_QUERY)
216 in_file_compressed ? "File is in RZIP format" : "File is NOT in RZIP format",
218 printf(" Size on disk: %" PRIi64 " bytes\n", in_file_size);
219 if (in_file_compressed)
220 printf(" Uncompressed size: %" PRIi64 " bytes\n", in_file_raw_size);
224 /* Check whether file is already in the
226 if ((in_file_compressed && (action == RZIP_ACTION_COMPRESS)) ||
227 (!in_file_compressed && (action == RZIP_ACTION_EXTRACT)))
229 printf("Input file is %s: %s\n",
231 "already in RZIP format - cannot compress" :
232 "not in RZIP format - cannot extract",
237 /* Check whether output file already exists */
238 if (path_is_valid(out_file_path))
244 printf("WARNING: Output file already exists: %s\n", out_file_path);
245 printf(" Overwrite? [Y/n]: ");
246 fgets(reply, sizeof(reply), stdin);
251 /* Open output file */
252 if (in_file_compressed)
253 out_file = intfstream_open_file(
254 out_file_path, RETRO_VFS_FILE_ACCESS_WRITE,
255 RETRO_VFS_FILE_ACCESS_HINT_NONE);
257 out_file = intfstream_open_rzip_file(
258 out_file_path, RETRO_VFS_FILE_ACCESS_WRITE);
262 fprintf(stderr, "ERROR: Failed to open output file: %s\n", out_file_path);
266 /* Start file transfer */
267 printf("%s file\n", in_file_compressed ? "Extracting" : "Compressing");
268 printf(" From: %s\n", in_file_path);
269 printf(" To: %s\n", in_place ? in_file_path : out_file_path);
273 int64_t data_written = 0;
274 uint8_t buffer[FILE_TRANSFER_CHUNK_SIZE];
275 /* Read a single chunk from input file */
276 int64_t data_read = intfstream_read(
277 in_file, buffer, sizeof(buffer));
281 fprintf(stderr, "ERROR: Failed to read from input file: %s\n", in_file_path);
285 total_data_read += data_read;
287 /* Check whether we have reached the end of the file */
291 intfstream_flush(out_file);
292 intfstream_close(out_file);
296 intfstream_close(in_file);
303 /* Write chunk to backup file */
304 data_written = intfstream_write(out_file, buffer, data_read);
306 if (data_written != data_read)
308 fprintf(stderr, "ERROR: Failed to write to output file: %s\n", out_file_path);
312 /* Update progress */
313 printf("\rProgress: %" PRIi64 " %%", total_data_read * 100 / in_file_raw_size);
316 printf("\rProgress: 100 %%\n");
318 /* Display final status 'report' */
319 printf("%s complete:\n", in_file_compressed ? "Extraction" : "Compression");
321 out_file_size = (int64_t)path_get_size(out_file_path);
322 file_size_diff = (in_file_size > out_file_size) ?
323 (in_file_size - out_file_size) :
324 (out_file_size - in_file_size);
326 printf(" %" PRIi64 " -> %" PRIi64 " bytes [%" PRIi64 " %% %s]\n",
327 in_file_size, out_file_size,
328 file_size_diff * 100 / in_file_size,
329 (out_file_size >= in_file_size) ?
330 "increase" : "decrease");
332 /* If this was an in-place operation,
333 * replace input file with output file */
336 filestream_delete(in_file_path);
337 if (filestream_rename(out_file_path, in_file_path))
339 fprintf(stderr, "ERROR: Failed to rename temporary file\n");
340 fprintf(stderr, " From: %s\n", out_file_path);
341 fprintf(stderr, " To: %s\n", in_file_path);
351 intfstream_close(in_file);
357 intfstream_close(out_file);