Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
emutools_files.c
Go to the documentation of this file.
1 /* Xemu - emulation (running on Linux/Unix/Windows/OSX, utilizing SDL2) of some
2  * 8 bit machines, including the Commodore LCD and Commodore 65 and MEGA65 as well.
3  Copyright (C)2016-2022 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 
20 #include "xemu/emutools.h"
21 #include "xemu/emutools_files.h"
22 
23 #include <string.h>
24 #include <stdlib.h>
25 #include <limits.h>
26 #include <errno.h>
27 
28 
29 #ifdef XEMU_ARCH_WIN
30 #include <windows.h>
31 #endif
32 
33 
35 char xemu_load_filepath[PATH_MAX];
36 
37 
38 #ifdef HAVE_XEMU_EXEC_API
39 #ifndef XEMU_ARCH_WIN
40 #include <sys/wait.h>
41 
42 int xemuexec_run ( char *const args[] )
43 {
44  pid_t pid = fork();
45  if (pid == -1) {
46  DEBUGPRINT("EXEC: fork() failed: %s" NL, strerror(errno));
47  return -1; // fork failed?
48  }
49  if (!pid) { // the child's execution process after fork()
50  int a;
51  for(a = 3; a < 1024; a++)
52  close(a);
53  close(0);
54  execvp(args[0], args);
55  // exec won't return in case if it's OK. so if we're still here, there was a problem with the exec func ...
56  printf("EXEC: execution of \"%s\" failed: %s" NL, args[0], strerror(errno));
57  _exit(127); // important to call this and not plain exit(), as we don't want to run atexit() registered stuffs and so on!!!
58  }
59  return pid; // the parent's execution process after fork()
60 }
61 
62 
63 int xemuexec_check_status ( pid_t pid, int wait )
64 {
65  int status;
66  pid_t ret;
67  if (pid <= 0)
68  return 127;
69  do {
70  ret = waitpid(pid, &status, wait ? 0 : WNOHANG);
71  if (ret == -1) {
72  if (errno == EINTR)
73  continue;
74  DEBUGPRINT("EXEC: WAIT: waitpid(%d) returned error: %s" NL, pid, strerror(errno));
75  return -1;
76  }
77  } while (ret < 0);
78  if (ret != pid) {
79  DEBUGPRINT("EXEC: still running" NL);
80  return XEMUEXEC_STILL_RUNNING; // still running?
81  }
82  if (WIFEXITED(status))
83  return WEXITSTATUS(status);
84  return 127; // we have not so much a standard status (not exited normally?!) so fake one ...
85 }
86 
87 #else
88 #include <tchar.h>
89 
90 /* I'm not a Windows programmer not even a user ... By inpsecting MSDN articles, I am not sure, what needs
91  * to be kept to be able to query the process later. So I keep about everything, namely this struct, malloc()'ed.
92  * Though to be able to ratioanalize the prototype of functions (no need for include windows.h all the time by
93  * callers too ...) the data type is a void* pointer instead of this madness "externally" ... */
94 
95 struct ugly_windows_ps_t {
96  PROCESS_INFORMATION pi;
97  STARTUPINFO si;
98  LPTSTR cmdline;
99  DWORD creationstatus;
100 };
101 
102 void *xemuexec_run ( char *const args[] )
103 {
104  char cmdline[1024];
105  int cmdlinesize = sizeof cmdline;
106  struct ugly_windows_ps_t *st = malloc(sizeof(struct ugly_windows_ps_t));
107  if (!st)
108  FATAL("exec: cannot allocate memory");
109  ZeroMemory(st, sizeof(struct ugly_windows_ps_t));
110  st->si.cb = sizeof(STARTUPINFO);
111  st->cmdline = NULL;
112  if (snprintf(cmdline, cmdlinesize,"\"%s\"", args[0]) != strlen(args[0]) + 2)
113  FATAL("exec: too long commandline");
114  cmdlinesize -= strlen(args[0]) + 2;
115  while (*(++args)) {
116  int arg_padding_len = strchr(*args, ' ') ? 3 : 1;
117  if (cmdlinesize <= 0)
118  FATAL("exec: too long commandline");
119  if (snprintf(cmdline + strlen(cmdline), cmdlinesize, arg_padding_len == 1 ? " %s" : " \"%s\"", *args) != strlen(*args) + arg_padding_len)
120  FATAL("exec: too long commandline");
121  cmdlinesize -= strlen(*args) + arg_padding_len;
122  }
123  st->cmdline = _tcsdup(TEXT(cmdline)); // really no idea about this windows madness, just copying examples ...
124  if (!st->cmdline) {
125  free(st);
126  FATAL("exec: cannot allocate memory");
127  }
128  // TODO: figure out what I should have for std handles to do and inheritance for the "child" process
129 #if 0
130  //st->si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
131  st->si.hStdError = (HANDLE)_open_osfhandle(_fileno(stderr), _O_TEXT);
132  st->si.hStdOutput = (HANDLE)_open_osfhandle(_fileno(stdout), _O_TEXT);
133  //_open_osfhandle((INT_PTR)_fileno(stdout), _O_TEXT);
134  //GetStdHandle(STD_OUTPUT_HANDLE);
135  //st->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
136  st->si.hStdInput = (HANDLE)_get_osfhandle(fileno(stdin));
137  //st->si.dwFlags |= STARTF_USESTDHANDLES;
138  SetHandleInformation(st->si.hStdError, HANDLE_FLAG_INHERIT, 0);
139  SetHandleInformation(st->si.hStdOutput, HANDLE_FLAG_INHERIT, 0);
140  SetHandleInformation(st->si.hStdInput, HANDLE_FLAG_INHERIT, 0);
141 #endif
142  if (CreateProcess(NULL,
143  st->cmdline,
144  NULL, // process handle not inheritable
145  NULL, // thread handle not inheritable
146  FALSE, // set handle inheritance
147  0, // no creation flags
148  NULL, // use parent's env. block
149  NULL, // use parent's starting directory
150  &st->si, // startup-info structure pointer
151  &st->pi // process-info structure pointer
152  )) { // Windows does this differently as well compared to others: non-zero value means OKEY ....
153  st->creationstatus = 0;
154  DEBUGPRINT("EXEC: (%s) seems to be OK :-)" NL, cmdline);
155  } else {
156  st->creationstatus = GetLastError();
157  DEBUGPRINT("EXEC: (%s) failed with %d" NL, cmdline, (int)st->creationstatus);
158  if (!st->creationstatus) { // I am not sure what Windows fumbles for, even MSDN is quite lame without _exact_ specification (MS should learn from POSIX dox ...)
159  st->creationstatus = 1;
160  }
161  }
162  //CloseHandle(st->si.hStdError);
163  //CloseHandle(st->si.hStdOutput);
164  //CloseHandle(st->si.hStdInput);
165  return st;
166 }
167 
168 
169 static void free_exec_struct_win32 ( struct ugly_windows_ps_t *st )
170 {
171  if (!st)
172  return;
173  if (st->creationstatus) {
174  CloseHandle(st->pi.hProcess);
175  CloseHandle(st->pi.hThread);
176  }
177  if (st->cmdline)
178  free(st->cmdline);
179  free(st);
180 }
181 
182 
183 #define PID ((struct ugly_windows_ps_t *)pid)
184 int xemuexec_check_status ( void* pid, int wait )
185 {
186  if (!pid)
187  return 127;
188  if (PID->creationstatus) {
189  DEBUGPRINT("EXEC: WAIT: returning because of deferred creationstatus(%d) != 0 situation" NL, (int)PID->creationstatus);
190  free_exec_struct_win32(PID);
191  return 127;
192  } else {
193  DWORD result = 0;
194  do {
195  if (!result)
196  usleep(10000);
197  GetExitCodeProcess(PID->pi.hProcess, &result); // TODO: check return value if GetExitCodeProcess() was OK at all!
198  } while (wait && result == STILL_ACTIVE);
199  if (result == STILL_ACTIVE)
200  return XEMUEXEC_STILL_RUNNING;
201  free_exec_struct_win32(PID);
202  return result;
203  }
204 }
205 #undef PID
206 
207 // end of #ifndef XEMU_ARCH_WIN
208 #endif
209 
211 {
212 #ifdef HAVE_XEMU_EXEC_API
213  static xemuexec_process_t fbp = XEMUEXEC_NULL_PROCESS_ID;
214  while (*dir == ' ' || *dir == '\t')
215  dir++;
216  if (!strncasecmp(dir, "file://", 7))
217  dir += 7;
218  char *args[] = {
219  FILE_BROWSER, dir, NULL
220 #ifdef XEMU_ARCH_WIN
221  ,
222  NULL,
223  NULL
224 #endif
225  };
226  if (!strncasecmp(dir, "ftp://", 6) || !strncasecmp(dir, "http://", 7) || !strncasecmp(dir, "https://", 8)) {
227 #ifdef XEMU_ARCH_WIN
228  args[0] = "cmd";
229  args[1] = "/c";
230  args[2] = "start";
231  args[3] = dir;
232 #else
233  args[0] = WEB_BROWSER;
234 #endif
235  }
236  if (fbp != XEMUEXEC_NULL_PROCESS_ID) {
237  int w = xemuexec_check_status(fbp, 0);
238  DEBUGPRINT("EXEC: FILEBROWSER: previous file browser process (" PRINTF_LLD ") status was: %d" NL, (unsigned long long int)(uintptr_t)fbp, w);
239  if (w == XEMUEXEC_STILL_RUNNING)
240  ERROR_WINDOW("A file browser is already has been opened.");
241  else if (w == -1)
242  ERROR_WINDOW("Process communication problem");
243  else
244  fbp = XEMUEXEC_NULL_PROCESS_ID;
245  }
246  if (XEMU_LIKELY(fbp == XEMUEXEC_NULL_PROCESS_ID))
247  fbp = xemuexec_run(args); // FIXME: process on exit will be "orphaned" (ie zombie) till exit from Xemu, because it won't be wait()'ed by the parent (us) ...
248 #else
249  ERROR_WINDOW("Sorry, no execution API is supported by this Xemu build\nto allow to launch an OS-native file browser for you on directory:\n%s", dir);
250 #endif
251 }
252 
253 
254 #ifdef HAVE_XEMU_INSTALLER
255 
256 char *xemu_installer_db = NULL;
257 
258 static const char installer_marker_prefix[] = "[XEMU_DOWNLOADER]=";
259 
260 static char installer_store_to[PATH_MAX];
261 static char installer_fetch_url[256];
262 static const char *downloader_utility_specifications[] = {
263 #ifdef XEMU_ARCH_WIN
264  "powershell", "-Command", "Invoke-WebRequest", installer_fetch_url, "-OutFile", installer_store_to, "-Verbose", NULL,
265  "powershell.exe", "-Command", "Invoke-WebRequest", installer_fetch_url, "-OutFile", installer_store_to, "-Verbose", NULL,
266 #endif
267  "curl", "-o", installer_store_to, installer_fetch_url, NULL,
268 #ifdef XEMU_ARCH_WIN
269  "curl.exe", "-o", installer_store_to, installer_fetch_url, NULL,
270 #endif
271  "wget", "-O", installer_store_to, installer_fetch_url, NULL,
272 #ifdef XEMU_ARCH_WIN
273  "wget.exe", "-O", installer_store_to, installer_fetch_url, NULL,
274 #endif
275  NULL // the final stop of listing
276 };
277 
278 static const char **downloader_utility_spec_start_p = downloader_utility_specifications;
279 static int downloader_utility_selected = 0;
280 static int legal_warning = 1;
281 
282 
283 
284 static int download_file ( int size )
285 {
286  int ret;
287  char path_final[PATH_MAX];
288  const char **execargs_p = downloader_utility_spec_start_p;
289  struct stat st;
290  strcpy(path_final, installer_store_to);
291  strcat(installer_store_to, ".temp");
292  DEBUGPRINT("INSTALLER: goal: %s -> %s -> %s (%d bytes)" NL,
293  installer_fetch_url, installer_store_to, path_final, size
294  );
295  if (unlink(path_final) && errno != ENOENT) {
296  ERROR_WINDOW("Installer: cannot delete already existing final file");
297  return -1;
298  }
299  do {
300  xemuexec_process_t proc;
301  printf("Tryning: %s\n", *execargs_p);
302  if (unlink(installer_store_to) && errno != ENOENT) {
303  ERROR_WINDOW("Installer: cannot delete already exisiting temp file");
304  return -1;
305  }
306  proc = xemuexec_run((char* const*)execargs_p);
307  ret = xemuexec_check_status(proc, 1);
308  printf("Exit status: %d\n", ret);
309  if (!ret || downloader_utility_selected)
310  break;
311  while (*(execargs_p++))
312  ;
313  } while (*execargs_p);
314  if (ret) {
315  ERROR_WINDOW("Installer: cannot download and/or no working download utility can be used");
316  unlink(installer_store_to);
317  return -1;
318  }
319  if (stat(installer_store_to, &st)) {
320  ERROR_WINDOW("Installer: cannot stat file (not downloaded at all?)");
321  return -1;
322  }
323  printf("File size = %d\n", (int)st.st_size);
324  if (st.st_size != size) {
325  unlink(installer_store_to);
326  ERROR_WINDOW("Installer: downloaded file has wrong size (%d, wanted: %d)", (int)st.st_size, size);
327  return -1;
328  }
329  if (rename(installer_store_to, path_final)) {
330  ERROR_WINDOW("Installer: cannot rename to final");
331  return -1;
332  }
333  if (!downloader_utility_selected) {
334  downloader_utility_spec_start_p = execargs_p;
335  downloader_utility_selected = 1;
336  DEBUGPRINT("INSTALLER: setting \"%s\" as the default downloader utility for this session." NL, *execargs_p);
337  }
338  return 0;
339 }
340 
341 
342 
343 static int download_file_by_db ( const char *filename, const char *storepath )
344 {
345  char *p;
346  int i;
347  if (!xemu_installer_db)
348  return -1;
349  strcpy(installer_store_to, storepath);
350  p = xemu_installer_db;
351  i = strlen(filename);
352  while (*p) {
353  while (*p && *p <= 32)
354  p++;
355  if (!strncmp(filename, p, i) && (p[i] == '\t' || p[i] == ' ')) {
356  p += i + 1;
357  while (*p == ' ' || *p == '\t')
358  p++;
359  if (*p > 32) {
360  long int sizereq;
361  char *q;
362  if (strncasecmp(p, "http://", 7) && strncasecmp(p, "https://", 8) && strncasecmp(p, "ftp://", 6)) {
363  ERROR_WINDOW("Bad download descriptor file at URL field (bar protocol) for record \"%s\"", filename);
364  return -1;
365  }
366  q = installer_fetch_url;
367  sizereq = 0;
368  while (*p > 32) {
369  if (sizereq == sizeof(installer_fetch_url) - 2) {
370  ERROR_WINDOW("Bad download descriptor file at URL field (too long) for record \"%s\"", filename);
371  return -1;
372  }
373  *q++ = *p++;
374  sizereq++;
375  }
376  *q = 0;
377  sizereq = strtol(p, &p, 0);
378  if (*p > 32)
379  sizereq = -1;
380  if (sizereq > 0 && sizereq <= 4194304) {
381  int ret;
382  char msgbuffer[sizeof(installer_fetch_url) + 256];
383  if (legal_warning) {
384  INFO_WINDOW("Legal-warning blah-blah ...");
385  legal_warning = 0;
386  }
387  sprintf(msgbuffer, "Downloading file \"%s\". Do you agree?\nSource: %s", filename, installer_fetch_url);
388  if (QUESTION_WINDOW("YES|NO", msgbuffer))
389  return -1;
390  ret = download_file(sizereq);
391  if (!ret)
392  INFO_WINDOW("File %s seems to be downloaded nicely with %s", filename, *downloader_utility_spec_start_p);
393  return ret;
394  } else {
395  ERROR_WINDOW("Bad download descriptor file at size field for record \"%s\" (or this file is not auto-installable)", filename);
396  return -1;
397  }
398  }
399  }
400  while (*p && *p != '\n' && *p != '\r')
401  p++;
402  }
403  DEBUGPRINT("INSTALLER: file-key %s cannot be found in the download description file" NL, filename);
404  return -1;
405 }
406 
407 
408 void xemu_set_installer ( const char *filename )
409 {
410  if (xemu_installer_db) {
411  free(xemu_installer_db);
412  xemu_installer_db = NULL;
413  }
414  if (filename) {
415  int ret = xemu_load_file(filename, NULL, 32, 65535, "Specified installer-description file cannot be loaded");
416  if (ret > 0) {
417  xemu_installer_db = xemu_load_buffer_p;
418  DEBUGPRINT("INSTALLER: description file loaded, %d bytes. Parsing will be done only on demand." NL, ret);
419  }
420  } else
421  DEBUGPRINT("INSTALLER: not activated." NL);
422 }
423 
424 
425 // end of #ifdef HAVE_XEMU_INSTALLER
426 #endif
427 
428 // end of #ifdef HAVE_XEMU_EXEC_API - else ...
429 #else
430 
432 {
433  ERROR_WINDOW("Sorry, no execution API is supported by this Xemu build\nto allow to launch an OS-native file browser for you on directory:\n%s", dir);
434 }
435 
436 // end of #ifdef HAVE_XEMU_EXEC_API - all
437 #endif
438 
439 
440 
441 
469 int xemu_open_file ( const char *filename, int mode, int *mode2, char *filepath_back )
470 {
471  char paths[16][PATH_MAX];
472  int a, max_paths;
473  if (!filename)
474  FATAL("Calling xemu_open_file() with NULL filename!");
475  if (!*filename)
476  FATAL("Calling xemu_open_file() with empty filename!");
477  max_paths = 0;
478 #ifdef XEMU_ARCH_HTML
479  sprintf(paths[max_paths++], "%s%s", EMSCRIPTEN_SDL_BASE_DIR, (filename[0] == '@' || filename[0] == '#') ? filename + 1 : filename);
480 #else
481  if (*filename == '@') {
482  sprintf(paths[max_paths++], "%s%s", sdl_pref_dir, filename + 1);
483  } else if (*filename == '#') {
484  sprintf(paths[max_paths++], "%s%s", sdl_inst_dir, filename + 1);
485  sprintf(paths[max_paths++], "%s%s", sdl_pref_dir, filename + 1);
486  sprintf(paths[max_paths++], "%srom" DIRSEP_STR "%s", sdl_base_dir, filename + 1);
487  sprintf(paths[max_paths++], "%s%s", sdl_base_dir, filename + 1);
488 #ifndef XEMU_ARCH_WIN
489  sprintf(paths[max_paths++], UNIX_DATADIR_0 "/%s", filename + 1);
490  sprintf(paths[max_paths++], UNIX_DATADIR_1 "/%s", filename + 1);
491  sprintf(paths[max_paths++], UNIX_DATADIR_2 "/%s", filename + 1);
492  sprintf(paths[max_paths++], UNIX_DATADIR_3 "/%s", filename + 1);
493 #endif
494 #ifdef HAVE_XEMU_INSTALLER
495  sprintf(paths[max_paths++], "%s%s%s", installer_marker_prefix, sdl_inst_dir, filename + 1);
496 #endif
497  } else
498  strcpy(paths[max_paths++], filename);
499 // End of #ifdef XEMU_ARCH_HTML
500 #endif
501  a = 0;
502  do {
503  int fd;
504  const char *filepath = paths[a];
505 #ifdef HAVE_XEMU_INSTALLER
506  if (!strncmp(paths[a], installer_marker_prefix, strlen(installer_marker_prefix))) {
507  filepath += strlen(installer_marker_prefix);
508  download_file_by_db(filename + 1, filepath);
509  }
510 #endif
511  // Notes:
512  // 1. O_BINARY is a windows stuff. However, since we define O_BINARY as zero for non-win archs, it's OK
513  // 2. 0666 mask is needed as it can be also a creat() function basically ...
514  fd = open(filepath, mode | O_BINARY, 0666);
515  if (filepath_back)
516  strcpy(filepath_back, filepath);
517  if (fd >= 0) {
518  if (mode2)
520  DEBUGPRINT("FILE: file %s opened as %s with base mode-set as fd=%d" NL, filename, paths[a], fd);
521  return fd;
522  }
523  if (mode2) {
524  fd = open(filepath, *mode2 | O_BINARY, 0666); // please read the comments at the previous open(), above
525  if (fd >= 0) {
526  DEBUGPRINT("FILE: file %s opened as %s with *second* mode-set as fd=%d" NL, filename, paths[a], fd);
527  return fd;
528  }
529  }
530  } while (++a < max_paths);
531  // if not found, copy the first try so the report to user is more sane
532  if (filepath_back)
533  strcpy(filepath_back, paths[0]);
534  DEBUGPRINT("FILE: %s cannot be open, tried path(s): ", filename);
535  for (a = 0; a < max_paths; a++)
536  DEBUGPRINT(" %s", paths[a]);
537  DEBUGPRINT(NL);
538  return -1;
539 }
540 
541 
542 
543 ssize_t xemu_safe_read ( int fd, void *buffer, size_t length )
544 {
545  ssize_t loaded = 0;
546  while (length > 0) {
547  ssize_t r = read(fd, buffer, length);
548  if (r < 0) { // I/O error on read
549  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
550  continue;
551  return -1;
552  }
553  if (r == 0) // end of file
554  break;
555  loaded += r;
556  length -= r;
557  buffer += r;
558  }
559  return loaded;
560 }
561 
562 
563 ssize_t xemu_safe_write ( int fd, const void *buffer, size_t length )
564 {
565  ssize_t saved = 0;
566  while (length > 0) {
567  ssize_t w = write(fd, buffer, length);
568  if (w < 0) { // I/O error on write
569  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
570  continue;
571  return -1;
572  }
573  if (w == 0) // to avoid endless loop, if no data could be written more
574  break;
575  saved += w;
576  length -= w;
577  buffer += w;
578  }
579  return saved;
580 }
581 
582 
584 {
585  struct stat st;
586  if (fstat(fd, &st))
587  return OFF_T_ERROR;
588  return st.st_size;
589 }
590 
591 
592 off_t xemu_safe_file_size_by_name ( const char *name )
593 {
594  struct stat st;
595  if (stat(name, &st))
596  return OFF_T_ERROR;
597  return st.st_size;
598 }
599 
600 int xemu_safe_close ( int fd )
601 {
602  return close(fd);
603 }
604 
605 
606 #ifdef XEMU_ARCH_WIN
607 #endif
608 
609 
610 int xemu_safe_open ( const char *fn, int flags )
611 {
612  return open(fn, flags | O_BINARY);
613 }
614 
615 
616 int xemu_safe_open_with_mode ( const char *fn, int flags, int mode )
617 {
618  return open(fn, flags | O_BINARY, mode);
619 }
620 
621 
622 int xemu_save_file ( const char *filename_in, void *data, int size, const char *cry )
623 {
624  static const char temp_end[] = ".TMP";
625  char filename[PATH_MAX];
626  char filename_real[PATH_MAX];
627  strcpy(filename, filename_in);
628  strcat(filename, temp_end);
629  int fd = xemu_open_file(filename, O_WRONLY | O_TRUNC | O_CREAT, NULL, filename_real);
630  if (fd < 0) {
631  if (cry)
632  ERROR_WINDOW("%s\nCannot create file: %s\n%s", cry, filename, strerror(errno));
633  return -1;
634  }
635  if (xemu_safe_write(fd, data, size) != size) {
636  if (cry)
637  ERROR_WINDOW("%s\nCannot write %d bytes into file: %s\n%s", cry, size, filename_real, strerror(errno));
638  close(fd);
639  unlink(filename_real);
640  return -1;
641  }
642  close(fd);
643  DEBUGPRINT("FILE: %d bytes saved into file: %s" NL, size, filename);
644  char filename_real2[PATH_MAX];
645  strcpy(filename_real2, filename_real);
646  filename_real2[strlen(filename_real2) - strlen(temp_end)] = 0;
647  DEBUGPRINT("FILE: renaming file: %s -> %s" NL, filename_real, filename_real2);
648 #ifdef XEMU_ARCH_WIN
649  unlink(filename_real2); // it seems windows does not allow to rename "onto" an existing file. So delete the target first ...
650 #endif
651  if (rename(filename_real, filename_real2)) {
652  if (cry)
653  ERROR_WINDOW("%s\nCannot rename file %s to %s\n%s", cry, filename_real, filename_real2, strerror(errno));
654  unlink(filename_real);
655  return -1;
656  }
657  return 0;
658 }
659 
660 
661 /* Loads a file, probably ROM image etc. It uses xemu_open_file() - see above - for opening it.
662  * Return value:
663  * - non-negative: given mumber of bytes loaded
664  * - negative, error: -1 file open error, -2 file read error, -3 limit constraint violation
665  * Input parameters:
666  * * filename: see comments at xemu_open_file()
667  * * store_to: pointer to the store buffer
668  * - note: the buffer will be filled only in case of success, no partial modification can be
669  * - if store_to is NULL, then the load buffer is NOT free'd and the buffer pointer is assigned to xemu_load_buffer_p
670  * * min_size,max_size: in bytes, the minimal needed and the maximal allowed file size (can be the same)
671  * - note: limit contraint violation, if this does not meet during the read ("load") process
672  * * cry: character message, to 'cry' (show an error window) in case of a problem. if NULL = no dialog box
673  */
674 int xemu_load_file ( const char *filename, void *store_to, int min_size, int max_size, const char *cry )
675 {
676  int fd = xemu_open_file(filename, O_RDONLY, NULL, xemu_load_filepath);
677  if (fd < 0) {
678  if (cry) {
679  ERROR_WINDOW("Cannot open file requested by %s: %s\nTried as: %s\n%s%s", filename, strerror(errno), xemu_load_filepath,
680  (*filename == '#') ? "(# prefixed, multiple paths also tried)\n" : "",
681  cry
682  );
683  }
684  return -1;
685  } else {
686  int load_size;
687  xemu_load_buffer_p = xemu_malloc(max_size + 1); // try to load one byte more than the max allowed, to detect too large file scenario
688  load_size = xemu_safe_read(fd, xemu_load_buffer_p, max_size + 1);
689  if (load_size < 0) {
690  ERROR_WINDOW("Cannot read file %s: %s\n%s", xemu_load_filepath, strerror(errno), cry ? cry : "");
691  free(xemu_load_buffer_p);
692  xemu_load_buffer_p = NULL;
693  close(fd);
694  return -2;
695  }
696  close(fd);
697  if (load_size < min_size) {
698  free(xemu_load_buffer_p);
699  xemu_load_buffer_p = NULL;
700  if (cry)
701  ERROR_WINDOW("File (%s) is too small (%d bytes), %d bytes needed.\n%s", xemu_load_filepath, load_size, min_size, cry);
702  else
703  DEBUGPRINT("FILE: file (%s) is too small (%d bytes), %d bytes needed." NL, xemu_load_filepath, load_size, min_size);
704  return -3;
705  }
706  if (load_size > max_size) {
707  free(xemu_load_buffer_p);
708  xemu_load_buffer_p = NULL;
709  if (cry)
710  ERROR_WINDOW("File (%s) is too large, larger than %d bytes.\n%s", xemu_load_filepath, max_size, cry);
711  else
712  DEBUGPRINT("FILE: file (%s) is too large, larger than %d bytes needed." NL, xemu_load_filepath, max_size);
713  return -3;
714  }
715  if (store_to) {
716  memcpy(store_to, xemu_load_buffer_p, load_size);
717  free(xemu_load_buffer_p);
718  xemu_load_buffer_p = NULL;
719  } else
721  DEBUGPRINT("FILE: %d bytes loaded from file: %s" NL, load_size, xemu_load_filepath);
722  return load_size;
723  }
724 }
725 
726 
727 int xemu_create_large_empty_file ( const char *os_path, Uint64 size, int is_sparse )
728 {
729  int error;
730  int fd = open(os_path, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0600);
731  if (fd < 0)
732  goto error;
733 #ifdef XEMU_ARCH_WIN
734  if (is_sparse) {
735  DWORD dwTemp;
736  if (DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL) == 0) {
737  ERROR_WINDOW("Cannot set file as sparse file!\nWindows error #" PRINTF_LLU "\nIt's not a fatal problem, though the file will take much more space than usual", (unsigned long long int)GetLastError());
738  goto error;
739  } else
740  DEBUGPRINT("WINDOWS: file has been made sparse, lpBytesReturned=" PRINTF_LLU NL, (unsigned long long int)dwTemp);
741  } else
742  DEBUGPRINT("WINDOWS: not using sparse file ..." NL);
743 #else
744  is_sparse = 0; // on non-Windows architectures we simply don't deal with sparse, that's the default!
745 #endif
746  if (size > 0) {
747  static const Uint8 zero = 0;
748  size--;
749  if (lseek(fd, size, SEEK_SET) != size)
750  goto error;
751  if (write(fd, &zero, 1) != 1)
752  goto error;
753  if (lseek(fd, -1, SEEK_CUR) != size)
754  goto error;
755  }
756  if (!close(fd))
757  return 0;
758 error:
759  error = errno;
760  if (error >= 0)
761  error = -9999;
762  if (fd >= 0) {
763  close(fd);
764  unlink(os_path);
765  }
766  // If request for sparse file and open itself succeeded, try again without sparse ...
767  return (is_sparse && fd >= 0) ? xemu_create_large_empty_file(os_path, size, 0) : error;
768 }
769 
770 
771 #if defined(XEMU_USE_LODEPNG) && defined(XEMU_FILES_SCREENSHOT_SUPPORT)
772 char xemu_screenshot_full_path[PATH_MAX+1];
773 #include "xemu/lodepng.h"
774 #include <time.h>
775 // TODO: use libpng in Linux, smaller binary (on windows I wouldn't introduce another DLL dependency though ...)
776 // NOTE: you must call this function before the final rendering of course, thus source_pixels has a full rendered frame already ;)
777 // NOTE: ... however, it must be called BEFORE xemu_update_screen() otherwise the texture access may not be valid anymore and crash occures
778 // source_pixels CAN be null. In this case though, Xemu framework tries to use the right pointer based on locked texture or non-locked mode.
779 int xemu_screenshot_png ( const char *path, const char *fn, unsigned int zoom_width, unsigned int zoom_height, Uint32 *source_pixels, unsigned int source_width, unsigned int source_height, unsigned int source_texture_width )
780 {
781  int target_width = source_width * zoom_width;
782  int target_height = source_height * zoom_height;
783  if (!source_pixels) {
784  // No source_pixels was given ...
785  source_pixels = xemu_frame_pixel_access_p;
786  if (!source_pixels) { // not ready to access?
787  DEBUGPRINT("SCREENSHOT: FAILED: No opened frame with source_pixels=NULL" NL);
788  return -1;
789  }
790  }
791  Uint8 *target_pixels = malloc(target_width * target_height * 3);
792  if (!target_pixels) {
793  ERROR_WINDOW("Not enough memory for taking a screenshot :(\n(could not allocate %d bytes of memory)", target_width * target_height * 3);
794  return -1;
795  }
796  for (int i = 0; i < target_width * target_height; i++) {
797  // Sampling pixel in the source
798  // Kinda crude algorithm, but it is not needed to be very fast and real-time operation, just
799  // to create a screenshot on user's request.
800  Uint32 pixel = source_pixels[(
801  (i % target_width) / zoom_width
802  ) + (
803  ((i / target_width) / zoom_height) * source_texture_width
804  )];
805  // Generate LodePNG compatible RGB stuff
806  // (note, this is maybe possible with simple SDL functions to convert a whole texture
807  // and/or surface without a madness-loop like this, but at least we know this works,
808  // and OK for any kind of endianness). Also lodePNG has 32 bit input encoder, though
809  // it's unknown for me, if RGB byte order can be altered. And also the scaling ...
810  target_pixels[i * 3 + 0] = (pixel & sdl_pix_fmt->Rmask) >> sdl_pix_fmt->Rshift << sdl_pix_fmt->Rloss;
811  target_pixels[i * 3 + 1] = (pixel & sdl_pix_fmt->Gmask) >> sdl_pix_fmt->Gshift << sdl_pix_fmt->Gloss;
812  target_pixels[i * 3 + 2] = (pixel & sdl_pix_fmt->Bmask) >> sdl_pix_fmt->Bshift << sdl_pix_fmt->Bloss;
813  }
814  Uint8 *png_stream = NULL;
815  size_t png_size = 0;
816  //unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
817  // const unsigned char* image, unsigned w, unsigned h);
818  int ret = lodepng_encode24(&png_stream, &png_size, target_pixels, target_width, target_height);
819  free(target_pixels);
820  if (ret) {
821  ERROR_WINDOW("Screenshot problem: loadPNG encode returned with error %u", (unsigned)ret);
822  if (png_stream)
823  free(png_stream);
824  return -1;
825  }
826  if (!png_stream || !png_size) {
827  if (png_stream)
828  free(png_stream);
829  ERROR_WINDOW("Screenshot problem: lodePNG returned invalid memory/size");
830  return -1;
831  }
832  // Now save the result.
833  if (!path && !fn) {
834  // if no path and fn, it means auto-generated and in default screenshot directory
835  sprintf(xemu_screenshot_full_path, "%s%s", sdl_pref_dir, "screenshots");
836  MKDIR(xemu_screenshot_full_path);
837  time_t ut = time(NULL);
838  struct tm *t = localtime(&ut);
839  sprintf(
840  xemu_screenshot_full_path + strlen(xemu_screenshot_full_path),
841  DIRSEP_STR "screenshot-%04d%02d%02d-%02d%02d%02d.png",
842  t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec
843  );
844  } else {
845  if (!fn)
846  FATAL("Invalid %s mode", __func__);
847  if (path)
848  sprintf(xemu_screenshot_full_path, "%s%c%s", path, DIRSEP_CHR, fn);
849  else
850  strcpy(xemu_screenshot_full_path, fn);
851  }
852  ret = xemu_save_file(xemu_screenshot_full_path, png_stream, png_size, "Cannot save screenshot PNG");
853  free(png_stream);
854  if (!ret)
855  DEBUGPRINT(
856  "SCREENSHOT: (%dx%d -> %dx%d) successfully saved as %s" NL,
857  source_width, source_height, target_width, target_height, xemu_screenshot_full_path
858  );
859  return ret;
860 }
861 #endif
xemu_safe_open
int xemu_safe_open(const char *fn, int flags)
Definition: emutools_files.c:610
lodepng.h
emutools.h
flags
Uint8 flags
Definition: z8k1.c:126
xemu_open_file
int xemu_open_file(const char *filename, int mode, int *mode2, char *filepath_back)
Definition: emutools_files.c:469
xemuexec_open_native_file_browser
void xemuexec_open_native_file_browser(char *dir)
Definition: emutools_files.c:431
sdl_base_dir
char * sdl_base_dir
Definition: emutools.c:97
OFF_T_ERROR
#define OFF_T_ERROR
Definition: fat32.c:42
O_BINARY
#define O_BINARY
Definition: emutools_basicdefs.h:140
FILE_BROWSER
#define FILE_BROWSER
Definition: emutools_files.h:39
fd
int fd
Definition: hdos.c:62
INFO_WINDOW
#define INFO_WINDOW(...)
Definition: xep128.h:114
sdl_pix_fmt
SDL_PixelFormat * sdl_pix_fmt
Definition: emutools.c:80
fn
const char * fn
Definition: roms.c:42
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
Uint32
uint32_t Uint32
Definition: fat32.c:49
Uint8
uint8_t Uint8
Definition: fat32.c:51
UNIX_DATADIR_3
#define UNIX_DATADIR_3
Definition: emutools_files.h:93
xemu_safe_file_size_by_fd
off_t xemu_safe_file_size_by_fd(int fd)
Definition: emutools_files.c:583
sdl_inst_dir
char * sdl_inst_dir
Definition: emutools.c:97
emutools_files.h
xemu_malloc
void * xemu_malloc(size_t size)
Definition: emutools.c:226
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
xemu_save_file
int xemu_save_file(const char *filename_in, void *data, int size, const char *cry)
Definition: emutools_files.c:622
dir
DIR * dir
Definition: cpmfs.c:46
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
mode
int mode
Definition: vera.c:61
NL
#define NL
Definition: fat32.c:37
PRINTF_LLU
#define PRINTF_LLU
Definition: emutools_basicdefs.h:146
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
xemu_load_file
int xemu_load_file(const char *filename, void *store_to, int min_size, int max_size, const char *cry)
Definition: emutools_files.c:674
xemu_frame_pixel_access_p
Uint32 * xemu_frame_pixel_access_p
Definition: emutools.c:93
PRINTF_LLD
#define PRINTF_LLD
Definition: emutools_basicdefs.h:145
UNIX_DATADIR_2
#define UNIX_DATADIR_2
Definition: emutools_files.h:92
xemu_load_buffer_p
void * xemu_load_buffer_p
Definition: emutools_files.c:34
xemu_safe_read
ssize_t xemu_safe_read(int fd, void *buffer, size_t length)
Definition: emutools_files.c:543
DIRSEP_STR
#define DIRSEP_STR
Definition: emutools_basicdefs.h:141
size
int size
Definition: inject.c:37
status
enum @26::@29 status
MKDIR
#define MKDIR(__n)
Definition: emutools_basicdefs.h:147
WEB_BROWSER
#define WEB_BROWSER
Definition: emutools_files.h:40
xemu_load_filepath
char xemu_load_filepath[PATH_MAX]
Definition: emutools_files.c:35
UNIX_DATADIR_1
#define UNIX_DATADIR_1
Definition: emutools_files.h:91
QUESTION_WINDOW
#define QUESTION_WINDOW(items, msg)
Definition: xep128.h:124
name
const char * name
Definition: joystick.c:46
xemu_create_large_empty_file
int xemu_create_large_empty_file(const char *os_path, Uint64 size, int is_sparse)
Definition: emutools_files.c:727
xemu_realloc
void * xemu_realloc(void *p, size_t size)
Definition: emutools.c:235
xemu_safe_write
ssize_t xemu_safe_write(int fd, const void *buffer, size_t length)
Definition: emutools_files.c:563
sdl_pref_dir
char * sdl_pref_dir
Definition: emutools.c:97
FATAL
#define FATAL(...)
Definition: xep128.h:117
XEMU_OPEN_FILE_FIRST_MODE_USED
#define XEMU_OPEN_FILE_FIRST_MODE_USED
Definition: emutools_files.h:45
xemu_safe_close
int xemu_safe_close(int fd)
Definition: emutools_files.c:600
xemu_safe_file_size_by_name
off_t xemu_safe_file_size_by_name(const char *name)
Definition: emutools_files.c:592
UNIX_DATADIR_0
#define UNIX_DATADIR_0
Definition: emutools_files.h:90
st
struct stat st
Definition: cpmfs.c:43
DIRSEP_CHR
#define DIRSEP_CHR
Definition: emutools_basicdefs.h:142
xemu_safe_open_with_mode
int xemu_safe_open_with_mode(const char *fn, int flags, int mode)
Definition: emutools_files.c:616