WiiUDownloader/cdecrypt/util.c
2023-07-18 12:27:23 +02:00

242 lines
6.4 KiB
C

/*
Common code for Gust (Koei/Tecmo) PC games tools
Copyright © 2019-2021 VitaSmith
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utf8.h"
#include "util.h"
bool create_path(char *path) {
bool result = true;
struct stat64_t st;
#if defined(_WIN32)
// Ignore Windows drive names
if ((strlen(path) == 2) && (path[1] == ':'))
return true;
#endif
if (stat64_utf8(path, &st) != 0) {
// Directory doesn't exist, create it
size_t pos = 0;
for (size_t n = strlen(path); n > 0; n--) {
if (path[n] == PATH_SEP) {
while ((n > 0) && (path[--n] == PATH_SEP))
;
pos = n + 1;
break;
}
}
if (pos > 0) {
// Create parent dirs
path[pos] = 0;
char *new_path = malloc(strlen(path) + 1);
if (new_path == NULL) {
fprintf(stderr, "ERROR: Can't allocate path\n");
return false;
}
strcpy(new_path, path);
result = create_path(new_path);
free(new_path);
path[pos] = PATH_SEP;
}
// Create node
if (result)
result = CREATE_DIR(path);
} else if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "ERROR: '%s' exists but isn't a directory\n", path);
return false;
}
return result;
}
// dirname/basename, that *PRESERVE* the string parameter.
// Note that these calls are not concurrent, meaning that you MUST be done
// using the returned string from a previous call before invoking again.
#if defined(_WIN32)
char *_basename_win32(const char *path, bool remove_extension) {
static char basename[128];
static char ext[64];
ext[0] = 0;
_splitpath_s(path, NULL, 0, NULL, 0, basename, sizeof(basename), ext,
sizeof(ext));
if ((ext[0] != 0) && !remove_extension)
strncat(basename, ext, sizeof(basename) - strlen(basename));
return basename;
}
// This call should behave pretty similar to UNIX' dirname
char *_dirname_win32(const char *path) {
static char dir[PATH_MAX];
static char drive[4];
int found_sep = 0;
memset(drive, 0, sizeof(drive));
_splitpath_s(path, drive, sizeof(drive), dir, sizeof(dir) - 3, NULL, 0, NULL,
0);
// Only deal with drives that are one letter
drive[2] = 0;
drive[3] = 0;
if (drive[1] != ':')
drive[0] = 0;
// Removing trailing path separators
for (int32_t n = (int32_t)strlen(dir) - 1;
(n > 0) && ((dir[n] == '/') || (dir[n] == '\\')); n--) {
dir[n] = 0;
found_sep++;
}
if (dir[0] == 0) {
if (drive[0] == 0)
return found_sep ? "\\" : ".";
drive[2] = '\\';
return drive;
}
if (drive[0] != 0) {
// Add the drive
memmove(&dir[2], dir, strlen(dir) + 1);
memcpy(dir, drive, strlen(drive));
dir[2] = '\\';
}
return dir;
}
#else
char *_basename_unix(const char *path) {
static char path_copy[PATH_MAX];
strncpy(path_copy, path, sizeof(path_copy));
path_copy[PATH_MAX - 1] = 0;
return basename(path_copy);
}
char *_dirname_unix(const char *path) {
static char path_copy[PATH_MAX];
strncpy(path_copy, path, sizeof(path_copy));
path_copy[PATH_MAX - 1] = 0;
return dirname(path_copy);
}
#endif
bool is_file(const char *path) {
struct stat64_t st;
return (stat64_utf8(path, &st) == 0) && S_ISREG(st.st_mode);
}
bool is_directory(const char *path) {
struct stat64_t st;
return (stat64_utf8(path, &st) == 0) && S_ISDIR(st.st_mode);
}
char *change_extension(const char *path, const char *extension) {
static char new_path[PATH_MAX];
strncpy(new_path, _basename((char *)path), sizeof(new_path) - 1);
for (size_t i = 0; i < sizeof(new_path); i++) {
if (new_path[i] == '.')
new_path[i] = 0;
}
strncat(new_path, extension, sizeof(new_path) - strlen(new_path) - 1);
return new_path;
}
size_t get_trailing_slash(const char *path) {
size_t i;
if ((path == NULL) || (path[0] == 0))
return 0;
for (i = strlen(path) - 1; (i > 0) && ((path[i] != '/') && (path[i] != '\\'));
i--)
;
return (i == 0) ? 0 : i + 1;
}
uint32_t read_file_max(const char *path, uint8_t **buf, uint32_t max_size) {
FILE *file = fopen_utf8(path, "rb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't open '%s'\n", path);
return 0;
}
fseek(file, 0L, SEEK_END);
uint32_t size = (uint32_t)ftell(file);
fseek(file, 0L, SEEK_SET);
if (max_size != 0)
size = min(size, max_size);
*buf = calloc(size, 1);
if (*buf == NULL) {
size = 0;
goto out;
}
if (fread(*buf, 1, size, file) != size) {
fprintf(stderr, "ERROR: Can't read '%s'\n", path);
size = 0;
}
out:
fclose(file);
if (size == 0) {
free(*buf);
*buf = NULL;
}
return size;
}
uint64_t get_file_size(const char *path) {
FILE *file = fopen_utf8(path, "rb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't open '%s'\n", path);
return 0;
}
fseek(file, 0L, SEEK_END);
uint64_t size = ftell64(file);
fclose(file);
return size;
}
void create_backup(const char *path) {
struct stat64_t st;
if (stat64_utf8(path, &st) == 0) {
char *backup_path = malloc(strlen(path) + 5);
if (backup_path == NULL)
return;
strcpy(backup_path, path);
strcat(backup_path, ".bak");
if (stat64_utf8(backup_path, &st) != 0) {
if (rename_utf8(path, backup_path) == 0)
printf("Saved backup as '%s'\n", backup_path);
else
fprintf(stderr, "WARNING: Could not create backup file '%s\n",
backup_path);
}
free(backup_path);
}
}
bool write_file(const uint8_t *buf, const uint32_t size, const char *path,
const bool backup) {
if (backup)
create_backup(path);
FILE *file = fopen_utf8(path, "wb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't create file '%s'\n", path);
return false;
}
bool r = (fwrite(buf, 1, size, file) == size);
fclose(file);
if (!r)
fprintf(stderr, "ERROR: Can't write file '%s'\n", path);
return r;
}