518 lines
11 KiB
C
518 lines
11 KiB
C
/*
|
|
PokeMini - Pokémon-Mini Emulator
|
|
Copyright (C) 2009-2015 JustBurn
|
|
|
|
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 "PMCommon.h"
|
|
#include <ctype.h>
|
|
|
|
// Return true if the string is valid and non-empty
|
|
int StringIsSet(char *str)
|
|
{
|
|
if (!str) return 0;
|
|
if (strlen(str) == 0) return 0;
|
|
return 1;
|
|
}
|
|
|
|
// Get multiple of 2
|
|
int GetMultiple2(int input)
|
|
{
|
|
if (!input) return 0;
|
|
input--;
|
|
input |= (input >> 1);
|
|
input |= (input >> 2);
|
|
input |= (input >> 4);
|
|
input |= (input >> 8);
|
|
input |= (input >> 16);
|
|
return input+1;
|
|
}
|
|
|
|
// Get multiple of 2 (Mask)
|
|
int GetMultiple2Mask(int input)
|
|
{
|
|
if (input) input--;
|
|
input |= (input >> 1);
|
|
input |= (input >> 2);
|
|
input |= (input >> 4);
|
|
input |= (input >> 8);
|
|
input |= (input >> 16);
|
|
return input;
|
|
}
|
|
|
|
// Check if file exist
|
|
int FileExist(const char *filename)
|
|
{
|
|
FILE *fi;
|
|
|
|
// Test open file
|
|
fi = fopen(filename, "rb");
|
|
if (fi == NULL) return 0;
|
|
fclose(fi);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Get filename
|
|
char *GetFilename(const char *filename)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) {
|
|
return (char *)&filename[i+1];
|
|
}
|
|
}
|
|
return (char *)filename;
|
|
}
|
|
|
|
// Get extension
|
|
char *GetExtension(const char *filename)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if (filename[i] == '.') {
|
|
return (char *)&filename[i];
|
|
}
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) break;
|
|
}
|
|
return (char *)&filename[strlen(filename)-1];
|
|
}
|
|
|
|
// Extract path
|
|
char *ExtractPath(char *filename, int slash)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) {
|
|
if (slash) filename[i+1] = 0;
|
|
else filename[i] = 0;
|
|
return filename;
|
|
}
|
|
}
|
|
if (slash && !strlen(filename)) {
|
|
filename[0] = '.';
|
|
filename[1] = '/';
|
|
filename[2] = 0;
|
|
}
|
|
return filename;
|
|
}
|
|
|
|
// Remove extension
|
|
char *RemoveExtension(char *filename)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if (filename[i] == '.') {
|
|
filename[i] = 0;
|
|
return filename;
|
|
}
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) {
|
|
return filename;
|
|
}
|
|
}
|
|
return filename;
|
|
}
|
|
|
|
// Check if filename has certain extension
|
|
int ExtensionCheck(const char *filename, const char *ext)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if (filename[i] == '.') {
|
|
if (strcasecmp(&filename[i], ext) == 0) {
|
|
return 1;
|
|
}
|
|
}
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// true if the path ends with a slash
|
|
int HasLastSlash(char *path)
|
|
{
|
|
int len = strlen(path);
|
|
if (!len) return 0;
|
|
return (path[len-1] == '/') || (path[len-1] == '\\');
|
|
}
|
|
|
|
// Convert slashes to windows type
|
|
void ConvertSlashes(char *filename, int slashtype)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if ((filename[i] == '/') || (filename[i] == '\\')) {
|
|
if (slashtype == 0) filename[i] = PATH_SLASH_CHR;
|
|
else if (slashtype == 1) filename[i] = '/';
|
|
else if (slashtype == 2) filename[i] = '\\';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trim string
|
|
char *TrimStr(char *s)
|
|
{
|
|
int siz = strlen(s);
|
|
char *ptr = s + siz - 1;
|
|
if (!siz) return s;
|
|
while ((ptr >= s) && (isspace((int)*ptr) || (*ptr == '\n') || (*ptr == '\r'))) --ptr;
|
|
ptr[1] = '\0';
|
|
while ((*s != 0) && (isspace((int)*s) || (*s == '\n') || (*s == '\r'))) s++;
|
|
return s;
|
|
}
|
|
|
|
// Remove comments
|
|
void RemoveComments(char *s)
|
|
{
|
|
while ((*s != 0) && (*s != ';') && (*s != '#')) s++;
|
|
*s = 0;
|
|
}
|
|
|
|
// Up to token...
|
|
char *UpToToken(char *out, const char *in, char *tokens, char *tokenfound)
|
|
{
|
|
char ch, *ltokens, lch;
|
|
if (out) *out = 0;
|
|
if (in) {
|
|
while ((ch = *in++) != 0) {
|
|
ltokens = tokens;
|
|
while ((lch = *ltokens++) != 0) {
|
|
if (ch == lch) {
|
|
if (tokenfound) *tokenfound = lch;
|
|
return (char *)in;
|
|
}
|
|
}
|
|
if (out) {
|
|
*out++ = ch;
|
|
*out = 0;
|
|
}
|
|
}
|
|
}
|
|
if (tokenfound) *tokenfound = 0;
|
|
return (char *)in;
|
|
}
|
|
|
|
// Remove characters
|
|
void RemoveChars(char *out, const char *in, char *chs)
|
|
{
|
|
char ch, *lchs, lch;
|
|
if (in) {
|
|
while ((ch = *in++) != 0) {
|
|
lchs = chs;
|
|
while ((lch = *lchs++) != 0) {
|
|
if (ch == lch) break;
|
|
}
|
|
if ((!lch) && (out)) {
|
|
*out++ = ch;
|
|
}
|
|
}
|
|
}
|
|
if (out) *out = 0;
|
|
}
|
|
|
|
// Convert string to boolean
|
|
int Str2Bool(char *s)
|
|
{
|
|
if (!strcasecmp(s, "1")) return 1;
|
|
else if (!strcasecmp(s, "y")) return 1;
|
|
else if (!strcasecmp(s, "yes")) return 1;
|
|
else if (!strcasecmp(s, "t")) return 1;
|
|
else if (!strcasecmp(s, "true")) return 1;
|
|
else if (!strcasecmp(s, "on")) return 1;
|
|
return 0;
|
|
}
|
|
|
|
// Convert boolean to string
|
|
const char *Bool2Str(int i)
|
|
{
|
|
return (i ? "true" : "false");
|
|
}
|
|
|
|
// Convert boolean to string with an affirmative result
|
|
const char *Bool2StrAf(int i)
|
|
{
|
|
return (i ? "yes" : "no");
|
|
}
|
|
|
|
// Fix symbol identification
|
|
void FixSymbolID(char *filename)
|
|
{
|
|
int i;
|
|
for (i = strlen(filename)-1; i >= 0; i--) {
|
|
if (((filename[i] < '0') || (filename[i] > '9')) &&
|
|
((filename[i] < 'a') || (filename[i] > 'z')) &&
|
|
((filename[i] < 'A') || (filename[i] > 'Z')) &&
|
|
(filename[i] != '_')) {
|
|
filename[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear control characters
|
|
void ClearCtrlChars(char *s, int len)
|
|
{
|
|
if (!len) return;
|
|
while (len--) {
|
|
if (*s < 32) *s = '.';
|
|
s++;
|
|
}
|
|
}
|
|
|
|
// atoi() that support hex numbers and default on failure
|
|
int atoi_Ex(const char *str, int defnum)
|
|
{
|
|
int num = defnum;
|
|
int res = 0;
|
|
int sign = 0;
|
|
if (strlen(str) >= 2 && str[0] == '-') {
|
|
sign = 1;
|
|
str++;
|
|
}
|
|
if (strlen(str) >= 2 && str[0] == '#') res = sscanf(&str[1], "%x", &num);
|
|
else if (strlen(str) >= 2 && str[0] == '$') res = sscanf(&str[1], "%x", &num);
|
|
else res = sscanf(str, "%i", &num);
|
|
if (res != 1) return defnum;
|
|
if (sign) num = -num;
|
|
return num;
|
|
}
|
|
|
|
// atoi() that support hex numbers, return false on failure
|
|
int atoi_Ex2(const char *str, int *outnum)
|
|
{
|
|
int res = 0;
|
|
int sign = 0;
|
|
if (strlen(str) >= 2 && str[0] == '-') {
|
|
sign = 1;
|
|
str++;
|
|
}
|
|
if (strlen(str) >= 2 && str[0] == '#') res = sscanf(&str[1], "%x", outnum);
|
|
else if (strlen(str) >= 2 && str[0] == '$') res = sscanf(&str[1], "%x", outnum);
|
|
else res = sscanf(str, "%i", outnum);
|
|
if (res == 1 && sign) *outnum = -*outnum;
|
|
return res;
|
|
}
|
|
|
|
// atof() that return float and support default on failure
|
|
float atof_Ex(const char *str, float defnum)
|
|
{
|
|
float num = defnum;
|
|
sscanf(str, "%f", &num);
|
|
return num;
|
|
}
|
|
|
|
// Separate string at character
|
|
int SeparateAtChar(char *s, char ch, char **key, char **value)
|
|
{
|
|
char *ptr = s;
|
|
while ((*ptr != 0) && (*ptr != ch)) ptr++;
|
|
if (*ptr == 0) return 0;
|
|
*ptr = 0;
|
|
*key = s;
|
|
*value = ptr+1;
|
|
return 1;
|
|
}
|
|
|
|
// Separate string at any character
|
|
static int SeparateAtChars_i(char s, char *chs)
|
|
{
|
|
if (s == 0) return 0;
|
|
while (*chs != 0) {
|
|
if (s == *chs++) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
int SeparateAtChars(char *s, char *chs, char **key, char **value)
|
|
{
|
|
char *ptr = s;
|
|
while (*ptr != 0) {
|
|
if (SeparateAtChars_i(*ptr, chs)) break;
|
|
ptr++;
|
|
}
|
|
if (*ptr == 0) return 0;
|
|
*ptr = 0;
|
|
if (key) *key = s;
|
|
if (value) *value = ptr+1;
|
|
return 1;
|
|
}
|
|
|
|
// Get an argument from executable parameters
|
|
int GetArgument(const char *runcmd, int args, char *out, int len, char **runcmd_found)
|
|
{
|
|
char lch = ' ', ch;
|
|
int argc = 0;
|
|
|
|
while ((ch = *runcmd++) != 0) {
|
|
if ((ch == ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r')) {
|
|
// Ignore spaces / new-lines
|
|
if ((lch != ' ') && (lch != '\t') && (lch != '\n') && (lch != '\r')) {
|
|
argc++;
|
|
if (runcmd_found && (argc == args)) *runcmd_found = (char *)runcmd - 1;
|
|
}
|
|
} else if (ch == '"') {
|
|
// " String
|
|
while ((ch = *runcmd++) != 0) {
|
|
if (ch == '"') {
|
|
break;
|
|
} else if (out && (argc == args) && (len > 0)) {
|
|
*out++ = ch;
|
|
len--;
|
|
}
|
|
lch = ch;
|
|
}
|
|
} else if (ch == '\'') {
|
|
// ' String
|
|
while ((ch = *runcmd++) != 0) {
|
|
if (ch == '\'') {
|
|
break;
|
|
} else if (out && (argc == args) && (len > 0)) {
|
|
*out++ = ch;
|
|
len--;
|
|
}
|
|
lch = ch;
|
|
}
|
|
} else {
|
|
// Any other character
|
|
if (out && (argc == args) && (len > 0)) {
|
|
*out++ = ch;
|
|
len--;
|
|
}
|
|
}
|
|
lch = ch;
|
|
}
|
|
|
|
if ((lch != ' ') && (lch != '\t') && (lch != '\n') && (lch != '\r')) {
|
|
argc++;
|
|
if (runcmd_found && (argc == args)) *runcmd_found = (char *)runcmd - 1;
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
FILE *PokeDebugFOut = NULL;
|
|
FILE *PokeDebugFErr = NULL;
|
|
|
|
void PokeDPrint(int pokemsg, char *format, ...)
|
|
{
|
|
char buffer[PMTMPV];
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsprintf(buffer, format, args);
|
|
switch (pokemsg) {
|
|
case POKEMSG_OUT:
|
|
if (PokeDebugFOut) fwrite(buffer, 1, strlen(buffer), PokeDebugFOut);
|
|
printf("%s", buffer);
|
|
break;
|
|
case POKEMSG_ERR:
|
|
if (PokeDebugFErr) fwrite(buffer, 1, strlen(buffer), PokeDebugFErr);
|
|
fprintf(stderr, "%s", buffer);
|
|
break;
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
#ifndef NO_DIRS
|
|
|
|
#ifdef _MSC_VER
|
|
#include <direct.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
char PokeMini_ExecDir[PMTMPV]; // Executable directory
|
|
char PokeMini_CurrDir[PMTMPV]; // Current directory
|
|
|
|
// Initialize directories
|
|
void PokeMini_InitDirs(char *argv0, char *exec)
|
|
{
|
|
// Get current directory
|
|
PokeMini_GetCurrentDir();
|
|
|
|
// Get executable directory
|
|
if (argv0) {
|
|
if ((argv0[0] == '/') || (strchr(argv0, ':') != NULL)) {
|
|
// Absolute path
|
|
strcpy(PokeMini_ExecDir, argv0);
|
|
if (exec) strcpy(exec, PokeMini_ExecDir);
|
|
ExtractPath(PokeMini_ExecDir, 1);
|
|
} else {
|
|
// Not an absolute path
|
|
if (HasLastSlash(PokeMini_CurrDir)) sprintf(PokeMini_ExecDir, "%s%s", PokeMini_CurrDir, argv0);
|
|
else sprintf(PokeMini_ExecDir, "%s/%s", PokeMini_CurrDir, argv0);
|
|
if (exec) strcpy(exec, PokeMini_ExecDir);
|
|
ExtractPath(PokeMini_ExecDir, 1);
|
|
}
|
|
} else {
|
|
strcpy(PokeMini_ExecDir, PokeMini_CurrDir);
|
|
}
|
|
|
|
}
|
|
|
|
// Get current directory and save on parameter
|
|
void PokeMini_GetCustomDir(char *dir, int max)
|
|
{
|
|
if (!getcwd(dir, max)) {
|
|
strcpy(dir, "/");
|
|
PokeDPrint(POKEMSG_ERR, "getcwd() error\n");
|
|
}
|
|
if (!strlen(dir)) strcpy(dir, "/");
|
|
else ConvertSlashes(dir, PATH_SLASH_UNIX);
|
|
}
|
|
|
|
// Go to custom directory
|
|
void PokeMini_GotoCustomDir(const char *dir)
|
|
{
|
|
char buffer[PMTMPV];
|
|
strcpy(buffer, dir);
|
|
ConvertSlashes(buffer, PATH_SLASH_OS);
|
|
if (chdir(buffer)) {
|
|
PokeDPrint(POKEMSG_ERR, "abs chdir('%s') error\n", buffer);
|
|
}
|
|
}
|
|
|
|
// Get current directory
|
|
void PokeMini_GetCurrentDir(void)
|
|
{
|
|
PokeMini_GetCustomDir(PokeMini_CurrDir, PMTMPV);
|
|
}
|
|
|
|
// Set current directory
|
|
void PokeMini_SetCurrentDir(const char *dir)
|
|
{
|
|
PokeMini_GotoCustomDir(dir);
|
|
PokeMini_GetCurrentDir();
|
|
}
|
|
|
|
// Go to current directory
|
|
void PokeMini_GotoCurrentDir(void)
|
|
{
|
|
PokeMini_GotoCustomDir(PokeMini_CurrDir);
|
|
}
|
|
|
|
// Go to launch directory
|
|
void PokeMini_GotoExecDir(void)
|
|
{
|
|
PokeMini_GotoCustomDir(PokeMini_ExecDir);
|
|
}
|
|
|
|
#else
|
|
|
|
void PokeMini_InitDirs(char *execdir) {}
|
|
void PokeMini_GetCurrentDir(void) {}
|
|
void PokeMini_GotoCurrentDir(void) {}
|
|
void PokeMini_GotoExecDir(void) {}
|
|
|
|
#endif
|