mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-08-05 22:50:25 +00:00
Merge pull request #419 from a740g/filesystem-refactor-and-update
Filesystem refactor and update
This commit is contained in:
commit
7aca957619
16 changed files with 1447 additions and 740 deletions
|
@ -52,14 +52,14 @@
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
// common includes
|
// common includes
|
||||||
# include <stdio.h>
|
|
||||||
# include <cmath>
|
# include <cmath>
|
||||||
# include <stdint.h>
|
|
||||||
# include <errno.h>
|
# include <errno.h>
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <fstream>
|
# include <fstream>
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
# include <limits.h>
|
# include <limits.h>
|
||||||
|
# include <stdint.h>
|
||||||
|
# include <stdio.h>
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
# include <time.h>
|
# include <time.h>
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@
|
||||||
|
|
||||||
# else
|
# else
|
||||||
|
|
||||||
|
# include <dlfcn.h>
|
||||||
# include <pthread.h>
|
# include <pthread.h>
|
||||||
# include <stdlib.h>
|
# include <stdlib.h>
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <dlfcn.h>
|
|
||||||
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
@ -93,6 +93,105 @@
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# define QB_FALSE 0
|
||||||
|
# define QB_TRUE -1
|
||||||
|
|
||||||
|
# define QB_ERROR_NEXT_WITHOUT_FOR 1
|
||||||
|
# define QB_ERROR_SYNTAX_ERROR 2
|
||||||
|
# define QB_ERROR_RETURN_WITHOUT_GOSUB 3
|
||||||
|
# define QB_ERROR_OUT_OF_DATA 4
|
||||||
|
# define QB_ERROR_ILLEGAL_FUNCTION_CALL 5
|
||||||
|
# define QB_ERROR_OVERFLOW 6
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY 7
|
||||||
|
# define QB_ERROR_LABEL_NOT_DEFINED 8
|
||||||
|
# define QB_ERROR_SUBSCRIPT_OUT_OF_RANGE 9
|
||||||
|
# define QB_ERROR_DUPLICATE_DEFINITION 10
|
||||||
|
# define QB_ERROR_DIVISION_BY_ZERO 11
|
||||||
|
# define QB_ERROR_ILLEGAL_IN_DIRECT_MODE 12
|
||||||
|
# define QB_ERROR_TYPE_MISMATCH 13
|
||||||
|
# define QB_ERROR_OUT_OF_STRING_SPACE 14
|
||||||
|
# define QB_ERROR_STRING_FORMULA_TOO_COMPLEX 16
|
||||||
|
# define QB_ERROR_CANNOT_CONTINUE 17
|
||||||
|
# define QB_ERROR_FUNCTION_NOT_DEFINED 18
|
||||||
|
# define QB_ERROR_NO_RESUME 19
|
||||||
|
# define QB_ERROR_RESUME_WITHOUT_ERROR 20
|
||||||
|
# define QB_ERROR_DEVICE_TIMEOUT 24
|
||||||
|
# define QB_ERROR_DEVICE_FAULT 25
|
||||||
|
# define QB_ERROR_FOR_WITHOUT_NEXT 26
|
||||||
|
# define QB_ERROR_OUT_OF_PAPER 27
|
||||||
|
# define QB_ERROR_WHILE_WITHOUT_WEND 29
|
||||||
|
# define QB_ERROR_WEND_WITHOUT_WHILE 30
|
||||||
|
# define QB_ERROR_DUPLICATE_LABEL 33
|
||||||
|
# define QB_ERROR_SUBPROGRAM_NOT_DEFINED 35
|
||||||
|
# define QB_ERROR_ARGUMENT_COUNT_MISMATCH 37
|
||||||
|
# define QB_ERROR_ARRAY_NOT_DEFINED 38
|
||||||
|
# define QB_ERROR_VARIABLE_REQUIRED 40
|
||||||
|
# define QB_ERROR_FIELD_OVERFLOW 50
|
||||||
|
# define QB_ERROR_INTERNAL_ERROR 51
|
||||||
|
# define QB_ERROR_BAD_FILE_NAME_OR_NUMBER 52
|
||||||
|
# define QB_ERROR_FILE_NOT_FOUND 53
|
||||||
|
# define QB_ERROR_BAD_FILE_MODE 54
|
||||||
|
# define QB_ERROR_FILE_ALREADY_OPEN 55
|
||||||
|
# define QB_ERROR_FIELD_STATEMENT_ACTIVE 56
|
||||||
|
# define QB_ERROR_DEVICE_IO_ERROR 57
|
||||||
|
# define QB_ERROR_FILE_ALREADY_EXISTS 58
|
||||||
|
# define QB_ERROR_BAD_RECORD_LENGTH 59
|
||||||
|
# define QB_ERROR_DISK_FULL 61
|
||||||
|
# define QB_ERROR_INPUT_PAST_END_OF_FILE 62
|
||||||
|
# define QB_ERROR_BAD_RECORD_NUMBER 63
|
||||||
|
# define QB_ERROR_BAD_FILE_NAME 64
|
||||||
|
# define QB_ERROR_TOO_MANY_FILES 67
|
||||||
|
# define QB_ERROR_DEVICE_UNAVAILABLE 68
|
||||||
|
# define QB_ERROR_COMMUNICATION_BUFFER_OVERFLOW 69
|
||||||
|
# define QB_ERROR_PERMISSION_DENIED 70
|
||||||
|
# define QB_ERROR_DISK_NOT_READY 71
|
||||||
|
# define QB_ERROR_DISK_MEDIA_ERROR 72
|
||||||
|
# define QB_ERROR_FEATURE_UNAVAILABLE 73
|
||||||
|
# define QB_ERROR_RENAME_ACROSS_DISKS 74
|
||||||
|
# define QB_ERROR_PATH_FILE_ACCESS_ERROR 75
|
||||||
|
# define QB_ERROR_PATH_NOT_FOUND 76
|
||||||
|
# define QB_ERROR_OUT_OF_STACK_SPACE 256
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL 257
|
||||||
|
# define QB_ERROR_INVALID_HANDLE 258
|
||||||
|
# define QB_ERROR_CANNOT_FIND_DYNAMIC_LIBRARY_FILE 259
|
||||||
|
# define QB_ERROR_FUNCTION_NOT_FOUND_IN_DYNAMIC_LIBRARY 260
|
||||||
|
# define QB_ERROR_FUNCTION_NOT_FOUND_IN_DYNAMIC_LIBRARY_261 261
|
||||||
|
# define QB_ERROR_GL_COMMAND_OUTSIDE_SUB_GL_SCOPE 270
|
||||||
|
# define QB_ERROR_END_SYSTEM_IN_SUB_GL_SCOPE 271
|
||||||
|
# define QB_ERROR_MEMORY_REGION_OUT_OF_RANGE 300
|
||||||
|
# define QB_ERROR_INVALID_SIZE 301
|
||||||
|
# define QB_ERROR_SOURCE_MEMORY_REGION_OUT_OF_RANGE 302
|
||||||
|
# define QB_ERROR_DESTINATION_MEMORY_REGION_OUT_OF_RANGE 303
|
||||||
|
# define QB_ERROR_BOTH_MEMORY_REGIONS_OUT_OF_RANGE 304
|
||||||
|
# define QB_ERROR_SOURCE_MEMORY_FREED 305
|
||||||
|
# define QB_ERROR_DESTINATION_MEMORY_FREED 306
|
||||||
|
# define QB_ERROR_MEMORY_ALREADY_FREED 307
|
||||||
|
# define QB_ERROR_MEMORY_HAS_BEEN_FREED 308
|
||||||
|
# define QB_ERROR_MEMORY_NOT_INITIALIZED 309
|
||||||
|
# define QB_ERROR_SOURCE_MEMORY_NOT_INITIALIZED 310
|
||||||
|
# define QB_ERROR_DESTINATION_MEMORY_NOT_INITIALIZED 311
|
||||||
|
# define QB_ERROR_BOTH_MEMORY_NOT_INITIALIZED 312
|
||||||
|
# define QB_ERROR_BOTH_MEMORY_FREED 313
|
||||||
|
# define QB_ERROR_ASSERT_FAILED 314
|
||||||
|
# define QB_ERROR_ASSERT_FAILED_WITH_DESCRIPTION 315
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_502 502
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_503 503
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_504 504
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_505 505
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_506 506
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_507 507
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_508 508
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_509 509
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_510 510
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_511 511
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_512 512
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_513 513
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_514 514
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_515 515
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_516 516
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_517 517
|
||||||
|
# define QB_ERROR_OUT_OF_MEMORY_FATAL_518 518
|
||||||
|
|
||||||
// QB64 string descriptor structure
|
// QB64 string descriptor structure
|
||||||
struct qbs_field {
|
struct qbs_field {
|
||||||
int32 fileno;
|
int32 fileno;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
#include "filepath.h"
|
||||||
|
#include "filesystem.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "game_controller.h"
|
#include "game_controller.h"
|
||||||
#include "glut-thread.h"
|
#include "glut-thread.h"
|
||||||
|
@ -3946,23 +3948,6 @@ int32 exit_value = 0;
|
||||||
|
|
||||||
void error(int32 error_number); // for forward references
|
void error(int32 error_number); // for forward references
|
||||||
|
|
||||||
char *fixdir(qbs *filename) {
|
|
||||||
// note: changes the slashes in a filename to make it compatible with the OS
|
|
||||||
// applied to QB commands: open, bload/bsave, loadfont, loadimage, sndopen/sndplayfile
|
|
||||||
static int32 i;
|
|
||||||
|
|
||||||
for (i = 0; i < filename->len; i++) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
if (filename->chr[i] == 47)
|
|
||||||
filename->chr[i] = 92;
|
|
||||||
#else
|
|
||||||
if (filename->chr[i] == 92)
|
|
||||||
filename->chr[i] = 47;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return (char *)filename->chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 width8050switch = 1; // if set, can automatically switch to WIDTH 80,50 if LOCATE'ing beyond row 26
|
int32 width8050switch = 1; // if set, can automatically switch to WIDTH 80,50 if LOCATE'ing beyond row 26
|
||||||
|
|
||||||
uint32 pal[256];
|
uint32 pal[256];
|
||||||
|
@ -20913,7 +20898,7 @@ void sub_bsave(qbs *filename, int32 offset, int32 size) {
|
||||||
if (size != 65536)
|
if (size != 65536)
|
||||||
size &= 0xFFFF;
|
size &= 0xFFFF;
|
||||||
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
|
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
|
||||||
fh.open(fixdir(tqbs), std::ios::binary | std::ios::out);
|
fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::out);
|
||||||
if (fh.is_open() == NULL) {
|
if (fh.is_open() == NULL) {
|
||||||
error(64);
|
error(64);
|
||||||
return;
|
return;
|
||||||
|
@ -20954,7 +20939,7 @@ void sub_bload(qbs *filename, int32 offset, int32 passed) {
|
||||||
offset &= 0xFFFF;
|
offset &= 0xFFFF;
|
||||||
}
|
}
|
||||||
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
|
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
|
||||||
fh.open(fixdir(tqbs), std::ios::binary | std::ios::in);
|
fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::in);
|
||||||
if (fh.is_open() == NULL) {
|
if (fh.is_open() == NULL) {
|
||||||
error(53);
|
error(53);
|
||||||
return;
|
return;
|
||||||
|
@ -23022,166 +23007,6 @@ shell_complete:;
|
||||||
|
|
||||||
} //_DONTWAIT & _HIDE
|
} //_DONTWAIT & _HIDE
|
||||||
|
|
||||||
void sub_kill(qbs *str) {
|
|
||||||
// note: file not found returned for non-existant paths too
|
|
||||||
// file already open returned if access unavailable
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
static int32 i;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static WIN32_FIND_DATA fd;
|
|
||||||
static HANDLE hFind;
|
|
||||||
static qbs *strpath = NULL;
|
|
||||||
if (!strpath)
|
|
||||||
strpath = qbs_new(0, 0);
|
|
||||||
static qbs *strfullz = NULL;
|
|
||||||
if (!strfullz)
|
|
||||||
strfullz = qbs_new(0, 0);
|
|
||||||
// find path
|
|
||||||
qbs_set(strpath, strz);
|
|
||||||
for (i = strpath->len; i > 0; i--) {
|
|
||||||
if ((strpath->chr[i - 1] == 47) || (strpath->chr[i - 1] == 92)) {
|
|
||||||
strpath->len = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // i
|
|
||||||
if (i == 0)
|
|
||||||
strpath->len = 0; // no path specified
|
|
||||||
static int32 count;
|
|
||||||
count = 0;
|
|
||||||
hFind = FindFirstFile(fixdir(strz), &fd);
|
|
||||||
if (hFind == INVALID_HANDLE_VALUE) {
|
|
||||||
error(53);
|
|
||||||
return;
|
|
||||||
} // file not found
|
|
||||||
do {
|
|
||||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
||||||
qbs_set(strfullz, qbs_add(strpath, qbs_new_txt_len(fd.cFileName, strlen(fd.cFileName) + 1)));
|
|
||||||
if (!DeleteFile((char *)strfullz->chr)) {
|
|
||||||
i = GetLastError();
|
|
||||||
if ((i == 5) || (i == 19) || (i == 33) || (i == 32)) {
|
|
||||||
FindClose(hFind);
|
|
||||||
error(55);
|
|
||||||
return;
|
|
||||||
} // file already open
|
|
||||||
FindClose(hFind);
|
|
||||||
error(53);
|
|
||||||
return; // file not found
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
} // not a directory
|
|
||||||
} while (FindNextFile(hFind, &fd));
|
|
||||||
FindClose(hFind);
|
|
||||||
if (!count) {
|
|
||||||
error(53);
|
|
||||||
return;
|
|
||||||
} // file not found
|
|
||||||
return;
|
|
||||||
#else
|
|
||||||
if (remove(fixdir(strz))) {
|
|
||||||
i = errno;
|
|
||||||
if (i == ENOENT) {
|
|
||||||
error(53);
|
|
||||||
return;
|
|
||||||
} // file not found
|
|
||||||
if (i == EACCES) {
|
|
||||||
error(75);
|
|
||||||
return;
|
|
||||||
} // path/file access error
|
|
||||||
error(64); // bad file name (assumed)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void sub_name(qbs *oldname, qbs *newname) {
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
static qbs *strz2 = NULL;
|
|
||||||
if (!strz2)
|
|
||||||
strz2 = qbs_new(0, 0);
|
|
||||||
static int32 i;
|
|
||||||
qbs_set(strz, qbs_add(oldname, qbs_new_txt_len("\0", 1)));
|
|
||||||
qbs_set(strz2, qbs_add(newname, qbs_new_txt_len("\0", 1)));
|
|
||||||
if (rename(fixdir(strz), fixdir(strz2))) {
|
|
||||||
i = errno;
|
|
||||||
if (i == ENOENT) {
|
|
||||||
error(53);
|
|
||||||
return;
|
|
||||||
} // file not found
|
|
||||||
if (i == EINVAL) {
|
|
||||||
error(64);
|
|
||||||
return;
|
|
||||||
} // bad file name
|
|
||||||
if (i == EACCES) {
|
|
||||||
error(75);
|
|
||||||
return;
|
|
||||||
} // path/file access error
|
|
||||||
error(5); // Illegal function call (assumed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sub_chdir(qbs *str) {
|
|
||||||
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
|
|
||||||
if (chdir(fixdir(strz)) == -1) {
|
|
||||||
// assume errno==ENOENT
|
|
||||||
error(76); // path not found
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32 tmp_long;
|
|
||||||
static int32 got_ports = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sub_mkdir(qbs *str) {
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
|
|
||||||
#ifdef QB64_UNIX
|
|
||||||
if (mkdir(fixdir(strz), 0770) == -1) {
|
|
||||||
#else
|
|
||||||
if (mkdir(fixdir(strz)) == -1) {
|
|
||||||
#endif
|
|
||||||
if (errno == EEXIST) {
|
|
||||||
error(75);
|
|
||||||
return;
|
|
||||||
} // path/file access error
|
|
||||||
// assume errno==ENOENT
|
|
||||||
error(76); // path not found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sub_rmdir(qbs *str) {
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
|
|
||||||
if (rmdir(fixdir(strz)) == -1) {
|
|
||||||
if (errno == ENOTEMPTY) {
|
|
||||||
error(75);
|
|
||||||
return;
|
|
||||||
} // path/file access error
|
|
||||||
// assume errno==ENOENT
|
|
||||||
error(76); // path not found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long double pow2(long double x, long double y) {
|
long double pow2(long double x, long double y) {
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
if (y != std::floor(y)) {
|
if (y != std::floor(y)) {
|
||||||
|
@ -24962,7 +24787,7 @@ int32_t func__loadfont(qbs *file_name, int32_t size, qbs *requirements, int32_t
|
||||||
FONT_DEBUG_PRINT("Loading font from memory. Size = %i", bytes);
|
FONT_DEBUG_PRINT("Loading font from memory. Size = %i", bytes);
|
||||||
} else {
|
} else {
|
||||||
qbs_set(fileNameZ, qbs_add(file_name, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
|
qbs_set(fileNameZ, qbs_add(file_name, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
|
||||||
content = FontLoadFileToMemory((char *)fileNameZ->chr, &bytes); // this we must free!!!
|
content = FontLoadFileToMemory(filepath_fix_directory(fileNameZ), &bytes); // this we must free!!!
|
||||||
FONT_DEBUG_PRINT("Loading font from file %s", fileNameZ->chr);
|
FONT_DEBUG_PRINT("Loading font from file %s", fileNameZ->chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26588,7 +26413,7 @@ void sub_run(qbs *f) {
|
||||||
strz = qbs_new(0, 0);
|
strz = qbs_new(0, 0);
|
||||||
|
|
||||||
qbs_set(str, f);
|
qbs_set(str, f);
|
||||||
fixdir(str);
|
filepath_fix_directory(str);
|
||||||
|
|
||||||
#ifdef QB64_WINDOWS
|
#ifdef QB64_WINDOWS
|
||||||
|
|
||||||
|
@ -29733,24 +29558,24 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
|
||||||
if (how == 2) {
|
if (how == 2) {
|
||||||
// with truncate
|
// with truncate
|
||||||
if (access == 1)
|
if (access == 1)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
|
||||||
if (access == 2)
|
if (access == 2)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
if (access == 3)
|
if (access == 3)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
} else {
|
} else {
|
||||||
// without truncate
|
// without truncate
|
||||||
if (access == 1)
|
if (access == 1)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary);
|
||||||
if (access == 2)
|
if (access == 2)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::app);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::app);
|
||||||
if (access == 3)
|
if (access == 3)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary);
|
||||||
}
|
}
|
||||||
if (how) {
|
if (how) {
|
||||||
if (!f->file_handle->is_open()) { // couldn't open file, so attempt creation
|
if (!f->file_handle->is_open()) { // couldn't open file, so attempt creation
|
||||||
f->file_handle_o = new std::ofstream();
|
f->file_handle_o = new std::ofstream();
|
||||||
f->file_handle_o->open(fixdir(filenamez), std::ios::out);
|
f->file_handle_o->open(filepath_fix_directory(filenamez), std::ios::out);
|
||||||
if (f->file_handle_o->is_open()) { // created new file
|
if (f->file_handle_o->is_open()) { // created new file
|
||||||
f->file_handle_o->close();
|
f->file_handle_o->close();
|
||||||
// retry open
|
// retry open
|
||||||
|
@ -29758,19 +29583,19 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
|
||||||
if (how == 2) {
|
if (how == 2) {
|
||||||
// with truncate
|
// with truncate
|
||||||
if (access == 1)
|
if (access == 1)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
|
||||||
if (access == 2)
|
if (access == 2)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
if (access == 3)
|
if (access == 3)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
} else {
|
} else {
|
||||||
// without truncate
|
// without truncate
|
||||||
if (access == 1)
|
if (access == 1)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary);
|
||||||
if (access == 2)
|
if (access == 2)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::app);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::app);
|
||||||
if (access == 3)
|
if (access == 3)
|
||||||
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary);
|
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete f->file_handle_o;
|
delete f->file_handle_o;
|
||||||
|
@ -29906,7 +29731,7 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
|
||||||
if (how)
|
if (how)
|
||||||
x3 = OPEN_ALWAYS;
|
x3 = OPEN_ALWAYS;
|
||||||
undefined_retry:
|
undefined_retry:
|
||||||
f_w->file_handle = CreateFile(fixdir(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
|
f_w->file_handle = CreateFile(filepath_fix_directory(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
|
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
|
||||||
if (how == 3) {
|
if (how == 3) {
|
||||||
|
@ -29950,7 +29775,7 @@ undefined_retry:
|
||||||
if (GetFileSize_low || GetFileSize_high) {
|
if (GetFileSize_low || GetFileSize_high) {
|
||||||
CloseHandle(f_w->file_handle);
|
CloseHandle(f_w->file_handle);
|
||||||
x3 = TRUNCATE_EXISTING;
|
x3 = TRUNCATE_EXISTING;
|
||||||
f_w->file_handle = CreateFile(fixdir(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
|
f_w->file_handle = CreateFile(filepath_fix_directory(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
|
||||||
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
|
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
|
||||||
gfs_free(i);
|
gfs_free(i);
|
||||||
|
@ -31669,134 +31494,6 @@ failed:;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void sub_files(qbs *str, int32 passed) {
|
|
||||||
if (new_error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
static int32 i, i2, i3;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
|
|
||||||
if (passed) {
|
|
||||||
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
|
|
||||||
} else {
|
|
||||||
qbs_set(strz, qbs_new_txt_len("\0", 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static WIN32_FIND_DATA fd;
|
|
||||||
static HANDLE hFind;
|
|
||||||
static qbs *strpath = NULL;
|
|
||||||
if (!strpath)
|
|
||||||
strpath = qbs_new(0, 0);
|
|
||||||
static qbs *strz2 = NULL;
|
|
||||||
if (!strz2)
|
|
||||||
strz2 = qbs_new(0, 0);
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
if (strz->len >= 2) {
|
|
||||||
if (strz->chr[strz->len - 2] == 92)
|
|
||||||
i = 1;
|
|
||||||
} else
|
|
||||||
i = 1;
|
|
||||||
if (i) { // add * (and new NULL term.)
|
|
||||||
strz->chr[strz->len - 1] = 42; //"*"
|
|
||||||
qbs_set(strz, qbs_add(strz, qbs_new_txt_len("\0", 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
qbs_set(strpath, strz);
|
|
||||||
|
|
||||||
for (i = strpath->len; i > 0; i--) {
|
|
||||||
if ((strpath->chr[i - 1] == 47) || (strpath->chr[i - 1] == 92)) {
|
|
||||||
strpath->len = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // i
|
|
||||||
if (i == 0)
|
|
||||||
strpath->len = 0; // no path specified
|
|
||||||
|
|
||||||
// print the current path
|
|
||||||
// note: for QBASIC compatibility reasons it does not print the directory name of the files being displayed
|
|
||||||
static uint8 curdir[4096];
|
|
||||||
static uint8 curdir2[4096];
|
|
||||||
i2 = GetCurrentDirectory(4096, (char *)curdir);
|
|
||||||
if (i2) {
|
|
||||||
i2 = GetShortPathName((char *)curdir, (char *)curdir2, 4096);
|
|
||||||
if (i2) {
|
|
||||||
qbs_set(strz2, qbs_ucase(qbs_new_txt_len((char *)curdir2, i2)));
|
|
||||||
qbs_print(strz2, 1);
|
|
||||||
} else {
|
|
||||||
error(5);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error(5);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hFind = FindFirstFile(fixdir(strz), &fd);
|
|
||||||
if (hFind == INVALID_HANDLE_VALUE) {
|
|
||||||
error(53);
|
|
||||||
return;
|
|
||||||
} // file not found
|
|
||||||
do {
|
|
||||||
|
|
||||||
if (!fd.cAlternateFileName[0]) { // no alternate filename exists
|
|
||||||
qbs_set(strz2, qbs_ucase(qbs_new_txt_len(fd.cFileName, strlen(fd.cFileName))));
|
|
||||||
} else {
|
|
||||||
qbs_set(strz2, qbs_ucase(qbs_new_txt_len(fd.cAlternateFileName, strlen(fd.cAlternateFileName))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strz2->len < 12) { // padding required
|
|
||||||
qbs_set(strz2, qbs_add(strz2, func_space(12 - strz2->len)));
|
|
||||||
i2 = 0;
|
|
||||||
for (i = 0; i < 12; i++) {
|
|
||||||
if (strz2->chr[i] == 46) {
|
|
||||||
memmove(&strz2->chr[8], &strz2->chr[i], 4);
|
|
||||||
memset(&strz2->chr[i], 32, 8 - i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // i
|
|
||||||
} // padding
|
|
||||||
|
|
||||||
// add " " or "<DIR> "
|
|
||||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
||||||
qbs_set(strz2, qbs_add(strz2, qbs_new_txt_len("<DIR> ", 6)));
|
|
||||||
} else {
|
|
||||||
qbs_set(strz2, qbs_add(strz2, func_space(6)));
|
|
||||||
}
|
|
||||||
|
|
||||||
makefit(strz2);
|
|
||||||
qbs_print(strz2, 0);
|
|
||||||
|
|
||||||
} while (FindNextFile(hFind, &fd));
|
|
||||||
FindClose(hFind);
|
|
||||||
|
|
||||||
static ULARGE_INTEGER FreeBytesAvailableToCaller;
|
|
||||||
static ULARGE_INTEGER TotalNumberOfBytes;
|
|
||||||
static ULARGE_INTEGER TotalNumberOfFreeBytes;
|
|
||||||
static int64 bytes;
|
|
||||||
static char *cp;
|
|
||||||
qbs_set(strpath, qbs_add(strpath, qbs_new_txt_len("\0", 1)));
|
|
||||||
cp = (char *)strpath->chr;
|
|
||||||
if (strpath->len == 1)
|
|
||||||
cp = NULL;
|
|
||||||
if (GetDiskFreeSpaceEx(cp, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) {
|
|
||||||
bytes = *(int64 *)(void *)&FreeBytesAvailableToCaller;
|
|
||||||
} else {
|
|
||||||
bytes = 0;
|
|
||||||
}
|
|
||||||
if (func_pos(NULL) > 1) {
|
|
||||||
strz2->len = 0;
|
|
||||||
qbs_print(strz2, 1);
|
|
||||||
} // new line if necessary
|
|
||||||
qbs_set(strz2, qbs_add(qbs_str(bytes), qbs_new_txt_len(" Bytes free", 11)));
|
|
||||||
qbs_print(strz2, 1);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 func__keyhit() {
|
int32 func__keyhit() {
|
||||||
/*
|
/*
|
||||||
//keyhit cyclic buffer
|
//keyhit cyclic buffer
|
||||||
|
@ -33463,65 +33160,6 @@ int32 func_strig(int32 i, int32 controller, int32 passed) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 func__fileexists(qbs *file) {
|
|
||||||
if (new_error)
|
|
||||||
return 0;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(file, qbs_new_txt_len("\0", 1)));
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static int32 x;
|
|
||||||
x = GetFileAttributes(fixdir(strz));
|
|
||||||
if (x == INVALID_FILE_ATTRIBUTES)
|
|
||||||
return 0;
|
|
||||||
if (x & FILE_ATTRIBUTE_DIRECTORY)
|
|
||||||
return 0;
|
|
||||||
return -1;
|
|
||||||
#elif defined(QB64_UNIX)
|
|
||||||
struct stat sb;
|
|
||||||
if (stat(fixdir(strz), &sb) == 0 && S_ISREG(sb.st_mode))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
// generic method (not currently used)
|
|
||||||
static std::ifstream fh;
|
|
||||||
fh.open(fixdir(strz), std::ios::binary | std::ios::in);
|
|
||||||
if (fh.is_open() == NULL) {
|
|
||||||
fh.clear(std::ios::goodbit);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fh.clear(std::ios::goodbit);
|
|
||||||
fh.close();
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 func__direxists(qbs *file) {
|
|
||||||
if (new_error)
|
|
||||||
return 0;
|
|
||||||
static qbs *strz = NULL;
|
|
||||||
if (!strz)
|
|
||||||
strz = qbs_new(0, 0);
|
|
||||||
qbs_set(strz, qbs_add(file, qbs_new_txt_len("\0", 1)));
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static int32 x;
|
|
||||||
x = GetFileAttributes(fixdir(strz));
|
|
||||||
if (x == INVALID_FILE_ATTRIBUTES)
|
|
||||||
return 0;
|
|
||||||
if (x & FILE_ATTRIBUTE_DIRECTORY)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(QB64_UNIX)
|
|
||||||
struct stat sb;
|
|
||||||
if (stat(fixdir(strz), &sb) == 0 && S_ISDIR(sb.st_mode))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return 0; // default response
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 func__console() {
|
int32 func__console() {
|
||||||
if (new_error)
|
if (new_error)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -36318,219 +35956,8 @@ int32 func__resizeheight() { return resize_event_y; }
|
||||||
int32 func__scaledwidth() { return environment_2d__screen_scaled_width; }
|
int32 func__scaledwidth() { return environment_2d__screen_scaled_width; }
|
||||||
int32 func__scaledheight() { return environment_2d__screen_scaled_height; }
|
int32 func__scaledheight() { return environment_2d__screen_scaled_height; }
|
||||||
|
|
||||||
// Get Current Working Directory
|
|
||||||
qbs *func__cwd() {
|
|
||||||
qbs *final, *tqbs;
|
|
||||||
int length;
|
|
||||||
char *buf, *ret;
|
|
||||||
|
|
||||||
#if defined QB64_WINDOWS
|
|
||||||
length = GetCurrentDirectoryA(0, NULL);
|
|
||||||
buf = (char *)malloc(length);
|
|
||||||
if (!buf) {
|
|
||||||
error(7); //"Out of memory"
|
|
||||||
return tqbs;
|
|
||||||
}
|
|
||||||
if (GetCurrentDirectoryA(length, buf) != --length) { // Sanity check
|
|
||||||
free(buf); // It's good practice
|
|
||||||
tqbs = qbs_new(0, 1);
|
|
||||||
error(51); //"Internal error"
|
|
||||||
return tqbs;
|
|
||||||
}
|
|
||||||
#elif defined QB64_UNIX
|
|
||||||
length = 512;
|
|
||||||
while (1) {
|
|
||||||
buf = (char *)malloc(length);
|
|
||||||
if (!buf) {
|
|
||||||
tqbs = qbs_new(0, 1);
|
|
||||||
error(7);
|
|
||||||
return tqbs;
|
|
||||||
}
|
|
||||||
ret = getcwd(buf, length);
|
|
||||||
if (ret)
|
|
||||||
break;
|
|
||||||
if (errno != ERANGE) {
|
|
||||||
tqbs = qbs_new(0, 1);
|
|
||||||
error(51);
|
|
||||||
return tqbs;
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
length += 512;
|
|
||||||
}
|
|
||||||
length = strlen(ret);
|
|
||||||
ret = (char *)realloc(ret, length); // Chops off the null byte
|
|
||||||
if (!ret) {
|
|
||||||
tqbs = qbs_new(0, 1);
|
|
||||||
error(7);
|
|
||||||
return tqbs;
|
|
||||||
}
|
|
||||||
buf = ret;
|
|
||||||
#endif
|
|
||||||
final = qbs_new(length, 1);
|
|
||||||
memcpy(final->chr, buf, length);
|
|
||||||
free(buf);
|
|
||||||
return final;
|
|
||||||
}
|
|
||||||
|
|
||||||
qbs *startDir = NULL; // set on startup
|
|
||||||
qbs *func__startdir() {
|
|
||||||
qbs *temp = qbs_new(0, 1);
|
|
||||||
qbs_set(temp, startDir);
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
qbs *rootDir = NULL; // the dir moved to when program begins
|
|
||||||
|
|
||||||
qbs *func__dir(qbs *context_in) {
|
|
||||||
|
|
||||||
static qbs *context = NULL;
|
|
||||||
if (!context) {
|
|
||||||
context = qbs_new(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
qbs_set(context, qbs_ucase(context_in));
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("TEXT")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DOCUMENT")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("DOCUMENTS")) || qbs_equal(qbs_ucase(context), qbs_new_txt("MY DOCUMENTS"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 5, NULL, 0, osPath))) { // Documents
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("MUSIC")) || qbs_equal(qbs_ucase(context), qbs_new_txt("AUDIO")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("SOUND")) || qbs_equal(qbs_ucase(context), qbs_new_txt("SOUNDS")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY MUSIC"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 13, NULL, 0, osPath))) { // Music
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PICTURE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PICTURES")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("IMAGE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("IMAGES")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY PICTURES"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 39, NULL, 0, osPath))) { // Pictures
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DCIM")) || qbs_equal(qbs_ucase(context), qbs_new_txt("CAMERA")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("CAMERA ROLL")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PHOTO")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("PHOTOS"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 39, NULL, 0, osPath))) { // Pictures
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("MOVIE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("MOVIES")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("VIDEO")) || qbs_equal(qbs_ucase(context), qbs_new_txt("VIDEOS")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY VIDEOS"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 14, NULL, 0, osPath))) { // Videos
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DOWNLOAD")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DOWNLOADS"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x0028, NULL, 0, osPath))) { // user folder
|
|
||||||
// XP & SHGetFolderPathA do not support the concept of a Downloads folder, however it can be constructed
|
|
||||||
mkdir((char *)((qbs_add(qbs_new_txt(osPath), qbs_new_txt_len("\\Downloads\0", 11)))->chr));
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\Downloads\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DESKTOP"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0, NULL, 0, osPath))) { // Desktop
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("APPDATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("APPLICATION DATA")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM DATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DATA"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x001a, NULL, 0, osPath))) { // CSIDL_APPDATA (%APPDATA%)
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("LOCALAPPDATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL APPLICATION DATA")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL PROGRAM DATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL DATA"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x001c, NULL, 0, osPath))) { // CSIDL_LOCAL_APPDATA (%LOCALAPPDATA%)
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x0026, NULL, 0, osPath))) { // CSIDL_PROGRAM_FILES (%PROGRAMFILES%)
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILESX86")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES X86")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES X86")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES 86")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES (X86)")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES (X86)")) ||
|
|
||||||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES(X86)"))) {
|
|
||||||
#ifdef QB64_WINDOWS &&_WIN64
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x002a, NULL, 0, osPath))) { // CSIDL_PROGRAM_FILES (%PROGRAMFILES(X86)%)
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qbs_equal(qbs_ucase(context), qbs_new_txt("TEMP")) || qbs_equal(qbs_ucase(context), qbs_new_txt("TEMP FILES"))) {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH + 1];
|
|
||||||
DWORD pathlen;
|
|
||||||
pathlen = GetTempPathA(261, osPath); //%TEMP%
|
|
||||||
char path[pathlen];
|
|
||||||
memcpy(path, &osPath, pathlen);
|
|
||||||
if (pathlen > 0) {
|
|
||||||
return qbs_new_txt(path);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// general fallback location
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
CHAR osPath[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, 0, NULL, 0, osPath))) { // desktop
|
|
||||||
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
|
|
||||||
}
|
|
||||||
return qbs_new_txt(".\\"); // current location
|
|
||||||
#else
|
|
||||||
return qbs_new_txt("./"); // current location
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void set_dynamic_info();
|
extern void set_dynamic_info();
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
#if defined(QB64_LINUX) && defined(X11)
|
#if defined(QB64_LINUX) && defined(X11)
|
||||||
|
@ -36716,9 +36143,9 @@ int main(int argc, char *argv[]) {
|
||||||
singlespace = qbs_new_cmem(1, 0);
|
singlespace = qbs_new_cmem(1, 0);
|
||||||
singlespace->chr[0] = 32;
|
singlespace->chr[0] = 32;
|
||||||
|
|
||||||
// store _CWD$ for recall using _STARTDIR$ in startDir
|
// store _CWD$ for recall using _STARTDIR$ in g_startDir
|
||||||
startDir = qbs_new(0, 0);
|
g_startDir = qbs_new(0, 0);
|
||||||
qbs_set(startDir, func__cwd());
|
qbs_set(g_startDir, func__cwd());
|
||||||
|
|
||||||
// switch to directory of this EXE file
|
// switch to directory of this EXE file
|
||||||
// http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
// http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
||||||
|
@ -36753,9 +36180,6 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rootDir = qbs_new(0, 0);
|
|
||||||
qbs_set(rootDir, func__cwd());
|
|
||||||
|
|
||||||
unknown_opcode_mess = qbs_new(0, 0);
|
unknown_opcode_mess = qbs_new(0, 0);
|
||||||
qbs_set(unknown_opcode_mess, qbs_new_txt_len("Unknown Opcode ( )\0", 20));
|
qbs_set(unknown_opcode_mess, qbs_new_txt_len("Unknown Opcode ( )\0", 20));
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,20 @@ qbs *qbs_new_txt(const char *);
|
||||||
qbs *qbs_new_txt_len(const char *, int32_t);
|
qbs *qbs_new_txt_len(const char *, int32_t);
|
||||||
qbs *qbs_add(qbs *, qbs *);
|
qbs *qbs_add(qbs *, qbs *);
|
||||||
qbs *qbs_set(qbs *, qbs *);
|
qbs *qbs_set(qbs *, qbs *);
|
||||||
|
int32 qbs_equal(qbs *str1, qbs *str2);
|
||||||
|
qbs *func_space(int32 spaces);
|
||||||
|
void makefit(qbs *text);
|
||||||
|
qbs *qbs_str(int64 value);
|
||||||
|
qbs *qbs_str(int32 value);
|
||||||
|
qbs *qbs_str(int16 value);
|
||||||
|
qbs *qbs_str(int8 value);
|
||||||
|
qbs *qbs_str(uint64 value);
|
||||||
|
qbs *qbs_str(uint32 value);
|
||||||
|
qbs *qbs_str(uint16 value);
|
||||||
|
qbs *qbs_str(uint8 value);
|
||||||
|
qbs *qbs_str(float value);
|
||||||
|
qbs *qbs_str(double value);
|
||||||
|
qbs *qbs_str(long double value);
|
||||||
void qbg_sub_window(float, float, float, float, int32);
|
void qbg_sub_window(float, float, float, float, int32);
|
||||||
extern int32 autodisplay;
|
extern int32 autodisplay;
|
||||||
// GFS forward references
|
// GFS forward references
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
libqb-objs-y += $(PATH_LIBQB)/src/threading.o
|
libqb-objs-y += $(PATH_LIBQB)/src/threading.o
|
||||||
libqb-objs-y += $(PATH_LIBQB)/src/buffer.o
|
libqb-objs-y += $(PATH_LIBQB)/src/buffer.o
|
||||||
libqb-objs-y += $(PATH_LIBQB)/src/filepath.o
|
libqb-objs-y += $(PATH_LIBQB)/src/filepath.o
|
||||||
|
libqb-objs-y += $(PATH_LIBQB)/src/filesystem.o
|
||||||
libqb-objs-y += $(PATH_LIBQB)/src/datetime.o
|
libqb-objs-y += $(PATH_LIBQB)/src/datetime.o
|
||||||
libqb-objs-y += $(PATH_LIBQB)/src/rounding.o
|
libqb-objs-y += $(PATH_LIBQB)/src/rounding.o
|
||||||
|
|
||||||
|
@ -20,4 +21,12 @@ ifeq ($(OS),osx)
|
||||||
libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/mac-key-monitor.o
|
libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/mac-key-monitor.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
$(PATH_LIBQB)/src/%.o: $(PATH_LIBQB)/src/%.cpp
|
||||||
|
$(CXX) -O2 $(CXXFLAGS) -Wall $< -c -o $@
|
||||||
|
|
||||||
|
ifeq ($(OS),osx)
|
||||||
|
$(PATH_LIBQB)/src/%.o: $(PATH_LIBQB)/src/%.mm
|
||||||
|
$(CXX) -O2 $(CXXFLAGS) -Wall $< -c -o $@
|
||||||
|
endif
|
||||||
|
|
||||||
CLEAN_LIST += $(libqb-objs-y) $(libqb-objs-yy) $(libqb-objs-)
|
CLEAN_LIST += $(libqb-objs-y) $(libqb-objs-yy) $(libqb-objs-)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef INCLUDE_LIBQB_FILEPATH_H
|
#ifndef INCLUDE_LIBQB_FILEPATH_H
|
||||||
#define INCLUDE_LIBQB_FILEPATH_H
|
#define INCLUDE_LIBQB_FILEPATH_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct qbs;
|
||||||
|
|
||||||
// Takes a path + filename, and returns just the filename portion
|
// Takes a path + filename, and returns just the filename portion
|
||||||
// Returns either NULL or empty string if it has none.
|
// Returns either NULL or empty string if it has none.
|
||||||
const char *filepath_get_filename(const char *path);
|
const char *filepath_get_filename(const char *path);
|
||||||
|
@ -12,4 +16,15 @@ const char *filepath_get_extension(const char *fliename);
|
||||||
// Returns true if the path is to a file that matches the provided extension
|
// Returns true if the path is to a file that matches the provided extension
|
||||||
bool filepath_has_extension(const char *path, const char *extension);
|
bool filepath_has_extension(const char *path, const char *extension);
|
||||||
|
|
||||||
|
// The following overloaded functions changes the path separators in path based on the OS (path is modified)
|
||||||
|
const char *filepath_fix_directory(char *path);
|
||||||
|
const char *filepath_fix_directory(qbs *path);
|
||||||
|
const char *filepath_fix_directory(std::string &path);
|
||||||
|
|
||||||
|
// Splits a file path into directory and file name
|
||||||
|
void filepath_split(const std::string &filePath, std::string &directory, std::string &fileName);
|
||||||
|
|
||||||
|
// Joins a directory and file name into a file path
|
||||||
|
void filepath_join(std::string &filePath, const std::string &directory, const std::string &fileName);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
26
internal/c/libqb/include/filesystem.h
Normal file
26
internal/c/libqb/include/filesystem.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
// QB64-PE filesystem related functions
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct qbs;
|
||||||
|
|
||||||
|
/// @brief This is a global variable that is set on startup and holds the directory that was current when the program was loaded
|
||||||
|
extern qbs *g_startDir;
|
||||||
|
|
||||||
|
qbs *func__cwd();
|
||||||
|
qbs *func__dir(qbs *qbsContext);
|
||||||
|
int32_t func__direxists(qbs *path);
|
||||||
|
int32_t func__fileexists(qbs *path);
|
||||||
|
qbs *func__startdir();
|
||||||
|
void sub_chdir(qbs *str);
|
||||||
|
qbs *func__files(qbs *qbsFileSpec, int32_t passed);
|
||||||
|
qbs *func__fullpath(qbs *qbsPathName);
|
||||||
|
void sub_files(qbs *str, int32_t passed);
|
||||||
|
void sub_kill(qbs *str);
|
||||||
|
void sub_mkdir(qbs *str);
|
||||||
|
void sub_name(qbs *oldname, qbs *newname);
|
||||||
|
void sub_rmdir(qbs *str);
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
#include "libqb-common.h"
|
#include "libqb-common.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <algorithm>
|
||||||
|
|
||||||
const char *filepath_get_filename(const char *path)
|
#include "../../libqb.h"
|
||||||
{
|
#include "filepath.h"
|
||||||
|
|
||||||
|
const char *filepath_get_filename(const char *path) {
|
||||||
const char *fileName;
|
const char *fileName;
|
||||||
|
|
||||||
if (path == NULL) {
|
if (path == NULL) {
|
||||||
|
@ -32,8 +34,7 @@ const char *filepath_get_filename(const char *path)
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *filepath_get_extension(const char *path)
|
const char *filepath_get_extension(const char *path) {
|
||||||
{
|
|
||||||
const char *extension;
|
const char *extension;
|
||||||
const char *lastOccurance;
|
const char *lastOccurance;
|
||||||
|
|
||||||
|
@ -57,8 +58,7 @@ const char *filepath_get_extension(const char *path)
|
||||||
return (lastOccurance != NULL) ? lastOccurance : extension;
|
return (lastOccurance != NULL) ? lastOccurance : extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool filepath_has_extension(const char *path, const char *extension)
|
bool filepath_has_extension(const char *path, const char *extension) {
|
||||||
{
|
|
||||||
const char *ext1;
|
const char *ext1;
|
||||||
const char *ext2;
|
const char *ext2;
|
||||||
|
|
||||||
|
@ -75,3 +75,86 @@ bool filepath_has_extension(const char *path, const char *extension)
|
||||||
return strcasecmp(ext1, ext2) == 0;
|
return strcasecmp(ext1, ext2) == 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
|
||||||
|
/// @param path The path to fix (contents may be changed)
|
||||||
|
/// @return Returns the C-string for convenience
|
||||||
|
const char *filepath_fix_directory(char *path) {
|
||||||
|
auto len = strlen(path);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
if (path[i] == '/')
|
||||||
|
path[i] = '\\';
|
||||||
|
#else
|
||||||
|
if (path[i] == '\\')
|
||||||
|
path[i] = '/';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
|
||||||
|
/// @param path The path to fix (contents may be changed)
|
||||||
|
/// @return Returns the C-string for convenience
|
||||||
|
const char *filepath_fix_directory(qbs *path) {
|
||||||
|
for (size_t i = 0; i < path->len; i++) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
if (path->chr[i] == '/')
|
||||||
|
path->chr[i] = '\\';
|
||||||
|
#else
|
||||||
|
if (path->chr[i] == '\\')
|
||||||
|
path->chr[i] = '/';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<char *>(path->chr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
|
||||||
|
/// @param path The path to fix (contents may be changed)
|
||||||
|
/// @return Returns the C-string for convenience
|
||||||
|
const char *filepath_fix_directory(std::string &path) {
|
||||||
|
std::transform(path.begin(), path.end(), path.begin(), [](unsigned char c) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
return c == '/' ? '\\' : c;
|
||||||
|
#else
|
||||||
|
return c == '\\' ? '/' : c;
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
return path.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits a file path into directory and file name
|
||||||
|
void filepath_split(const std::string &filePath, std::string &directory, std::string &fileName) {
|
||||||
|
// Find the last occurrence of either '/' or '\\'
|
||||||
|
size_t lastSlash = filePath.find_last_of("/\\");
|
||||||
|
|
||||||
|
if (lastSlash != std::string::npos) {
|
||||||
|
directory = filePath.substr(0, lastSlash + 1); // include the trailing separator
|
||||||
|
fileName = filePath.substr(lastSlash + 1);
|
||||||
|
} else {
|
||||||
|
// No directory separator found
|
||||||
|
directory.clear();
|
||||||
|
fileName = filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joins a directory and file name into a file path
|
||||||
|
void filepath_join(std::string &filePath, const std::string &directory, const std::string &fileName) {
|
||||||
|
// Check if the directory has a trailing separator, and add one if not
|
||||||
|
filePath = directory;
|
||||||
|
|
||||||
|
if (!filePath.empty() && filePath.back() != '/' && filePath.back() != '\\') {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
filePath += '\\';
|
||||||
|
#else
|
||||||
|
filePath += '/';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the file name to the directory
|
||||||
|
filePath += fileName;
|
||||||
|
}
|
||||||
|
|
974
internal/c/libqb/src/filesystem.cpp
Normal file
974
internal/c/libqb/src/filesystem.cpp
Normal file
|
@ -0,0 +1,974 @@
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
// QB64-PE filesystem related functions
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "libqb-common.h"
|
||||||
|
|
||||||
|
#include "filepath.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
#include "../../libqb.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
# include <shlobj.h>
|
||||||
|
#else
|
||||||
|
# include <sys/statvfs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef QB64_BACKSLASH_FILESYSTEM
|
||||||
|
# define FS_PATH_SEPARATOR '\\'
|
||||||
|
#else
|
||||||
|
# define FS_PATH_SEPARATOR '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (FILENAME_MAX > 4096)
|
||||||
|
# define FS_PATHNAME_LENGTH_MAX FILENAME_MAX
|
||||||
|
#else
|
||||||
|
# define FS_PATHNAME_LENGTH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief This is a global variable that is set on startup and holds the directory that was current when the program was loaded
|
||||||
|
qbs *g_startDir = nullptr;
|
||||||
|
|
||||||
|
/// @brief Gets the current working directory
|
||||||
|
/// @return A qbs containing the current working directory or an empty string on error
|
||||||
|
qbs *func__cwd() {
|
||||||
|
std::string path;
|
||||||
|
qbs *qbsFinal;
|
||||||
|
|
||||||
|
path.resize(FILENAME_MAX, '\0');
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (getcwd(&path[0], path.size())) {
|
||||||
|
auto size = strlen(path.c_str());
|
||||||
|
qbsFinal = qbs_new(size, 1);
|
||||||
|
memcpy(qbsFinal->chr, &path[0], size);
|
||||||
|
|
||||||
|
return qbsFinal;
|
||||||
|
} else {
|
||||||
|
if (errno == ERANGE)
|
||||||
|
path.resize(path.size() << 1, '\0'); // buffer size was not sufficient; try again with a larger buffer
|
||||||
|
else
|
||||||
|
break; // some other error occurred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qbsFinal = qbs_new(0, 1);
|
||||||
|
error(QB_ERROR_INTERNAL_ERROR);
|
||||||
|
|
||||||
|
return qbsFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns true if the specified directory exists
|
||||||
|
/// @param path The directory to check for
|
||||||
|
/// @return True if the directory exists
|
||||||
|
static inline bool FS_DirectoryExists(const char *path) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
auto x = GetFileAttributesA(path);
|
||||||
|
|
||||||
|
return x != INVALID_FILE_ATTRIBUTES && (x & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
#else
|
||||||
|
struct stat info;
|
||||||
|
|
||||||
|
return stat(path, &info) == 0 && S_ISDIR(info.st_mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Known directories (primarily Windows based, but we'll do our best to emulate on other platforms)
|
||||||
|
enum class FS_KnownDirectory {
|
||||||
|
HOME = 0,
|
||||||
|
DESKTOP,
|
||||||
|
DOCUMENTS,
|
||||||
|
PICTURES,
|
||||||
|
MUSIC,
|
||||||
|
VIDEOS,
|
||||||
|
DOWNLOAD,
|
||||||
|
APP_DATA,
|
||||||
|
LOCAL_APP_DATA,
|
||||||
|
PROGRAM_DATA,
|
||||||
|
SYSTEM_FONTS,
|
||||||
|
USER_FONTS,
|
||||||
|
TEMP,
|
||||||
|
PROGRAM_FILES,
|
||||||
|
PROGRAM_FILES_32,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
/// @brief Returns the full path for a known directory
|
||||||
|
/// @param kD Is a value from FS_KnownDirectory (above)
|
||||||
|
/// @return The full path that ends with the system path separator
|
||||||
|
static std::string FS_GetKnownDirectory(FS_KnownDirectory kD) {
|
||||||
|
std::string path(FS_PATHNAME_LENGTH_MAX, '\0'); // allocate something that is sufficiently large
|
||||||
|
|
||||||
|
switch (kD) {
|
||||||
|
case FS_KnownDirectory::DESKTOP: // %USERPROFILE%\OneDrive\Desktop
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::DOCUMENTS: // %USERPROFILE%\OneDrive\Documents
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PICTURES: // %USERPROFILE%\OneDrive\Pictures
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_MYPICTURES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::MUSIC: // %USERPROFILE%\Music
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_MYMUSIC | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::VIDEOS: // %USERPROFILE%\Videos
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_MYVIDEO | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::DOWNLOAD: // %USERPROFILE%\Downloads
|
||||||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]))) {
|
||||||
|
// XP & SHGetFolderPathA do not support the concept of a Downloads folder, however it can be constructed
|
||||||
|
path.resize(strlen(path.c_str()));
|
||||||
|
path.append("\\Downloads");
|
||||||
|
mkdir(path.c_str());
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::APP_DATA: // %USERPROFILE%\AppData\Roaming
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::LOCAL_APP_DATA: // %USERPROFILE%\AppData\Local
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PROGRAM_DATA: // %SystemDrive%\ProgramData
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::SYSTEM_FONTS: // %SystemRoot%\Fonts
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_FONTS | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::USER_FONTS: // %USERPROFILE%\AppData\Local\Microsoft\Windows\Fonts
|
||||||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]))) {
|
||||||
|
path.resize(strlen(path.c_str()));
|
||||||
|
path.append("\\Microsoft\\Windows\\Fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::TEMP: // %USERPROFILE%\AppData\Local\Temp
|
||||||
|
GetTempPathA(path.size(), &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PROGRAM_FILES: // %SystemDrive%\Program Files
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PROGRAM_FILES_32: // %SystemDrive%\Program Files (x86)
|
||||||
|
# ifdef _WIN64
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86 | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
# else
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
# endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::HOME: // %USERPROFILE%
|
||||||
|
default:
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we got anything at all
|
||||||
|
if (!strlen(path.c_str())) {
|
||||||
|
path.resize(FS_PATHNAME_LENGTH_MAX, '\0'); // just in case this was shrunk above
|
||||||
|
|
||||||
|
if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0])))
|
||||||
|
path.assign(".\\"); // fallback to the current directory
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the trailing slash
|
||||||
|
path.resize(strlen(path.c_str()));
|
||||||
|
if (path.back() != FS_PATH_SEPARATOR)
|
||||||
|
path.append(1, FS_PATH_SEPARATOR);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/// @brief Returns the full path for a known directory
|
||||||
|
/// @param kD Is a value from FS_KnownDirectory (above)
|
||||||
|
/// @return The full path that ends with the system path separator
|
||||||
|
static std::string FS_GetKnownDirectory(FS_KnownDirectory kD) {
|
||||||
|
std::string path;
|
||||||
|
auto envVar = getenv("HOME");
|
||||||
|
|
||||||
|
switch (kD) {
|
||||||
|
case FS_KnownDirectory::DESKTOP: // %USERPROFILE%\OneDrive\Desktop
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/Desktop");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::DOCUMENTS: // %USERPROFILE%\OneDrive\Documents
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/Documents");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PICTURES: // %USERPROFILE%\OneDrive\Pictures
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/Pictures");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::MUSIC: // %USERPROFILE%\Music
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/Music");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::VIDEOS: // %USERPROFILE%\Videos
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
# ifdef QB64_MACOSX
|
||||||
|
path.append("/Movies");
|
||||||
|
# else
|
||||||
|
path.append("/Videos");
|
||||||
|
# endif
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::DOWNLOAD: // %USERPROFILE%\Downloads
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/Downloads");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::APP_DATA: // %USERPROFILE%\AppData\Roaming
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/.config");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::LOCAL_APP_DATA: // %USERPROFILE%\AppData\Local
|
||||||
|
case FS_KnownDirectory::PROGRAM_DATA: // %SystemDrive%\ProgramData
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/.local/share");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::SYSTEM_FONTS: // %SystemRoot%\Fonts
|
||||||
|
# ifdef QB64_MACOSX
|
||||||
|
path.assign("/System/Library/Fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str())) {
|
||||||
|
path.assign("/Library/Fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
path.assign("/usr/share/fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str())) {
|
||||||
|
path.assign("/usr/local/share/fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::USER_FONTS: // %USERPROFILE%\AppData\Local\Microsoft\Windows\Fonts
|
||||||
|
if (envVar) {
|
||||||
|
path.assign(envVar);
|
||||||
|
# ifdef QB64_MACOSX
|
||||||
|
path.append("/Library/Fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
# else
|
||||||
|
path.append("/.local/share/fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str())) {
|
||||||
|
path.assign(envVar);
|
||||||
|
path.append("/.fonts");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::TEMP: // %USERPROFILE%\AppData\Local\Temp
|
||||||
|
path.assign("/var/tmp");
|
||||||
|
if (!FS_DirectoryExists(path.c_str())) {
|
||||||
|
path.assign("/tmp");
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::PROGRAM_FILES: // %SystemDrive%\Program Files
|
||||||
|
case FS_KnownDirectory::PROGRAM_FILES_32: // %SystemDrive%\Program Files (x86)
|
||||||
|
# ifdef QB64_MACOSX
|
||||||
|
path.assign("/Applications");
|
||||||
|
# else
|
||||||
|
path.assign("/opt");
|
||||||
|
# endif
|
||||||
|
if (!FS_DirectoryExists(path.c_str()))
|
||||||
|
path.clear();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_KnownDirectory::HOME: // %USERPROFILE%
|
||||||
|
default:
|
||||||
|
if (envVar)
|
||||||
|
path.assign(envVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we got anything at all
|
||||||
|
if (!strlen(path.c_str()))
|
||||||
|
path.assign(envVar ? envVar : "./");
|
||||||
|
|
||||||
|
// Add the trailing slash
|
||||||
|
path.resize(strlen(path.c_str()));
|
||||||
|
if (path.back() != FS_PATH_SEPARATOR)
|
||||||
|
path.append(1, FS_PATH_SEPARATOR);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Returns common paths such as My Documents, My Pictures, My Music, Desktop
|
||||||
|
/// @param qbsContext Is the directory type
|
||||||
|
/// @return A qbs containing the directory or an empty string on error
|
||||||
|
qbs *func__dir(qbs *qbsContext) {
|
||||||
|
std::string path, context(reinterpret_cast<char *>(qbsContext->chr), qbsContext->len);
|
||||||
|
|
||||||
|
std::transform(context.begin(), context.end(), context.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||||
|
|
||||||
|
// The following is largely unchanged from what we previously had
|
||||||
|
if (context.compare("TEXT") == 0 || context.compare("DOCUMENT") == 0 || context.compare("DOCUMENTS") == 0 || context.compare("MY DOCUMENTS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::DOCUMENTS);
|
||||||
|
} else if (context.compare("MUSIC") == 0 || context.compare("AUDIO") == 0 || context.compare("SOUND") == 0 || context.compare("SOUNDS") == 0 ||
|
||||||
|
context.compare("MY MUSIC") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::MUSIC);
|
||||||
|
} else if (context.compare("PICTURE") == 0 || context.compare("PICTURES") == 0 || context.compare("IMAGE") == 0 || context.compare("IMAGES") == 0 ||
|
||||||
|
context.compare("MY PICTURES") == 0 || context.compare("DCIM") == 0 || context.compare("CAMERA") == 0 || context.compare("CAMERA ROLL") == 0 ||
|
||||||
|
context.compare("PHOTO") == 0 || context.compare("PHOTOS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::PICTURES);
|
||||||
|
} else if (context.compare("MOVIE") == 0 || context.compare("MOVIES") == 0 || context.compare("VIDEO") == 0 || context.compare("VIDEOS") == 0 ||
|
||||||
|
context.compare("MY VIDEOS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::VIDEOS);
|
||||||
|
} else if (context.compare("DOWNLOAD") == 0 || context.compare("DOWNLOADS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::DOWNLOAD);
|
||||||
|
} else if (context.compare("DESKTOP") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::DESKTOP);
|
||||||
|
} else if (context.compare("APPDATA") == 0 || context.compare("APPLICATION DATA") == 0 || context.compare("PROGRAM DATA") == 0 ||
|
||||||
|
context.compare("DATA") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::APP_DATA);
|
||||||
|
} else if (context.compare("LOCALAPPDATA") == 0 || context.compare("LOCAL APPLICATION DATA") == 0 || context.compare("LOCAL PROGRAM DATA") == 0 ||
|
||||||
|
context.compare("LOCAL DATA") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::LOCAL_APP_DATA);
|
||||||
|
} else if (context.compare("PROGRAMFILES") == 0 || context.compare("PROGRAM FILES") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_FILES);
|
||||||
|
} else if (context.compare("PROGRAMFILESX86") == 0 || context.compare("PROGRAMFILES X86") == 0 || context.compare("PROGRAM FILES X86") == 0 ||
|
||||||
|
context.compare("PROGRAM FILES 86") == 0 || context.compare("PROGRAM FILES (X86)") == 0 || context.compare("PROGRAMFILES (X86)") == 0 ||
|
||||||
|
context.compare("PROGRAM FILES(X86)") == 0 || context.compare("PROGRAMFILES(X86)") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_FILES_32);
|
||||||
|
} else if (context.compare("TMP") == 0 || context.compare("TEMP") == 0 || context.compare("TEMP FILES") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::TEMP);
|
||||||
|
} else if (context.compare("HOME") == 0 || context.compare("USER") == 0 || context.compare("PROFILE") == 0 || context.compare("USERPROFILE") == 0 ||
|
||||||
|
context.compare("USER PROFILE") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::HOME);
|
||||||
|
} else if (context.compare("FONT") == 0 || context.compare("FONTS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::SYSTEM_FONTS);
|
||||||
|
} else if (context.compare("USERFONT") == 0 || context.compare("USER FONT") == 0 || context.compare("USERFONTS") == 0 ||
|
||||||
|
context.compare("USER FONTS") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::USER_FONTS);
|
||||||
|
} else if (context.compare("PROGRAMDATA") == 0 || context.compare("COMMON PROGRAM DATA") == 0) {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_DATA);
|
||||||
|
} else {
|
||||||
|
path = FS_GetKnownDirectory(FS_KnownDirectory::DESKTOP); // anything else defaults to the desktop where the user can easily see stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size = path.size();
|
||||||
|
auto qbsFinal = qbs_new(size, 1);
|
||||||
|
memcpy(qbsFinal->chr, &path[0], size);
|
||||||
|
|
||||||
|
return qbsFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns true if a directory specified exists
|
||||||
|
/// @param path The directory path
|
||||||
|
/// @return True if the directory exists
|
||||||
|
int32_t func__direxists(qbs *path) {
|
||||||
|
if (new_error)
|
||||||
|
return QB_FALSE;
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
|
||||||
|
|
||||||
|
return FS_DirectoryExists(filepath_fix_directory(pathName)) ? QB_TRUE : QB_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns true if a file specified exists
|
||||||
|
/// @param path The file path to check for
|
||||||
|
/// @return True if the file exists
|
||||||
|
static inline bool FS_FileExists(const char *path) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
auto x = GetFileAttributesA(path);
|
||||||
|
|
||||||
|
return x != INVALID_FILE_ATTRIBUTES && !(x & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
#else
|
||||||
|
struct stat info;
|
||||||
|
|
||||||
|
return stat(path, &info) == 0 && S_ISREG(info.st_mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns true if a file specified exists
|
||||||
|
/// @param path The file path to check for
|
||||||
|
/// @return True if the file exists
|
||||||
|
int32_t func__fileexists(qbs *path) {
|
||||||
|
if (new_error)
|
||||||
|
return QB_FALSE;
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
|
||||||
|
|
||||||
|
return FS_FileExists(filepath_fix_directory(pathName)) ? QB_TRUE : QB_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return the startup directory
|
||||||
|
/// @return A qbs containing the directory path
|
||||||
|
qbs *func__startdir() {
|
||||||
|
auto temp = qbs_new(0, 1);
|
||||||
|
|
||||||
|
qbs_set(temp, g_startDir);
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Changes the current directory
|
||||||
|
/// @param str The directory path to change to
|
||||||
|
void sub_chdir(qbs *str) {
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
|
||||||
|
|
||||||
|
if (chdir(filepath_fix_directory(pathName)) == -1)
|
||||||
|
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Checks if s is an empty string (either NULL or zero length)
|
||||||
|
/// @param s A null-terminated string or NULL
|
||||||
|
/// @return False is we have a valid string > length 0
|
||||||
|
static inline bool FS_IsStringEmpty(const char *s) { return s == nullptr || s[0] == '\0'; }
|
||||||
|
|
||||||
|
/// @brief This is a basic pattern matching function used by FS_GetDirectoryEntryName()
|
||||||
|
/// @param fileSpec The pattern to match
|
||||||
|
/// @param fileName The filename to match
|
||||||
|
/// @return True if it is a match, false otherwise
|
||||||
|
static inline bool FS_IsPatternMatching(const char *fileSpec, const char *fileName) {
|
||||||
|
auto spec = fileSpec;
|
||||||
|
auto name = fileName;
|
||||||
|
const char *any = nullptr;
|
||||||
|
|
||||||
|
while (*spec || *name) {
|
||||||
|
switch (*spec) {
|
||||||
|
case '*': // handle wildcard '*' character
|
||||||
|
any = spec;
|
||||||
|
spec++;
|
||||||
|
while (*name && *name != *spec)
|
||||||
|
name++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?': // handle wildcard '?' character
|
||||||
|
spec++;
|
||||||
|
if (*name)
|
||||||
|
name++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // compare non-wildcard characters
|
||||||
|
if (*spec != *name) {
|
||||||
|
if (any && *name)
|
||||||
|
spec = any;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
spec++;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns true if fileSpec has any wildcards
|
||||||
|
/// @param fileSpec The string to check
|
||||||
|
/// @return True if * or ? are found
|
||||||
|
static inline bool FS_HasPattern(const char *fileSpec) { return fileSpec != nullptr && (strchr(fileSpec, '*') || strchr(fileSpec, '?')); }
|
||||||
|
|
||||||
|
/// @brief An MS BASIC PDS DIR$ style function
|
||||||
|
/// @param fileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
|
||||||
|
/// @return Returns a file or directory name matching fileSpec or an empty string when there is nothing left
|
||||||
|
static const char *FS_GetDirectoryEntryName(const char *fileSpec) {
|
||||||
|
static DIR *pDir = nullptr;
|
||||||
|
static char pattern[FS_PATHNAME_LENGTH_MAX];
|
||||||
|
static char entry[FS_PATHNAME_LENGTH_MAX];
|
||||||
|
|
||||||
|
entry[0] = '\0'; // set to an empty string
|
||||||
|
|
||||||
|
if (!FS_IsStringEmpty(fileSpec)) {
|
||||||
|
// We got a filespec. Check if we have one already going and if so, close it
|
||||||
|
if (pDir) {
|
||||||
|
closedir(pDir);
|
||||||
|
pDir = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char dirName[FS_PATHNAME_LENGTH_MAX]; // we only need this for opendir()
|
||||||
|
|
||||||
|
if (FS_HasPattern(fileSpec)) {
|
||||||
|
// We have a pattern. Check if we have a path in it
|
||||||
|
auto p = strrchr(fileSpec, '/'); // try *nix style separator
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
if (!p)
|
||||||
|
p = strrchr(fileSpec, '\\'); // try windows style separator
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
// Split the path and the filespec
|
||||||
|
strncpy(pattern, p + 1, FS_PATHNAME_LENGTH_MAX);
|
||||||
|
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
|
||||||
|
auto len = std::min<size_t>((p - fileSpec) + 1, FS_PATHNAME_LENGTH_MAX - 1);
|
||||||
|
memcpy(dirName, fileSpec, len);
|
||||||
|
dirName[len] = '\0';
|
||||||
|
} else {
|
||||||
|
// No path. Use the current path
|
||||||
|
strncpy(pattern, fileSpec, FS_PATHNAME_LENGTH_MAX);
|
||||||
|
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
|
||||||
|
strcpy(dirName, "./");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No pattern. Check if this is a file and simply return the name if it exists
|
||||||
|
if (FS_FileExists(fileSpec)) {
|
||||||
|
strncpy(entry, filepath_get_filename(fileSpec), FS_PATHNAME_LENGTH_MAX);
|
||||||
|
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, We'll just assume it's a directory
|
||||||
|
strncpy(dirName, fileSpec, FS_PATHNAME_LENGTH_MAX);
|
||||||
|
dirName[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
|
||||||
|
strcpy(pattern, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
pDir = opendir(dirName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDir) {
|
||||||
|
for (;;) {
|
||||||
|
auto pDirent = readdir(pDir);
|
||||||
|
if (!pDirent) {
|
||||||
|
closedir(pDir);
|
||||||
|
pDir = nullptr;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FS_IsPatternMatching(pattern, pDirent->d_name)) {
|
||||||
|
strncpy(entry, pDirent->d_name, FS_PATHNAME_LENGTH_MAX);
|
||||||
|
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief This mimics MS BASIC PDS 7.1 & VBDOS 1.0 DIR$() function
|
||||||
|
/// @param qbsFileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
|
||||||
|
/// @param passed Flags for optional parameters
|
||||||
|
/// @return Retuns a qbs with the directory entry name or an empty string if there are no more entries
|
||||||
|
qbs *func__files(qbs *qbsFileSpec, int32_t passed) {
|
||||||
|
static std::string directory;
|
||||||
|
std::string pathName;
|
||||||
|
const char *entry;
|
||||||
|
qbs *qbsFinal;
|
||||||
|
|
||||||
|
// Check if fresh arguments were passed and we need to begin a new session
|
||||||
|
if (passed) {
|
||||||
|
std::string fileSpec(reinterpret_cast<char *>(qbsFileSpec->chr), qbsFileSpec->len);
|
||||||
|
|
||||||
|
if (FS_DirectoryExists(filepath_fix_directory(fileSpec)))
|
||||||
|
directory = fileSpec;
|
||||||
|
else
|
||||||
|
filepath_split(fileSpec, directory, pathName); // split the file path
|
||||||
|
|
||||||
|
entry = FS_GetDirectoryEntryName(fileSpec.c_str());
|
||||||
|
|
||||||
|
if (FS_IsStringEmpty(entry)) {
|
||||||
|
// This is per MS BASIC PDS 7.1 and VBDOS 1.0 behavior
|
||||||
|
qbsFinal = qbs_new(0, 1);
|
||||||
|
error(QB_ERROR_FILE_NOT_FOUND);
|
||||||
|
return qbsFinal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entry = FS_GetDirectoryEntryName(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath_join(pathName, directory, entry);
|
||||||
|
auto size = strlen(entry);
|
||||||
|
|
||||||
|
if (size && FS_DirectoryExists(pathName.c_str())) {
|
||||||
|
// Add a trailing slash if it is a directory
|
||||||
|
qbsFinal = qbs_new(size + 1, 1);
|
||||||
|
memcpy(qbsFinal->chr, entry, size);
|
||||||
|
qbsFinal->chr[size] = FS_PATH_SEPARATOR;
|
||||||
|
} else {
|
||||||
|
qbsFinal = qbs_new(size, 1);
|
||||||
|
memcpy(qbsFinal->chr, entry, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return qbsFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns the free volume space for a given directory
|
||||||
|
/// @param path A directory that resides on the volume we want the free space for
|
||||||
|
/// @return The free space in bytes
|
||||||
|
static uint64_t FS_GetFreeDiskSpace(const char *path) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
ULARGE_INTEGER freeBytesAvailable;
|
||||||
|
ULARGE_INTEGER totalNumberOfBytes;
|
||||||
|
ULARGE_INTEGER totalNumberOfFreeBytes;
|
||||||
|
|
||||||
|
if (GetDiskFreeSpaceExA(path, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
|
||||||
|
return totalNumberOfFreeBytes.QuadPart;
|
||||||
|
#else
|
||||||
|
struct statvfs stat;
|
||||||
|
|
||||||
|
if (statvfs(path, &stat) == 0)
|
||||||
|
return static_cast<uint64_t>(stat.f_bsize) * stat.f_bfree;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0; // zero if something failed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the fully qualified name (FQN)
|
||||||
|
/// @param path The path name to get the FQN for
|
||||||
|
/// @return The FQN
|
||||||
|
static std::string FS_GetFQN(const char *path) {
|
||||||
|
std::string FQN = path; // fallback
|
||||||
|
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
DWORD size = GetFullPathNameA(path, 0, nullptr, nullptr); // get the required buffer size
|
||||||
|
|
||||||
|
if (size) {
|
||||||
|
FQN.resize(size);
|
||||||
|
if (GetFullPathNameA(path, size, &FQN[0], nullptr))
|
||||||
|
FQN.resize(size - 1); // resize to exclude the null terminator
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
char *result = realpath(path, nullptr);
|
||||||
|
if (result) {
|
||||||
|
FQN = result;
|
||||||
|
free(result); // cleanup memory allocated by realpath
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (FS_DirectoryExists(FQN.c_str()) && FQN.back() != FS_PATH_SEPARATOR)
|
||||||
|
FQN.append(1, FS_PATH_SEPARATOR);
|
||||||
|
|
||||||
|
return FQN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the fully qualified name (FQN)
|
||||||
|
/// @param qbsPathName The path name to get the FQN for
|
||||||
|
/// @return The FQN
|
||||||
|
qbs *func__fullpath(qbs *qbsPathName) {
|
||||||
|
qbs *temp;
|
||||||
|
|
||||||
|
if (!qbsPathName->len) {
|
||||||
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
|
temp = qbs_new(0, 1);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(qbsPathName->chr), qbsPathName->len);
|
||||||
|
filepath_fix_directory(pathName);
|
||||||
|
|
||||||
|
if (!FS_DirectoryExists(pathName.c_str()) && !FS_FileExists(pathName.c_str())) {
|
||||||
|
// Path not found
|
||||||
|
error(QB_ERROR_PATH_NOT_FOUND);
|
||||||
|
temp = qbs_new(0, 1);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathName = FS_GetFQN(pathName.c_str());
|
||||||
|
temp = qbs_new(pathName.size(), 1);
|
||||||
|
memcpy(temp->chr, &pathName[0], pathName.size());
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the short name for a file / directory (if possible)
|
||||||
|
/// @param path The file / directory to get the short name for
|
||||||
|
/// @return The short name
|
||||||
|
static std::string FS_GetShortName(const char *path) {
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
DWORD size = GetShortPathNameA(path, nullptr, 0); // get the required buffer size
|
||||||
|
|
||||||
|
if (size) {
|
||||||
|
std::string shortPath;
|
||||||
|
shortPath.resize(size);
|
||||||
|
if (GetShortPathNameA(path, &shortPath[0], size)) {
|
||||||
|
shortPath.resize(size - 1); // resize to exclude the null terminator
|
||||||
|
return shortPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return path; // return the path as-is for *nix or if GetShortPathNameA failed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Prints a list of files in the current directory using a file specification
|
||||||
|
/// @param str Is a string containing a path (it can include wildcards)
|
||||||
|
/// @param passed Optional parameters
|
||||||
|
void sub_files(qbs *str, int32_t passed) {
|
||||||
|
static qbs *strz = nullptr;
|
||||||
|
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!strz)
|
||||||
|
strz = qbs_new(0, 0);
|
||||||
|
|
||||||
|
std::string fileSpec, directory, pathName;
|
||||||
|
|
||||||
|
if (passed && str->len) {
|
||||||
|
fileSpec.assign(reinterpret_cast<char *>(str->chr), str->len);
|
||||||
|
|
||||||
|
if (FS_DirectoryExists(filepath_fix_directory(fileSpec))) {
|
||||||
|
directory = FS_GetFQN(fileSpec.c_str());
|
||||||
|
} else {
|
||||||
|
std::string d;
|
||||||
|
filepath_split(fileSpec, d, pathName);
|
||||||
|
directory = FS_GetFQN(d.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileSpec = "./";
|
||||||
|
directory = FS_GetFQN(fileSpec.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!directory.size()) {
|
||||||
|
// Invalid filespec
|
||||||
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string shortName = FS_GetShortName(directory.c_str());
|
||||||
|
|
||||||
|
// Print the path
|
||||||
|
qbs_set(strz, qbs_new_txt_len(shortName.c_str(), shortName.size()));
|
||||||
|
qbs_print(strz, 1);
|
||||||
|
|
||||||
|
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
|
||||||
|
filepath_join(pathName, directory, entry);
|
||||||
|
|
||||||
|
if (FS_IsStringEmpty(entry)) {
|
||||||
|
// File not found
|
||||||
|
error(QB_ERROR_FILE_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print directory entries
|
||||||
|
do {
|
||||||
|
fileSpec = FS_GetShortName(pathName.c_str()); // we do not need fileSpec anymore
|
||||||
|
filepath_split(fileSpec, directory, shortName);
|
||||||
|
qbs_set(strz, qbs_new_txt_len(shortName.c_str(), shortName.size()));
|
||||||
|
|
||||||
|
if (strz->len < 12) {
|
||||||
|
// Padding required
|
||||||
|
qbs_set(strz, qbs_add(strz, func_space(12 - strz->len)));
|
||||||
|
for (auto i = 0; i < 12; i++) {
|
||||||
|
if (strz->chr[i] == 46) {
|
||||||
|
memmove(&strz->chr[8], &strz->chr[i], 4);
|
||||||
|
memset(&strz->chr[i], 32, 8 - i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FS_DirectoryExists(pathName.c_str()))
|
||||||
|
qbs_set(strz, qbs_add(strz, qbs_new_txt_len("<DIR> ", 6)));
|
||||||
|
else
|
||||||
|
qbs_set(strz, qbs_add(strz, func_space(6)));
|
||||||
|
|
||||||
|
makefit(strz);
|
||||||
|
qbs_print(strz, 0);
|
||||||
|
|
||||||
|
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
|
||||||
|
filepath_join(pathName, directory, entry);
|
||||||
|
} while (!FS_IsStringEmpty(entry));
|
||||||
|
|
||||||
|
if (func_pos(0) > 1) {
|
||||||
|
// Move to a new line if necessary
|
||||||
|
strz->len = 0;
|
||||||
|
qbs_print(strz, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the free volume space
|
||||||
|
qbs_set(strz, qbs_add(qbs_str(FS_GetFreeDiskSpace(directory.c_str())), qbs_new_txt_len(" Bytes free", 11)));
|
||||||
|
qbs_print(strz, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deletes files from disk
|
||||||
|
/// @param str The file(s) to delete (may contain wildcard at the final level)
|
||||||
|
void sub_kill(qbs *str) {
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string directory, pathName, fileSpec(reinterpret_cast<char *>(str->chr), str->len);
|
||||||
|
|
||||||
|
filepath_split(filepath_fix_directory(fileSpec), directory, pathName); // split the file path
|
||||||
|
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
|
||||||
|
|
||||||
|
// Keep looking through the entries until we file a file
|
||||||
|
while (!FS_IsStringEmpty(entry)) {
|
||||||
|
filepath_join(pathName, directory, entry);
|
||||||
|
|
||||||
|
if (FS_FileExists(pathName.c_str()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have exhausted the entries without ever finding a file
|
||||||
|
if (FS_IsStringEmpty(entry)) {
|
||||||
|
// This behavior is per QBasic 1.1
|
||||||
|
error(QB_ERROR_FILE_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all matches
|
||||||
|
do {
|
||||||
|
// We'll delete only if it is a file
|
||||||
|
if (FS_FileExists(pathName.c_str())) {
|
||||||
|
if (remove(pathName.c_str())) {
|
||||||
|
auto i = errno;
|
||||||
|
|
||||||
|
if (i == ENOENT) {
|
||||||
|
error(QB_ERROR_FILE_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
} // file not found
|
||||||
|
|
||||||
|
if (i == EACCES) {
|
||||||
|
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
|
||||||
|
return;
|
||||||
|
} // path / file access error
|
||||||
|
|
||||||
|
error(QB_ERROR_BAD_FILE_NAME); // bad file name (assumed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
|
||||||
|
filepath_join(pathName, directory, entry);
|
||||||
|
} while (!FS_IsStringEmpty(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Creates a new directory
|
||||||
|
/// @param str The directory path name to create
|
||||||
|
void sub_mkdir(qbs *str) {
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
|
||||||
|
|
||||||
|
#ifdef QB64_WINDOWS
|
||||||
|
if (mkdir(filepath_fix_directory(pathName)) == -1)
|
||||||
|
#else
|
||||||
|
if (mkdir(filepath_fix_directory(pathName), S_IRWXU | S_IRWXG) == -1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (errno == EEXIST) {
|
||||||
|
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
|
||||||
|
return;
|
||||||
|
} // path / file access error
|
||||||
|
|
||||||
|
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Renames a file or directory
|
||||||
|
/// @param oldname The old file / directory name
|
||||||
|
/// @param newname The new file / directory name
|
||||||
|
void sub_name(qbs *oldname, qbs *newname) {
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string pathNameOld(reinterpret_cast<char *>(oldname->chr), oldname->len), pathNameNew(reinterpret_cast<char *>(newname->chr), newname->len);
|
||||||
|
|
||||||
|
if (rename(filepath_fix_directory(pathNameOld), filepath_fix_directory(pathNameNew))) {
|
||||||
|
auto i = errno;
|
||||||
|
|
||||||
|
if (i == ENOENT) {
|
||||||
|
error(QB_ERROR_FILE_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
} // file not found
|
||||||
|
|
||||||
|
if (i == EINVAL) {
|
||||||
|
error(QB_ERROR_BAD_FILE_NAME);
|
||||||
|
return;
|
||||||
|
} // bad file name
|
||||||
|
|
||||||
|
if (i == EACCES) {
|
||||||
|
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
|
||||||
|
return;
|
||||||
|
} // path / file access error
|
||||||
|
|
||||||
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // illegal function call (assumed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deletes an empty directory
|
||||||
|
/// @param str The path name of the directory to delete
|
||||||
|
void sub_rmdir(qbs *str) {
|
||||||
|
if (new_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
|
||||||
|
|
||||||
|
if (rmdir(filepath_fix_directory(pathName)) == -1) {
|
||||||
|
if (errno == ENOTEMPTY) {
|
||||||
|
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
|
||||||
|
return;
|
||||||
|
} // path/file access error; not an empty directory
|
||||||
|
|
||||||
|
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
#define STB_VORBIS_HEADER_ONLY
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "extras/stb_vorbis.c"
|
#include "extras/stb_vorbis.c"
|
||||||
|
#include "filepath.h"
|
||||||
#include "miniaudio.h"
|
#include "miniaudio.h"
|
||||||
#include "mutex.h"
|
#include "mutex.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -28,9 +29,6 @@
|
||||||
#define INVALID_MEM_LOCK 1073741821
|
#define INVALID_MEM_LOCK 1073741821
|
||||||
// This should be defined elsewhere (in libqb?). Since it is not, we are doing it here
|
// This should be defined elsewhere (in libqb?). Since it is not, we are doing it here
|
||||||
#define MEM_TYPE_SOUND 5
|
#define MEM_TYPE_SOUND 5
|
||||||
// In QuickBASIC false means 0 and true means -1 (sad, but true XD)
|
|
||||||
#define QB_FALSE MA_FALSE
|
|
||||||
#define QB_TRUE -MA_TRUE
|
|
||||||
// This is returned to the caller if handle allocation fails with a -1
|
// This is returned to the caller if handle allocation fails with a -1
|
||||||
// CreateHandle() does not return 0 because it is a valid internal handle
|
// CreateHandle() does not return 0 because it is a valid internal handle
|
||||||
// Handle 0 is 'handled' as a special case
|
// Handle 0 is 'handled' as a special case
|
||||||
|
@ -87,7 +85,7 @@ struct RawStream {
|
||||||
libqb_mutex *m; // we'll use a mutex to give exclusive access to resources used by both threads
|
libqb_mutex *m; // we'll use a mutex to give exclusive access to resources used by both threads
|
||||||
bool stop; // set this to true to stop supply of samples completely (including silent samples)
|
bool stop; // set this to true to stop supply of samples completely (including silent samples)
|
||||||
|
|
||||||
static const size_t DEFAULT_SIZE = 1024; // this is almost twice the amout what miniaudio actually asks for in frameCount
|
static const size_t DEFAULT_SIZE = 1024; // this is almost twice the amount what miniaudio actually asks for in frameCount
|
||||||
|
|
||||||
// Delete default, copy and move constructors and assignments
|
// Delete default, copy and move constructors and assignments
|
||||||
RawStream() = delete;
|
RawStream() = delete;
|
||||||
|
@ -118,7 +116,7 @@ struct RawStream {
|
||||||
libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors
|
libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors
|
||||||
consumer->cursor = 0; // reset the cursor
|
consumer->cursor = 0; // reset the cursor
|
||||||
consumer->data.clear(); // clear the consumer vector
|
consumer->data.clear(); // clear the consumer vector
|
||||||
std::swap(consumer, producer); // quicky swap the Buffer pointers
|
std::swap(consumer, producer); // quickly swap the Buffer pointers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief This pushes a sample frame at the end of the queue. This is mutex protected and called by the main thread
|
/// @brief This pushes a sample frame at the end of the queue. This is mutex protected and called by the main thread
|
||||||
|
@ -255,7 +253,7 @@ static ma_data_source_vtable rawStreamDataSourceVtable = {
|
||||||
/// @brief This creates, initializes and sets up a raw stream for playback
|
/// @brief This creates, initializes and sets up a raw stream for playback
|
||||||
/// @param pmaEngine This should come from the QBPE sound engine
|
/// @param pmaEngine This should come from the QBPE sound engine
|
||||||
/// @param pmaSound This should come from a QBPE sound handle
|
/// @param pmaSound This should come from a QBPE sound handle
|
||||||
/// @return Returns a pointer to a data souce if successful, NULL otherwise
|
/// @return Returns a pointer to a data source if successful, NULL otherwise
|
||||||
static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) {
|
static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) {
|
||||||
if (!pmaEngine || !pmaSound) { // these should not be NULL
|
if (!pmaEngine || !pmaSound) { // these should not be NULL
|
||||||
AUDIO_DEBUG_PRINT("Invalid arguments");
|
AUDIO_DEBUG_PRINT("Invalid arguments");
|
||||||
|
@ -287,7 +285,7 @@ static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) {
|
||||||
result = ma_sound_init_from_data_source(pmaEngine, &pRawStream->maDataSource, MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, NULL,
|
result = ma_sound_init_from_data_source(pmaEngine, &pRawStream->maDataSource, MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, NULL,
|
||||||
pmaSound); // attach data source to the ma_sound
|
pmaSound); // attach data source to the ma_sound
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
AUDIO_DEBUG_PRINT("Error %i: failed to initalize sound from data source", result);
|
AUDIO_DEBUG_PRINT("Error %i: failed to initialize sound from data source", result);
|
||||||
|
|
||||||
delete pRawStream;
|
delete pRawStream;
|
||||||
|
|
||||||
|
@ -423,7 +421,7 @@ class BufferMap {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief This is a PSG class that handles all kinds of sound generatation for BEEP, SOUND and PLAY
|
/// @brief This is a PSG class that handles all kinds of sound generation for BEEP, SOUND and PLAY
|
||||||
class PSG {
|
class PSG {
|
||||||
public:
|
public:
|
||||||
/// @brief Various types of waveform that can be generated
|
/// @brief Various types of waveform that can be generated
|
||||||
|
@ -496,7 +494,7 @@ class PSG {
|
||||||
auto neededFrames = (ma_uint64)(waveDuration * rawStream->sampleRate);
|
auto neededFrames = (ma_uint64)(waveDuration * rawStream->sampleRate);
|
||||||
|
|
||||||
if (!neededFrames || maWaveform.config.frequency >= 20000 || mixCursor + neededFrames > waveBuffer.size()) {
|
if (!neededFrames || maWaveform.config.frequency >= 20000 || mixCursor + neededFrames > waveBuffer.size()) {
|
||||||
AUDIO_DEBUG_PRINT("Not generating any wavefrom. Frames = %llu, frequency = %lf, cursor = %llu", neededFrames, maWaveform.config.frequency,
|
AUDIO_DEBUG_PRINT("Not generating any waveform. Frames = %llu, frequency = %lf, cursor = %llu", neededFrames, maWaveform.config.frequency,
|
||||||
mixCursor);
|
mixCursor);
|
||||||
return; // nothing to do
|
return; // nothing to do
|
||||||
}
|
}
|
||||||
|
@ -769,7 +767,7 @@ class PSG {
|
||||||
if (processedChar == 'X') { // "X" + VARPTR$()
|
if (processedChar == 'X') { // "X" + VARPTR$()
|
||||||
// A minimum of 3 bytes is need to read the address
|
// A minimum of 3 bytes is need to read the address
|
||||||
if (currentState.length < 3) {
|
if (currentState.length < 3) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,12 +789,12 @@ class PSG {
|
||||||
continue;
|
continue;
|
||||||
} else if (currentChar == '=') { // "=" + VARPTR$()
|
} else if (currentChar == '=') { // "=" + VARPTR$()
|
||||||
if (dots) {
|
if (dots) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numberEntered) {
|
if (numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,7 +814,7 @@ class PSG {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (currentState.length < 3) {
|
if (currentState.length < 3) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,14 +866,14 @@ class PSG {
|
||||||
default:
|
default:
|
||||||
// bit type?
|
// bit type?
|
||||||
if ((currentChar & 64) == 0) {
|
if ((currentChar & 64) == 0) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto x2 = currentChar & 63;
|
auto x2 = currentChar & 63;
|
||||||
|
|
||||||
if (x2 > 56) {
|
if (x2 > 56) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
} // valid number of bits?
|
} // valid number of bits?
|
||||||
|
|
||||||
|
@ -897,7 +895,7 @@ class PSG {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d > 2147483647.0 || d < -2147483648.0) {
|
if (d > 2147483647.0 || d < -2147483648.0) {
|
||||||
error(5); // out of range value!
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // out of range value!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,7 +904,7 @@ class PSG {
|
||||||
continue;
|
continue;
|
||||||
} else if (currentChar >= '0' && currentChar <= '9') {
|
} else if (currentChar >= '0' && currentChar <= '9') {
|
||||||
if (dots || numberEntered == 2) {
|
if (dots || numberEntered == 2) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,7 +918,7 @@ class PSG {
|
||||||
continue;
|
continue;
|
||||||
} else if (currentChar == '.') {
|
} else if (currentChar == '.') {
|
||||||
if (followUp != 7 && followUp != 1 && followUp != 4) {
|
if (followUp != 7 && followUp != 1 && followUp != 4) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -932,14 +930,14 @@ class PSG {
|
||||||
follow_up:
|
follow_up:
|
||||||
if (followUp == 10) { // Q...
|
if (followUp == 10) { // Q...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number > 100) { // 0 - 100 ms
|
if (number > 100) { // 0 - 100 ms
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -950,14 +948,14 @@ class PSG {
|
||||||
break;
|
break;
|
||||||
} else if (followUp == 9) { // @...
|
} else if (followUp == 9) { // @...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if ((WaveformType)number <= WaveformType::NONE || (WaveformType)number >= WaveformType::COUNT) {
|
if ((WaveformType)number <= WaveformType::NONE || (WaveformType)number >= WaveformType::COUNT) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -969,14 +967,14 @@ class PSG {
|
||||||
break;
|
break;
|
||||||
} else if (followUp == 8) { // V...
|
} else if (followUp == 8) { // V...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number > MAX_MML_VOLUME) {
|
if (number > MAX_MML_VOLUME) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,7 +988,7 @@ class PSG {
|
||||||
if (numberEntered) {
|
if (numberEntered) {
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
if (number < 1 || number > 64) {
|
if (number < 1 || number > 64) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
duration = 1.0 / (tempo / 60.0) * (4.0 / ((double)number));
|
duration = 1.0 / (tempo / 60.0) * (4.0 / ((double)number));
|
||||||
|
@ -1027,7 +1025,7 @@ class PSG {
|
||||||
break;
|
break;
|
||||||
} else if (followUp == 6) { // T...
|
} else if (followUp == 6) { // T...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1045,7 +1043,7 @@ class PSG {
|
||||||
break;
|
break;
|
||||||
} else if (followUp == 5) { // M...
|
} else if (followUp == 5) { // M...
|
||||||
if (numberEntered) {
|
if (numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,7 +1071,7 @@ class PSG {
|
||||||
background = false;
|
background = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1082,14 +1080,14 @@ class PSG {
|
||||||
continue;
|
continue;
|
||||||
} else if (followUp == 4) { // N...
|
} else if (followUp == 4) { // N...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number > 84) {
|
if (number > 84) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,14 +1096,14 @@ class PSG {
|
||||||
goto follow_up_1;
|
goto follow_up_1;
|
||||||
} else if (followUp == 3) { // O...
|
} else if (followUp == 3) { // O...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number > MAX_OCTAVE) {
|
if (number > MAX_OCTAVE) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,14 +1115,14 @@ class PSG {
|
||||||
break;
|
break;
|
||||||
} else if (followUp == 2) { // L...
|
} else if (followUp == 2) { // L...
|
||||||
if (!numberEntered) {
|
if (!numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number < MIN_LENGTH || number > MAX_LENGTH) {
|
if (number < MIN_LENGTH || number > MAX_LENGTH) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1137,7 +1135,7 @@ class PSG {
|
||||||
} else if (followUp == 1) { // A-G...
|
} else if (followUp == 1) { // A-G...
|
||||||
if (currentChar == '-') {
|
if (currentChar == '-') {
|
||||||
if (noteShifted || numberEntered) {
|
if (noteShifted || numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,7 +1146,7 @@ class PSG {
|
||||||
}
|
}
|
||||||
if (currentChar == '+' || currentChar == '#') {
|
if (currentChar == '+' || currentChar == '#') {
|
||||||
if (noteShifted || numberEntered) {
|
if (noteShifted || numberEntered) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,7 +1161,7 @@ class PSG {
|
||||||
numberEntered = 0;
|
numberEntered = 0;
|
||||||
|
|
||||||
if (number < 0 || number > 64) {
|
if (number < 0 || number > 64) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1276,12 +1274,12 @@ class PSG {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numberEntered || followUp) {
|
if (numberEntered || followUp) {
|
||||||
error(5); // unhandled data
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // unhandled data
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1536,7 +1534,7 @@ struct AudioEngine {
|
||||||
soundHandles[handle]->isUsed = false;
|
soundHandles[handle]->isUsed = false;
|
||||||
soundHandles[handle]->type = SoundHandle::Type::NONE;
|
soundHandles[handle]->type = SoundHandle::Type::NONE;
|
||||||
|
|
||||||
// Save the free hanndle to lowestFreeHandle if it is lower than lowestFreeHandle
|
// Save the free handle to lowestFreeHandle if it is lower than lowestFreeHandle
|
||||||
if (handle < lowestFreeHandle)
|
if (handle < lowestFreeHandle)
|
||||||
lowestFreeHandle = handle;
|
lowestFreeHandle = handle;
|
||||||
|
|
||||||
|
@ -1588,13 +1586,13 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((frequency < 37.0 && frequency != 0) || frequency > 32767.0 || lengthInClockTicks < 0.0 || lengthInClockTicks > 65535.0) {
|
if ((frequency < 37.0 && frequency != 0) || frequency > 32767.0 || lengthInClockTicks < 0.0 || lengthInClockTicks > 65535.0) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passed & 1) {
|
if (passed & 1) {
|
||||||
if (volume < PSG::MIN_VOLUME || volume > PSG::MAX_VOLUME) {
|
if (volume < PSG::MIN_VOLUME || volume > PSG::MAX_VOLUME) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
audioEngine.psg->SetAmplitude(volume);
|
audioEngine.psg->SetAmplitude(volume);
|
||||||
|
@ -1602,7 +1600,7 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
|
||||||
|
|
||||||
if (passed & 2) {
|
if (passed & 2) {
|
||||||
if (panning < PSG::PAN_LEFT || panning > PSG::PAN_RIGHT) {
|
if (panning < PSG::PAN_LEFT || panning > PSG::PAN_RIGHT) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
audioEngine.psg->SetPanning((float)panning);
|
audioEngine.psg->SetPanning((float)panning);
|
||||||
|
@ -1610,7 +1608,7 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
|
||||||
|
|
||||||
if (passed & 4) {
|
if (passed & 4) {
|
||||||
if ((PSG::WaveformType)waveform <= PSG::WaveformType::NONE || (PSG::WaveformType)waveform >= PSG::WaveformType::COUNT) {
|
if ((PSG::WaveformType)waveform <= PSG::WaveformType::NONE || (PSG::WaveformType)waveform >= PSG::WaveformType::COUNT) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
audioEngine.psg->SetWaveformType((PSG::WaveformType)waveform);
|
audioEngine.psg->SetWaveformType((PSG::WaveformType)waveform);
|
||||||
|
@ -1654,7 +1652,7 @@ void sub_play(const qbs *str) {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This returns the sample rate from ma engine if ma is initialized.
|
/// This returns the sample rate from ma engine if ma is initialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>miniaudio sample rtate</returns>
|
/// <returns>miniaudio sample rate</returns>
|
||||||
int32_t func__sndrate() { return audioEngine.sampleRate; }
|
int32_t func__sndrate() { return audioEngine.sampleRate; }
|
||||||
|
|
||||||
/// @brief Creates a ma_decoder and ma_sound from a memory buffer for a valid sound handle
|
/// @brief Creates a ma_decoder and ma_sound from a memory buffer for a valid sound handle
|
||||||
|
@ -1722,7 +1720,7 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
|
||||||
if (!reqs)
|
if (!reqs)
|
||||||
reqs = qbs_new(0, 0);
|
reqs = qbs_new(0, 0);
|
||||||
|
|
||||||
// Alocate a sound handle
|
// Allocate a sound handle
|
||||||
int32_t handle = audioEngine.CreateHandle();
|
int32_t handle = audioEngine.CreateHandle();
|
||||||
if (handle < 1) // We are not expected to open files with handle 0
|
if (handle < 1) // We are not expected to open files with handle 0
|
||||||
return INVALID_SOUND_HANDLE;
|
return INVALID_SOUND_HANDLE;
|
||||||
|
@ -1734,7 +1732,7 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
|
||||||
if (passed && requirements->len)
|
if (passed && requirements->len)
|
||||||
qbs_set(reqs, qbs_ucase(requirements)); // Convert tmp str to perm str
|
qbs_set(reqs, qbs_ucase(requirements)); // Convert tmp str to perm str
|
||||||
|
|
||||||
// Set the flags to specifiy how we want the audio file to be opened
|
// Set the flags to specify how we want the audio file to be opened
|
||||||
if (passed && requirements->len && func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_STREAM), 1)) {
|
if (passed && requirements->len && func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_STREAM), 1)) {
|
||||||
audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_STREAM; // Check if the user wants to stream the file
|
audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_STREAM; // Check if the user wants to stream the file
|
||||||
AUDIO_DEBUG_PRINT("Sound will stream");
|
AUDIO_DEBUG_PRINT("Sound will stream");
|
||||||
|
@ -1757,8 +1755,8 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
|
||||||
qbs_set(fileNameZ, qbs_add(fileName, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
|
qbs_set(fileNameZ, qbs_add(fileName, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
|
||||||
|
|
||||||
// Forward the request to miniaudio to open the sound file
|
// Forward the request to miniaudio to open the sound file
|
||||||
audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, (const char *)fileNameZ->chr, audioEngine.soundHandles[handle]->maFlags, NULL,
|
audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, filepath_fix_directory(fileNameZ), audioEngine.soundHandles[handle]->maFlags,
|
||||||
NULL, &audioEngine.soundHandles[handle]->maSound);
|
NULL, NULL, &audioEngine.soundHandles[handle]->maSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE
|
// If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE
|
||||||
|
@ -1782,7 +1780,7 @@ void sub__sndclose(int32_t handle) {
|
||||||
if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle)) {
|
if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle)) {
|
||||||
// If we have a raw stream then force it to push all it's data to miniaudio
|
// If we have a raw stream then force it to push all it's data to miniaudio
|
||||||
// Note that this will take care of checking if the handle is a raw steam and other stuff
|
// Note that this will take care of checking if the handle is a raw steam and other stuff
|
||||||
// So it is completly safe to call it this way
|
// So it is completely safe to call it this way
|
||||||
sub__sndrawdone(handle, true);
|
sub__sndrawdone(handle, true);
|
||||||
|
|
||||||
if (audioEngine.soundHandles[handle]->type == SoundHandle::Type::RAW)
|
if (audioEngine.soundHandles[handle]->type == SoundHandle::Type::RAW)
|
||||||
|
@ -1826,7 +1824,7 @@ int32_t func__sndcopy(int32_t src_handle) {
|
||||||
} else if (audioEngine.soundHandles[src_handle]->maDecoder) {
|
} else if (audioEngine.soundHandles[src_handle]->maDecoder) {
|
||||||
AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_decoder");
|
AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_decoder");
|
||||||
|
|
||||||
dst_handle = audioEngine.CreateHandle(); // alocate a sound handle
|
dst_handle = audioEngine.CreateHandle(); // allocate a sound handle
|
||||||
if (dst_handle < 1)
|
if (dst_handle < 1)
|
||||||
return INVALID_SOUND_HANDLE;
|
return INVALID_SOUND_HANDLE;
|
||||||
|
|
||||||
|
@ -1847,7 +1845,7 @@ int32_t func__sndcopy(int32_t src_handle) {
|
||||||
} else {
|
} else {
|
||||||
AUDIO_DEBUG_PRINT("Doing regular miniaudio sound copy");
|
AUDIO_DEBUG_PRINT("Doing regular miniaudio sound copy");
|
||||||
|
|
||||||
dst_handle = audioEngine.CreateHandle(); // alocate a sound handle
|
dst_handle = audioEngine.CreateHandle(); // allocate a sound handle
|
||||||
if (dst_handle < 1)
|
if (dst_handle < 1)
|
||||||
return INVALID_SOUND_HANDLE;
|
return INVALID_SOUND_HANDLE;
|
||||||
|
|
||||||
|
@ -2182,7 +2180,7 @@ int32_t func__sndopenraw() {
|
||||||
if (!audioEngine.isInitialized)
|
if (!audioEngine.isInitialized)
|
||||||
return INVALID_SOUND_HANDLE;
|
return INVALID_SOUND_HANDLE;
|
||||||
|
|
||||||
// Alocate a sound handle
|
// Allocate a sound handle
|
||||||
int32_t handle = audioEngine.CreateHandle();
|
int32_t handle = audioEngine.CreateHandle();
|
||||||
if (handle < 1)
|
if (handle < 1)
|
||||||
return INVALID_SOUND_HANDLE;
|
return INVALID_SOUND_HANDLE;
|
||||||
|
@ -2415,7 +2413,7 @@ mem_block func__memsound(int32_t handle, int32_t targetChannel, int32_t passed)
|
||||||
AUDIO_DEBUG_PRINT("Format = %u, channels = %u, frames = %llu", maFormat, channels, sampleFrames);
|
AUDIO_DEBUG_PRINT("Format = %u, channels = %u, frames = %llu", maFormat, channels, sampleFrames);
|
||||||
|
|
||||||
// Setup type: This was not done in the old code
|
// Setup type: This was not done in the old code
|
||||||
// But we are doing it here. By examing the type the user can now figure out if they have to use FP32 or integers
|
// But we are doing it here. By examining the type the user can now figure out if they have to use FP32 or integers
|
||||||
switch (maFormat) {
|
switch (maFormat) {
|
||||||
case ma_format::ma_format_f32:
|
case ma_format::ma_format_f32:
|
||||||
mb.type = 4 + 256; // FP32
|
mb.type = 4 + 256; // FP32
|
||||||
|
@ -2497,7 +2495,7 @@ void snd_init() {
|
||||||
|
|
||||||
// Get and save the engine sample rate. We will let miniaudio choose the device sample rate for us
|
// Get and save the engine sample rate. We will let miniaudio choose the device sample rate for us
|
||||||
// This ensures we get the lowest latency
|
// This ensures we get the lowest latency
|
||||||
// Set the resource manager decorder sample rate to the device sample rate (miniaudio engine bug?)
|
// Set the resource manager decoder sample rate to the device sample rate (miniaudio engine bug?)
|
||||||
audioEngine.maResourceManager.config.decodedSampleRate = audioEngine.sampleRate = ma_engine_get_sample_rate(&audioEngine.maEngine);
|
audioEngine.maResourceManager.config.decodedSampleRate = audioEngine.sampleRate = ma_engine_get_sample_rate(&audioEngine.maEngine);
|
||||||
|
|
||||||
// Set the initialized flag as true
|
// Set the initialized flag as true
|
||||||
|
|
|
@ -189,13 +189,13 @@ struct FontManager {
|
||||||
FT_Face face; // FreeType face object
|
FT_Face face; // FreeType face object
|
||||||
FT_Pos monospaceWidth; // the monospace width (if font was loaded as monospace, else zero)
|
FT_Pos monospaceWidth; // the monospace width (if font was loaded as monospace, else zero)
|
||||||
FT_Pos defaultHeight; // default (max) pixel height the user wants
|
FT_Pos defaultHeight; // default (max) pixel height the user wants
|
||||||
FT_Pos baseline; // font baeline in pixels
|
FT_Pos baseline; // font baseline in pixels
|
||||||
int32_t options; // fonts options that were passed by QB64 while loading the font
|
int32_t options; // fonts options that were passed by QB64 while loading the font
|
||||||
|
|
||||||
/// @brief Manages a single glyph in a font
|
/// @brief Manages a single glyph in a font
|
||||||
struct Glyph {
|
struct Glyph {
|
||||||
// Usually the bitmap size & metrics returned by FT for mono and gray can be the same
|
// Usually the bitmap size & metrics returned by FT for mono and gray can be the same
|
||||||
// But it's a bad idea to assume that is the case everytime
|
// But it's a bad idea to assume that is the case every time
|
||||||
struct Bitmap {
|
struct Bitmap {
|
||||||
uint8_t *data; // pointer to the raw pixels
|
uint8_t *data; // pointer to the raw pixels
|
||||||
FT_Vector size; // bitmap width & height in pixels
|
FT_Vector size; // bitmap width & height in pixels
|
||||||
|
@ -205,7 +205,7 @@ struct FontManager {
|
||||||
|
|
||||||
FT_UInt index; // glyph index
|
FT_UInt index; // glyph index
|
||||||
Bitmap bmpMono; // monochrome bitmap in 8-bit format
|
Bitmap bmpMono; // monochrome bitmap in 8-bit format
|
||||||
Bitmap bmpGray; // anti-aliased bitamp in 8-bit format
|
Bitmap bmpGray; // anti-aliased bitmap in 8-bit format
|
||||||
Bitmap *bitmap; // pointer to the currently selected bitmap (mono / gray)
|
Bitmap *bitmap; // pointer to the currently selected bitmap (mono / gray)
|
||||||
|
|
||||||
// Delete copy and move constructors and assignments
|
// Delete copy and move constructors and assignments
|
||||||
|
@ -435,7 +435,7 @@ struct FontManager {
|
||||||
auto newGlyph = new Glyph;
|
auto newGlyph = new Glyph;
|
||||||
|
|
||||||
if (!newGlyph) {
|
if (!newGlyph) {
|
||||||
FONT_DEBUG_PRINT("Failed to allocate mmemory");
|
FONT_DEBUG_PRINT("Failed to allocate memory");
|
||||||
return nullptr; // failed to allocate memory
|
return nullptr; // failed to allocate memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,7 +573,7 @@ struct FontManager {
|
||||||
|
|
||||||
if (h >= vectorSize) {
|
if (h >= vectorSize) {
|
||||||
// Scan through the entire vector and return a slot that is not being used
|
// Scan through the entire vector and return a slot that is not being used
|
||||||
// Ideally this should execute in extremely few (if at all) senarios
|
// Ideally this should execute in extremely few (if at all) scenarios
|
||||||
// Also, this loop should not execute if size is 0
|
// Also, this loop should not execute if size is 0
|
||||||
for (h = 0; h < vectorSize; h++) {
|
for (h = 0; h < vectorSize; h++) {
|
||||||
if (!fonts[h]->isUsed) {
|
if (!fonts[h]->isUsed) {
|
||||||
|
@ -1018,14 +1018,14 @@ int32_t func__UFontHeight(int32_t qb64_fh, int32_t passed) {
|
||||||
if (passed) {
|
if (passed) {
|
||||||
// Check if a valid font handle was passed
|
// Check if a valid font handle was passed
|
||||||
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
||||||
error(258);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qb64_fh = write_page->font; // else get the current write page font handle
|
qb64_fh = write_page->font; // else get the current write page font handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// For buint-in fonts return the handle value (which is = font height)
|
// For built-in fonts return the handle value (which is = font height)
|
||||||
if (qb64_fh < 32)
|
if (qb64_fh < 32)
|
||||||
return qb64_fh;
|
return qb64_fh;
|
||||||
|
|
||||||
|
@ -1041,7 +1041,7 @@ int32_t func__UFontHeight(int32_t qb64_fh, int32_t passed) {
|
||||||
return fnt->defaultHeight;
|
return fnt->defaultHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Returns the text widht in pixels
|
/// @brief Returns the text width in pixels
|
||||||
/// @param text The text to calculate the width for
|
/// @param text The text to calculate the width for
|
||||||
/// @param utf_encoding The UTF encoding of the text (0 = ASCII, 8 = UTF-8, 16 - UTF-16, 32 = UTF-32)
|
/// @param utf_encoding The UTF encoding of the text (0 = ASCII, 8 = UTF-8, 16 - UTF-16, 32 = UTF-32)
|
||||||
/// @param qb64_fh A QB64 font handle (this can be a builtin font as well)
|
/// @param qb64_fh A QB64 font handle (this can be a builtin font as well)
|
||||||
|
@ -1056,7 +1056,7 @@ int32_t func__UPrintWidth(const qbs *text, int32_t utf_encoding, int32_t qb64_fh
|
||||||
// Check UTF argument
|
// Check UTF argument
|
||||||
if (passed & 1) {
|
if (passed & 1) {
|
||||||
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1066,7 +1066,7 @@ int32_t func__UPrintWidth(const qbs *text, int32_t utf_encoding, int32_t qb64_fh
|
||||||
// Check if a valid font handle was passed
|
// Check if a valid font handle was passed
|
||||||
if (passed & 2) {
|
if (passed & 2) {
|
||||||
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
||||||
error(258);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1122,14 +1122,14 @@ int32_t func__ULineSpacing(int32_t qb64_fh, int32_t passed) {
|
||||||
if (passed) {
|
if (passed) {
|
||||||
// Check if a valid font handle was passed
|
// Check if a valid font handle was passed
|
||||||
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
||||||
error(258);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qb64_fh = write_page->font; // else get the current write page font handle
|
qb64_fh = write_page->font; // else get the current write page font handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// For buint-in fonts return the handle value (which is = font height)
|
// For built-in fonts return the handle value (which is = font height)
|
||||||
if (qb64_fh < 32)
|
if (qb64_fh < 32)
|
||||||
return qb64_fh;
|
return qb64_fh;
|
||||||
|
|
||||||
|
@ -1160,7 +1160,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
|
|
||||||
// Check if we are in text mode and generate an error if we are
|
// Check if we are in text mode and generate an error if we are
|
||||||
if (write_page->text) {
|
if (write_page->text) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,7 +1177,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
// Check UTF argument
|
// Check UTF argument
|
||||||
if (passed & 2) {
|
if (passed & 2) {
|
||||||
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1187,7 +1187,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
// Check if a valid font handle was passed
|
// Check if a valid font handle was passed
|
||||||
if (passed & 4) {
|
if (passed & 4) {
|
||||||
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
||||||
error(258);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1464,7 +1464,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
free(drawBuf);
|
free(drawBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Calculate the starting pixel positions of each chancter to an array. First one being zero.
|
/// @brief Calculate the starting pixel positions of each codepoint to an array. First one being zero.
|
||||||
/// This also calculates the pixel position of the last + 1 character.
|
/// This also calculates the pixel position of the last + 1 character.
|
||||||
/// @param text Text for which the data needs to be calculated. This can be unicode encoded
|
/// @param text Text for which the data needs to be calculated. This can be unicode encoded
|
||||||
/// @param arr A QB64 LONG array. This should be codepoints + 1 long. If the array is shorter additional calculated data is ignored
|
/// @param arr A QB64 LONG array. This should be codepoints + 1 long. If the array is shorter additional calculated data is ignored
|
||||||
|
@ -1488,7 +1488,7 @@ int32_t func__UCharPos(const qbs *text, void *arr, int32_t utf_encoding, int32_t
|
||||||
// Check UTF argument
|
// Check UTF argument
|
||||||
if (passed & 2) {
|
if (passed & 2) {
|
||||||
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1498,7 +1498,7 @@ int32_t func__UCharPos(const qbs *text, void *arr, int32_t utf_encoding, int32_t
|
||||||
// Check if a valid font handle was passed
|
// Check if a valid font handle was passed
|
||||||
if (passed & 4) {
|
if (passed & 4) {
|
||||||
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
|
||||||
error(258);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -49,13 +49,6 @@
|
||||||
// This is returned to the caller if something goes wrong while loading the image
|
// This is returned to the caller if something goes wrong while loading the image
|
||||||
#define INVALID_IMAGE_HANDLE -1
|
#define INVALID_IMAGE_HANDLE -1
|
||||||
|
|
||||||
// TODO: We should have QB64 error code enums in some common place
|
|
||||||
// Maybe consolidate error() & friends, and these enums
|
|
||||||
#define ERROR_ILLEGAL_FUNCTION_CALL 5
|
|
||||||
#define ERROR_INTERNAL_ERROR 51
|
|
||||||
#define ERROR_BAD_FILE_NAME 64
|
|
||||||
#define ERROR_INVALID_HANDLE 258
|
|
||||||
|
|
||||||
#ifdef QB64_WINDOWS
|
#ifdef QB64_WINDOWS
|
||||||
# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_))
|
# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_))
|
||||||
#else
|
#else
|
||||||
|
@ -294,7 +287,7 @@ static uint32_t *image_qoi_load_from_memory(const uint8_t *buffer, size_t size,
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Decodes an image file freom a file using the dr_pcx & stb_image libraries.
|
/// @brief Decodes an image file from a file using the dr_pcx & stb_image libraries.
|
||||||
/// @param fileName A valid filename
|
/// @param fileName A valid filename
|
||||||
/// @param xOut Out: width in pixels. This cannot be NULL
|
/// @param xOut Out: width in pixels. This cannot be NULL
|
||||||
/// @param yOut Out: height in pixels. This cannot be NULL
|
/// @param yOut Out: height in pixels. This cannot be NULL
|
||||||
|
@ -386,7 +379,7 @@ static inline uint8_t image_clamp_component(int32_t n) { return n < 0 ? 0 : n >
|
||||||
|
|
||||||
/// @brief This takes in a 32bpp (BGRA) image raw data and spits out an 8bpp raw image along with it's 256 color (BGRA) palette.
|
/// @brief This takes in a 32bpp (BGRA) image raw data and spits out an 8bpp raw image along with it's 256 color (BGRA) palette.
|
||||||
/// @param src32 The source raw image data. This must be in BGRA format and not NULL
|
/// @param src32 The source raw image data. This must be in BGRA format and not NULL
|
||||||
/// @param w The widht of the image in pixels
|
/// @param w The width of the image in pixels
|
||||||
/// @param h The height of the image in pixels
|
/// @param h The height of the image in pixels
|
||||||
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
|
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
|
||||||
/// @return A pointer to a 8bpp raw image or NULL if operation failed
|
/// @return A pointer to a 8bpp raw image or NULL if operation failed
|
||||||
|
@ -448,7 +441,7 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h,
|
||||||
/// If the number of unique colors in the 32bpp image > 256, then the functions returns a NULL.
|
/// If the number of unique colors in the 32bpp image > 256, then the functions returns a NULL.
|
||||||
/// Unlike image_convert_8bpp(), no 'real' conversion takes place.
|
/// Unlike image_convert_8bpp(), no 'real' conversion takes place.
|
||||||
/// @param src The source raw image data. This must be in BGRA format and not NULL
|
/// @param src The source raw image data. This must be in BGRA format and not NULL
|
||||||
/// @param w The widht of the image in pixels
|
/// @param w The width of the image in pixels
|
||||||
/// @param h The height of the image in pixels
|
/// @param h The height of the image in pixels
|
||||||
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
|
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
|
||||||
/// @return A pointer to a 8bpp raw image or NULL if operation failed
|
/// @return A pointer to a 8bpp raw image or NULL if operation failed
|
||||||
|
@ -545,7 +538,7 @@ static inline uint32_t image_swap_red_blue(uint32_t clr) { return ((clr & 0xFF00
|
||||||
|
|
||||||
/// @brief This function loads an image into memory and returns valid LONG image handle values that are less than -1
|
/// @brief This function loads an image into memory and returns valid LONG image handle values that are less than -1
|
||||||
/// @param qbsFileName The filename or memory buffer (see requirements below) of the image
|
/// @param qbsFileName The filename or memory buffer (see requirements below) of the image
|
||||||
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware acclerated), 256=8bpp or 257=8bpp (without palette remap)
|
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware accelerated), 256=8bpp or 257=8bpp (without palette remap)
|
||||||
/// @param qbsRequirements A qbs that can contain one or more of: hardware, memory, adaptive
|
/// @param qbsRequirements A qbs that can contain one or more of: hardware, memory, adaptive
|
||||||
/// @param passed How many parameters were passed?
|
/// @param passed How many parameters were passed?
|
||||||
/// @return Valid LONG image handle values that are less than -1 or -1 on failure
|
/// @return Valid LONG image handle values that are less than -1 or -1 on failure
|
||||||
|
@ -573,7 +566,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
|
||||||
|
|
||||||
if ((bpp != 32) && (bpp != 256)) { // invalid BPP?
|
if ((bpp != 32) && (bpp != 256)) { // invalid BPP?
|
||||||
IMAGE_DEBUG_PRINT("Invalid bpp (0x%X)", bpp);
|
IMAGE_DEBUG_PRINT("Invalid bpp (0x%X)", bpp);
|
||||||
error(5);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
return INVALID_IMAGE_HANDLE;
|
return INVALID_IMAGE_HANDLE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -594,8 +587,6 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
|
||||||
|
|
||||||
IMAGE_DEBUG_PRINT("Parsing requirements string: %s", requirements.c_str());
|
IMAGE_DEBUG_PRINT("Parsing requirements string: %s", requirements.c_str());
|
||||||
|
|
||||||
// requirements.find(REQUIREMENT_STRING_MEMORY) != std::string::npos
|
|
||||||
|
|
||||||
if (requirements.find("HARDWARE") != std::string::npos && bpp == 32) {
|
if (requirements.find("HARDWARE") != std::string::npos && bpp == 32) {
|
||||||
isHardwareImage = true;
|
isHardwareImage = true;
|
||||||
IMAGE_DEBUG_PRINT("Hardware image selected");
|
IMAGE_DEBUG_PRINT("Hardware image selected");
|
||||||
|
@ -627,7 +618,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
|
||||||
pixels = image_decode_from_memory(qbsFileName->chr, qbsFileName->len, &x, &y, scaler);
|
pixels = image_decode_from_memory(qbsFileName->chr, qbsFileName->len, &x, &y, scaler);
|
||||||
} else {
|
} else {
|
||||||
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
|
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
|
||||||
pixels = image_decode_from_file(fileName.c_str(), &x, &y, scaler);
|
pixels = image_decode_from_file(filepath_fix_directory(fileName), &x, &y, scaler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pixels)
|
if (!pixels)
|
||||||
|
@ -718,7 +709,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
|
||||||
/// @param qbsFileName The file path name to save to
|
/// @param qbsFileName The file path name to save to
|
||||||
/// @param imageHandle Optional: The image handle. If omitted, then this is _DISPLAY()
|
/// @param imageHandle Optional: The image handle. If omitted, then this is _DISPLAY()
|
||||||
/// @param qbsRequirements Optional: Extra format and setting arguments
|
/// @param qbsRequirements Optional: Extra format and setting arguments
|
||||||
/// @param passed Argument bitmask
|
/// @param passed Optional parameters
|
||||||
void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, int32_t passed) {
|
void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, int32_t passed) {
|
||||||
enum struct SaveFormat { PNG = 0, QOI, BMP, TGA, JPG, HDR };
|
enum struct SaveFormat { PNG = 0, QOI, BMP, TGA, JPG, HDR };
|
||||||
static const char *formatName[] = {"png", "qoi", "bmp", "tga", "jpg", "hdr"};
|
static const char *formatName[] = {"png", "qoi", "bmp", "tga", "jpg", "hdr"};
|
||||||
|
@ -728,7 +719,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
|
|
||||||
if (!qbsFileName->len) { // empty file names not allowed
|
if (!qbsFileName->len) { // empty file names not allowed
|
||||||
IMAGE_DEBUG_PRINT("Empty file name");
|
IMAGE_DEBUG_PRINT("Empty file name");
|
||||||
error(ERROR_BAD_FILE_NAME);
|
error(QB_ERROR_BAD_FILE_NAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,11 +734,11 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
imageHandle = -imageHandle;
|
imageHandle = -imageHandle;
|
||||||
|
|
||||||
if (imageHandle >= nextimg) {
|
if (imageHandle >= nextimg) {
|
||||||
error(ERROR_INVALID_HANDLE);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!img[imageHandle].valid) {
|
if (!img[imageHandle].valid) {
|
||||||
error(ERROR_INVALID_HANDLE);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -759,11 +750,11 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
|
|
||||||
// Safety
|
// Safety
|
||||||
if (imageHandle >= nextimg) {
|
if (imageHandle >= nextimg) {
|
||||||
error(ERROR_INVALID_HANDLE);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!img[imageHandle].valid) {
|
if (!img[imageHandle].valid) {
|
||||||
error(ERROR_INVALID_HANDLE);
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -792,6 +783,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
IMAGE_DEBUG_PRINT("Format selected: %s", formatName[(int)format]);
|
IMAGE_DEBUG_PRINT("Format selected: %s", formatName[(int)format]);
|
||||||
|
|
||||||
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
|
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
|
||||||
|
filepath_fix_directory(fileName);
|
||||||
|
|
||||||
// Check if fileName has a valid extension and add one if it does not have one
|
// Check if fileName has a valid extension and add one if it does not have one
|
||||||
if (fileName.length() > 4) { // must be at least n.ext
|
if (fileName.length() > 4) { // must be at least n.ext
|
||||||
|
@ -889,7 +881,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
pixels.resize(width * height);
|
pixels.resize(width * height);
|
||||||
|
|
||||||
if (img[imageHandle].bits_per_pixel == 32) { // BGRA pixels
|
if (img[imageHandle].bits_per_pixel == 32) { // BGRA pixels
|
||||||
IMAGE_DEBUG_PRINT("Coverting BGRA surface to RGBA");
|
IMAGE_DEBUG_PRINT("Converting BGRA surface to RGBA");
|
||||||
|
|
||||||
auto p = img[imageHandle].offset32;
|
auto p = img[imageHandle].offset32;
|
||||||
|
|
||||||
|
@ -898,7 +890,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
} else { // indexed pixels
|
} else { // indexed pixels
|
||||||
IMAGE_DEBUG_PRINT("Coverting BGRA indexed surface to RGBA");
|
IMAGE_DEBUG_PRINT("Converting BGRA indexed surface to RGBA");
|
||||||
auto p = img[imageHandle].offset;
|
auto p = img[imageHandle].offset;
|
||||||
|
|
||||||
for (size_t i = 0; i < pixels.size(); i++) {
|
for (size_t i = 0; i < pixels.size(); i++) {
|
||||||
|
@ -915,7 +907,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
stbi_write_png_compression_level = 100;
|
stbi_write_png_compression_level = 100;
|
||||||
if (!stbi_write_png(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 0)) {
|
if (!stbi_write_png(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 0)) {
|
||||||
IMAGE_DEBUG_PRINT("stbi_write_png() failed");
|
IMAGE_DEBUG_PRINT("stbi_write_png() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -928,28 +920,28 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
|
|
||||||
if (!qoi_write(fileName.c_str(), pixels.data(), &desc)) {
|
if (!qoi_write(fileName.c_str(), pixels.data(), &desc)) {
|
||||||
IMAGE_DEBUG_PRINT("qoi_write() failed");
|
IMAGE_DEBUG_PRINT("qoi_write() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case SaveFormat::BMP: {
|
case SaveFormat::BMP: {
|
||||||
if (!stbi_write_bmp(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
|
if (!stbi_write_bmp(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
|
||||||
IMAGE_DEBUG_PRINT("stbi_write_bmp() failed");
|
IMAGE_DEBUG_PRINT("stbi_write_bmp() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case SaveFormat::TGA: {
|
case SaveFormat::TGA: {
|
||||||
if (!stbi_write_tga(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
|
if (!stbi_write_tga(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
|
||||||
IMAGE_DEBUG_PRINT("stbi_write_tga() failed");
|
IMAGE_DEBUG_PRINT("stbi_write_tga() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case SaveFormat::JPG: {
|
case SaveFormat::JPG: {
|
||||||
if (!stbi_write_jpg(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 100)) {
|
if (!stbi_write_jpg(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 100)) {
|
||||||
IMAGE_DEBUG_PRINT("stbi_write_jpg() failed");
|
IMAGE_DEBUG_PRINT("stbi_write_jpg() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -974,12 +966,12 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
|
|
||||||
if (!stbi_write_hdr(fileName.c_str(), width, height, HDRComponents, HDRPixels.data())) {
|
if (!stbi_write_hdr(fileName.c_str(), width, height, HDRComponents, HDRPixels.data())) {
|
||||||
IMAGE_DEBUG_PRINT("stbi_write_hdr() failed");
|
IMAGE_DEBUG_PRINT("stbi_write_hdr() failed");
|
||||||
error(ERROR_ILLEGAL_FUNCTION_CALL);
|
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
IMAGE_DEBUG_PRINT("Save handler not implemented");
|
IMAGE_DEBUG_PRINT("Save handler not implemented");
|
||||||
error(ERROR_INTERNAL_ERROR);
|
error(QB_ERROR_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
#include "extended_math.h"
|
||||||
|
#include "filepath.h"
|
||||||
|
#include "filesystem.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include "rounding.h"
|
#include "rounding.h"
|
||||||
#include "extended_math.h"
|
|
||||||
|
|
||||||
extern int32 func__cinp(int32 toggle,
|
extern int32 func__cinp(int32 toggle,
|
||||||
int32 passed); // Console INP scan code reader
|
int32 passed); // Console INP scan code reader
|
||||||
|
@ -88,14 +90,9 @@ void requestKeyboardOverlayImage(int32 handle) {
|
||||||
|
|
||||||
// extern functions
|
// extern functions
|
||||||
|
|
||||||
extern qbs *func__dir(qbs *context);
|
|
||||||
|
|
||||||
extern int32 func__scaledwidth();
|
extern int32 func__scaledwidth();
|
||||||
extern int32 func__scaledheight();
|
extern int32 func__scaledheight();
|
||||||
|
|
||||||
extern qbs *func__cwd();
|
|
||||||
extern qbs *func__startdir();
|
|
||||||
|
|
||||||
extern void sub__fps(double fps, int32 passed);
|
extern void sub__fps(double fps, int32 passed);
|
||||||
|
|
||||||
extern void sub__resize(int32 on_off, int32 stretch_smooth);
|
extern void sub__resize(int32 on_off, int32 stretch_smooth);
|
||||||
|
@ -178,8 +175,6 @@ extern int32 func_windowexists();
|
||||||
extern int32 func_screenicon();
|
extern int32 func_screenicon();
|
||||||
extern int32 func_screenwidth();
|
extern int32 func_screenwidth();
|
||||||
extern int32 func_screenheight();
|
extern int32 func_screenheight();
|
||||||
extern int32 func__borderwidth();
|
|
||||||
extern int32 func__titlebarheight();
|
|
||||||
extern void sub_screenicon();
|
extern void sub_screenicon();
|
||||||
extern void sub__console(int32);
|
extern void sub__console(int32);
|
||||||
extern int32 func__console();
|
extern int32 func__console();
|
||||||
|
@ -191,8 +186,6 @@ extern int32 func__hasfocus();
|
||||||
extern void set_foreground_window(ptrszint i);
|
extern void set_foreground_window(ptrszint i);
|
||||||
extern qbs *func__title();
|
extern qbs *func__title();
|
||||||
extern int32 func__handle();
|
extern int32 func__handle();
|
||||||
extern int32 func__fileexists(qbs *);
|
|
||||||
extern int32 func__direxists(qbs *);
|
|
||||||
extern int32 func_stick(int32 i, int32 axis_group, int32 passed);
|
extern int32 func_stick(int32 i, int32 axis_group, int32 passed);
|
||||||
extern int32 func_strig(int32 i, int32 controller, int32 passed);
|
extern int32 func_strig(int32 i, int32 controller, int32 passed);
|
||||||
extern void sub__maptriangle(int32 cull_options, float sx1, float sy1,
|
extern void sub__maptriangle(int32 cull_options, float sx1, float sy1,
|
||||||
|
@ -224,7 +217,6 @@ extern void field_get(int32 fileno, int64 seekpos, int32 passed);
|
||||||
extern void field_put(int32 fileno, int64 seekpos, int32 passed);
|
extern void field_put(int32 fileno, int64 seekpos, int32 passed);
|
||||||
extern int32 func__keydown(int32 x);
|
extern int32 func__keydown(int32 x);
|
||||||
extern int32 func__keyhit();
|
extern int32 func__keyhit();
|
||||||
extern void sub_files(qbs *str, int32 passed);
|
|
||||||
extern int32 func_lpos(int32);
|
extern int32 func_lpos(int32);
|
||||||
extern void sub__printimage(int32 i);
|
extern void sub__printimage(int32 i);
|
||||||
extern float func__mousemovementx(int32 context, int32 passed);
|
extern float func__mousemovementx(int32 context, int32 passed);
|
||||||
|
@ -246,7 +238,6 @@ extern qbs *func__clipboard();
|
||||||
extern int32 func__clipboardimage();
|
extern int32 func__clipboardimage();
|
||||||
extern void sub__clipboardimage(int32 src);
|
extern void sub__clipboardimage(int32 src);
|
||||||
extern int32 func__exit();
|
extern int32 func__exit();
|
||||||
extern char *fixdir(qbs *);
|
|
||||||
extern void revert_input_check();
|
extern void revert_input_check();
|
||||||
extern int32 func__openhost(qbs *);
|
extern int32 func__openhost(qbs *);
|
||||||
extern int32 func__openconnection(int32);
|
extern int32 func__openconnection(int32);
|
||||||
|
@ -276,7 +267,6 @@ extern void sub__putimage(double f_dx1, double f_dy1, double f_dx2,
|
||||||
double f_sy1, double f_sx2, double f_sy2,
|
double f_sy1, double f_sx2, double f_sy2,
|
||||||
int32 passed);
|
int32 passed);
|
||||||
extern int32 selectfont(int32 f, img_struct *im);
|
extern int32 selectfont(int32 f, img_struct *im);
|
||||||
extern void sndsetup();
|
|
||||||
extern uint32 sib();
|
extern uint32 sib();
|
||||||
extern uint32 sib_mod0();
|
extern uint32 sib_mod0();
|
||||||
extern uint8 *rm8();
|
extern uint8 *rm8();
|
||||||
|
@ -299,10 +289,6 @@ extern void sub_defseg(int32 segment, int32 passed);
|
||||||
extern int32 func_peek(int32 offset);
|
extern int32 func_peek(int32 offset);
|
||||||
extern void sub_poke(int32 offset, int32 value);
|
extern void sub_poke(int32 offset, int32 value);
|
||||||
extern void more_return_points();
|
extern void more_return_points();
|
||||||
extern void qb64_generatesound(double f, double l, uint8 wait);
|
|
||||||
extern uint8 *soundwave(double frequency, double length, double volume,
|
|
||||||
double fadein, double fadeout);
|
|
||||||
extern int32 wavesize(double length);
|
|
||||||
extern qbs *qbs_new_descriptor();
|
extern qbs *qbs_new_descriptor();
|
||||||
extern void qbs_free_descriptor(qbs *str);
|
extern void qbs_free_descriptor(qbs *str);
|
||||||
extern void qbs_free(qbs *str);
|
extern void qbs_free(qbs *str);
|
||||||
|
@ -524,14 +510,8 @@ extern qbs *func_input(int32 n, int32 i, int32 passed);
|
||||||
extern int32 func__statusCode(int32 handle);
|
extern int32 func__statusCode(int32 handle);
|
||||||
|
|
||||||
extern double func_sqr(double value);
|
extern double func_sqr(double value);
|
||||||
extern void snd_check();
|
|
||||||
extern qbs *func_command(int32 index, int32 passed);
|
extern qbs *func_command(int32 index, int32 passed);
|
||||||
extern int32 func__commandcount();
|
extern int32 func__commandcount();
|
||||||
extern void sub_kill(qbs *str);
|
|
||||||
extern void sub_name(qbs *oldname, qbs *newname);
|
|
||||||
extern void sub_chdir(qbs *str);
|
|
||||||
extern void sub_mkdir(qbs *str);
|
|
||||||
extern void sub_rmdir(qbs *str);
|
|
||||||
extern long double pow2(long double x, long double y);
|
extern long double pow2(long double x, long double y);
|
||||||
extern int32 func_freefile();
|
extern int32 func_freefile();
|
||||||
extern void sub__mousehide();
|
extern void sub__mousehide();
|
||||||
|
@ -1333,8 +1313,8 @@ void sub_chain(qbs *f) {
|
||||||
extensions_ready:
|
extensions_ready:
|
||||||
|
|
||||||
// normalize dir slashes
|
// normalize dir slashes
|
||||||
fixdir(f_exe);
|
filepath_fix_directory(f_exe);
|
||||||
fixdir(f_bas);
|
filepath_fix_directory(f_bas);
|
||||||
|
|
||||||
// get path (strip paths from f_exe & f_bas)
|
// get path (strip paths from f_exe & f_bas)
|
||||||
f_path->len = 0;
|
f_path->len = 0;
|
||||||
|
|
|
@ -625,6 +625,29 @@ id.specialformat = "[?]"
|
||||||
id.hr_syntax = "FILES fileSpec$"
|
id.hr_syntax = "FILES fileSpec$"
|
||||||
regid
|
regid
|
||||||
|
|
||||||
|
clearid
|
||||||
|
id.n = qb64prefix$ + "Files"
|
||||||
|
id.musthave = "$"
|
||||||
|
id.subfunc = 1
|
||||||
|
id.callname = "func__files"
|
||||||
|
id.args = 1
|
||||||
|
id.arg = MKL$(STRINGTYPE - ISPOINTER)
|
||||||
|
id.specialformat = "[?]"
|
||||||
|
id.ret = STRINGTYPE - ISPOINTER
|
||||||
|
id.hr_syntax = "_FILES$([fileSpec$])"
|
||||||
|
regid
|
||||||
|
|
||||||
|
clearid
|
||||||
|
id.n = qb64prefix$ + "FullPath"
|
||||||
|
id.musthave = "$"
|
||||||
|
id.subfunc = 1
|
||||||
|
id.callname = "func__fullpath"
|
||||||
|
id.args = 1
|
||||||
|
id.arg = MKL$(STRINGTYPE - ISPOINTER)
|
||||||
|
id.ret = STRINGTYPE - ISPOINTER
|
||||||
|
id.hr_syntax = "_FULLPATH$(pathName$)"
|
||||||
|
regid
|
||||||
|
|
||||||
clearid
|
clearid
|
||||||
id.n = qb64prefix$ + "PrintImage": id.Dependency = DEPENDENCY_PRINTER
|
id.n = qb64prefix$ + "PrintImage": id.Dependency = DEPENDENCY_PRINTER
|
||||||
id.subfunc = 2
|
id.subfunc = 2
|
||||||
|
|
|
@ -5,5 +5,5 @@ listOfKeywords$ = listOfKeywords$ + "_GLCOPYTEXSUBIMAGE2D@_GLCULLFACE@_GLDELETEL
|
||||||
listOfKeywords$ = listOfKeywords$ + "_GLPOPATTRIB@_GLPOPCLIENTATTRIB@_GLPOPMATRIX@_GLPOPNAME@_GLPRIORITIZETEXTURES@_GLPUSHATTRIB@_GLPUSHCLIENTATTRIB@_GLPUSHMATRIX@_GLPUSHNAME@_GLRASTERPOS2D@_GLRASTERPOS2DV@_GLRASTERPOS2F@_GLRASTERPOS2FV@_GLRASTERPOS2I@_GLRASTERPOS2IV@_GLRASTERPOS2S@_GLRASTERPOS2SV@_GLRASTERPOS3D@_GLRASTERPOS3DV@_GLRASTERPOS3F@_GLRASTERPOS3FV@_GLRASTERPOS3I@_GLRASTERPOS3IV@_GLRASTERPOS3S@_GLRASTERPOS3SV@_GLRASTERPOS4D@_GLRASTERPOS4DV@_GLRASTERPOS4F@_GLRASTERPOS4FV@_GLRASTERPOS4I@_GLRASTERPOS4IV@_GLRASTERPOS4S@_GLRASTERPOS4SV@_GLREADBUFFER@_GLREADPIXELS@_GLRECTD@_GLRECTDV@_GLRECTF@_GLRECTFV@_GLRECTI@_GLRECTIV@_GLRECTS@_GLRECTSV@_GLRENDERMODE@_GLROTATED@_GLROTATEF@_GLSCALED@_GLSCALEF@_GLSCISSOR@_GLSELECTBUFFER@_GLSHADEMODEL@_GLSTENCILFUNC@_GLSTENCILMASK@_GLSTENCILOP@_GLTEXCOORD1D@_GLTEXCOORD1DV@_GLTEXCOORD1F@_GLTEXCOORD1FV@_GLTEXCOORD1I@_GLTEXCOORD1IV@_GLTEXCOORD1S@_GLTEXCOORD1SV@_GLTEXCOORD2D@_GLTEXCOORD2DV@_GLTEXCOORD2F@_GLTEXCOORD2FV@_GLTEXCOORD2I@_GLTEXCOORD2IV@_GLTEXCOORD2S@_GLTEXCOORD2SV@_GLTEXCOORD3D@_GLTEXCOORD3DV@_GLTEXCOORD3F@_GLTEXCOORD3FV@_GLTEXCOORD3I@_GLTEXCOORD3IV@_GLTEXCOORD3S@_GLTEXCOORD3SV@_GLTEXCOORD4D@_GLTEXCOORD4DV@_GLTEXCOORD4F@_GLTEXCOORD4FV@_GLTEXCOORD4I@_GLTEXCOORD4IV@_GLTEXCOORD4S@_GLTEXCOORD4SV@_GLTEXCOORDPOINTER@_GLTEXENVF@_GLTEXENVFV@_GLTEXENVI@_GLTEXENVIV@_GLTEXGEND@_GLTEXGENDV@_GLTEXGENF@_GLTEXGENFV@_GLTEXGENI@_GLTEXGENIV@_GLTEXIMAGE1D@_GLTEXIMAGE2D@_GLTEXPARAMETERF@_GLTEXPARAMETERFV@_GLTEXPARAMETERI@_GLTEXPARAMETERIV@_GLTEXSUBIMAGE1D@_GLTEXSUBIMAGE2D@_GLTRANSLATED@_GLTRANSLATEF@_GLVERTEX2D@_GLVERTEX2DV@_GLVERTEX2F@_GLVERTEX2FV@_GLVERTEX2I@_GLVERTEX2IV@_GLVERTEX2S@_GLVERTEX2SV@_GLVERTEX3D@_GLVERTEX3DV@_GLVERTEX3F@_GLVERTEX3FV@_GLVERTEX3I@_GLVERTEX3IV@_GLVERTEX3S@_GLVERTEX3SV@_GLVERTEX4D@_GLVERTEX4DV@_GLVERTEX4F@_GLVERTEX4FV@_GLVERTEX4I@_GLVERTEX4IV@_GLVERTEX4S@_GLVERTEX4SV@_GLVERTEXPOINTER@_GLVIEWPORT@SMOOTH@STRETCH@_ANTICLOCKWISE@_BEHIND@_CLEAR@_FILLBACKGROUND@_GLUPERSPECTIVE@_HARDWARE@_HARDWARE1@_KEEPBACKGROUND@_NONE@_OFF@_ONLY@_ONLYBACKGROUND@_ONTOP@_SEAMLESS@_SMOOTH@_SMOOTHSHRUNK@_SMOOTHSTRETCHED@"
|
listOfKeywords$ = listOfKeywords$ + "_GLPOPATTRIB@_GLPOPCLIENTATTRIB@_GLPOPMATRIX@_GLPOPNAME@_GLPRIORITIZETEXTURES@_GLPUSHATTRIB@_GLPUSHCLIENTATTRIB@_GLPUSHMATRIX@_GLPUSHNAME@_GLRASTERPOS2D@_GLRASTERPOS2DV@_GLRASTERPOS2F@_GLRASTERPOS2FV@_GLRASTERPOS2I@_GLRASTERPOS2IV@_GLRASTERPOS2S@_GLRASTERPOS2SV@_GLRASTERPOS3D@_GLRASTERPOS3DV@_GLRASTERPOS3F@_GLRASTERPOS3FV@_GLRASTERPOS3I@_GLRASTERPOS3IV@_GLRASTERPOS3S@_GLRASTERPOS3SV@_GLRASTERPOS4D@_GLRASTERPOS4DV@_GLRASTERPOS4F@_GLRASTERPOS4FV@_GLRASTERPOS4I@_GLRASTERPOS4IV@_GLRASTERPOS4S@_GLRASTERPOS4SV@_GLREADBUFFER@_GLREADPIXELS@_GLRECTD@_GLRECTDV@_GLRECTF@_GLRECTFV@_GLRECTI@_GLRECTIV@_GLRECTS@_GLRECTSV@_GLRENDERMODE@_GLROTATED@_GLROTATEF@_GLSCALED@_GLSCALEF@_GLSCISSOR@_GLSELECTBUFFER@_GLSHADEMODEL@_GLSTENCILFUNC@_GLSTENCILMASK@_GLSTENCILOP@_GLTEXCOORD1D@_GLTEXCOORD1DV@_GLTEXCOORD1F@_GLTEXCOORD1FV@_GLTEXCOORD1I@_GLTEXCOORD1IV@_GLTEXCOORD1S@_GLTEXCOORD1SV@_GLTEXCOORD2D@_GLTEXCOORD2DV@_GLTEXCOORD2F@_GLTEXCOORD2FV@_GLTEXCOORD2I@_GLTEXCOORD2IV@_GLTEXCOORD2S@_GLTEXCOORD2SV@_GLTEXCOORD3D@_GLTEXCOORD3DV@_GLTEXCOORD3F@_GLTEXCOORD3FV@_GLTEXCOORD3I@_GLTEXCOORD3IV@_GLTEXCOORD3S@_GLTEXCOORD3SV@_GLTEXCOORD4D@_GLTEXCOORD4DV@_GLTEXCOORD4F@_GLTEXCOORD4FV@_GLTEXCOORD4I@_GLTEXCOORD4IV@_GLTEXCOORD4S@_GLTEXCOORD4SV@_GLTEXCOORDPOINTER@_GLTEXENVF@_GLTEXENVFV@_GLTEXENVI@_GLTEXENVIV@_GLTEXGEND@_GLTEXGENDV@_GLTEXGENF@_GLTEXGENFV@_GLTEXGENI@_GLTEXGENIV@_GLTEXIMAGE1D@_GLTEXIMAGE2D@_GLTEXPARAMETERF@_GLTEXPARAMETERFV@_GLTEXPARAMETERI@_GLTEXPARAMETERIV@_GLTEXSUBIMAGE1D@_GLTEXSUBIMAGE2D@_GLTRANSLATED@_GLTRANSLATEF@_GLVERTEX2D@_GLVERTEX2DV@_GLVERTEX2F@_GLVERTEX2FV@_GLVERTEX2I@_GLVERTEX2IV@_GLVERTEX2S@_GLVERTEX2SV@_GLVERTEX3D@_GLVERTEX3DV@_GLVERTEX3F@_GLVERTEX3FV@_GLVERTEX3I@_GLVERTEX3IV@_GLVERTEX3S@_GLVERTEX3SV@_GLVERTEX4D@_GLVERTEX4DV@_GLVERTEX4F@_GLVERTEX4FV@_GLVERTEX4I@_GLVERTEX4IV@_GLVERTEX4S@_GLVERTEX4SV@_GLVERTEXPOINTER@_GLVIEWPORT@SMOOTH@STRETCH@_ANTICLOCKWISE@_BEHIND@_CLEAR@_FILLBACKGROUND@_GLUPERSPECTIVE@_HARDWARE@_HARDWARE1@_KEEPBACKGROUND@_NONE@_OFF@_ONLY@_ONLYBACKGROUND@_ONTOP@_SEAMLESS@_SMOOTH@_SMOOTHSHRUNK@_SMOOTHSTRETCHED@"
|
||||||
listOfKeywords$ = listOfKeywords$ + "_SOFTWARE@_SQUAREPIXELS@_STRETCH@_ALLOWFULLSCREEN@_ALL@_ECHO@_INSTRREV@_TRIM$@_ACCEPTFILEDROP@_FINISHDROP@_TOTALDROPPEDFILES@_DROPPEDFILE@_DROPPEDFILE$@_SHR@_SHL@_ROR@_ROL@"
|
listOfKeywords$ = listOfKeywords$ + "_SOFTWARE@_SQUAREPIXELS@_STRETCH@_ALLOWFULLSCREEN@_ALL@_ECHO@_INSTRREV@_TRIM$@_ACCEPTFILEDROP@_FINISHDROP@_TOTALDROPPEDFILES@_DROPPEDFILE@_DROPPEDFILE$@_SHR@_SHL@_ROR@_ROL@"
|
||||||
listOfKeywords$ = listOfKeywords$ + "_DEFLATE$@_INFLATE$@_READBIT@_RESETBIT@_SETBIT@_TOGGLEBIT@$ASSERTS@_ASSERT@_CAPSLOCK@_NUMLOCK@_SCROLLLOCK@_TOGGLE@_CONSOLEFONT@_CONSOLECURSOR@_CONSOLEINPUT@_CINP@$NOPREFIX@$COLOR@$DEBUG@$EMBED@_EMBEDDED$@_ENVIRONCOUNT@$UNSTABLE@$MIDISOUNDFONT@"
|
listOfKeywords$ = listOfKeywords$ + "_DEFLATE$@_INFLATE$@_READBIT@_RESETBIT@_SETBIT@_TOGGLEBIT@$ASSERTS@_ASSERT@_CAPSLOCK@_NUMLOCK@_SCROLLLOCK@_TOGGLE@_CONSOLEFONT@_CONSOLECURSOR@_CONSOLEINPUT@_CINP@$NOPREFIX@$COLOR@$DEBUG@$EMBED@_EMBEDDED$@_ENVIRONCOUNT@$UNSTABLE@$MIDISOUNDFONT@"
|
||||||
listOfKeywords$ = listOfKeywords$ + "_NOTIFYPOPUP@_MESSAGEBOX@_INPUTBOX$@_SELECTFOLDERDIALOG$@_COLORCHOOSERDIALOG@_OPENFILEDIALOG$@_SAVEFILEDIALOG$@_SAVEIMAGE@"
|
listOfKeywords$ = listOfKeywords$ + "_NOTIFYPOPUP@_MESSAGEBOX@_INPUTBOX$@_SELECTFOLDERDIALOG$@_COLORCHOOSERDIALOG@_OPENFILEDIALOG$@_SAVEFILEDIALOG$@_SAVEIMAGE@_FILES$@_FULLPATH$@"
|
||||||
listOfKeywords$ = listOfKeywords$ + "_STATUSCODE@_SNDNEW@_SCALEDWIDTH@_SCALEDHEIGHT@_UFONTHEIGHT@_UPRINTWIDTH@_ULINESPACING@_UPRINTSTRING@_UCHARPOS@"
|
listOfKeywords$ = listOfKeywords$ + "_STATUSCODE@_SNDNEW@_SCALEDWIDTH@_SCALEDHEIGHT@_UFONTHEIGHT@_UPRINTWIDTH@_ULINESPACING@_UPRINTSTRING@_UCHARPOS@"
|
||||||
|
|
59
tests/compile_tests/filesystem/filesys_test.bas
Normal file
59
tests/compile_tests/filesystem/filesys_test.bas
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
$CONSOLE:ONLY
|
||||||
|
OPTION _EXPLICIT
|
||||||
|
|
||||||
|
ON ERROR GOTO test_failed
|
||||||
|
|
||||||
|
CHDIR _STARTDIR$
|
||||||
|
|
||||||
|
PRINT "Creating directory temp_dir"
|
||||||
|
MKDIR "temp_dir"
|
||||||
|
|
||||||
|
PRINT "Changing to directory temp_dir"
|
||||||
|
CHDIR "temp_dir"
|
||||||
|
|
||||||
|
PRINT "Changing to parent directory"
|
||||||
|
CHDIR ".."
|
||||||
|
|
||||||
|
PRINT "Renaming temp_dir to dummy_dir"
|
||||||
|
NAME "temp_dir" AS "dummy_dir"
|
||||||
|
|
||||||
|
PRINT "_DIREXISTS(dummy_dir):"; _DIREXISTS("./dummy_dir")
|
||||||
|
|
||||||
|
PRINT "Creating a temporary file inside dummy_dir"
|
||||||
|
DIM fileName AS STRING: fileName = CreateDummyFile$("./dummy_dir/")
|
||||||
|
|
||||||
|
PRINT "_FILEEXISTS(fileName):"; _FILEEXISTS(fileName)
|
||||||
|
|
||||||
|
PRINT "Deleting fileName"
|
||||||
|
KILL fileName
|
||||||
|
|
||||||
|
PRINT "Creating 10 dummy files inside dummy_dir"
|
||||||
|
DIM i AS LONG: FOR i = 0 TO 9
|
||||||
|
fileName = CreateDummyFile$("./dummy_dir/")
|
||||||
|
NEXT i
|
||||||
|
|
||||||
|
PRINT "Deleting all 10 dummy files"
|
||||||
|
KILL "./dummy_dir/*.tmp"
|
||||||
|
|
||||||
|
PRINT "Deleting dummy_dir"
|
||||||
|
RMDIR "dummy_dir"
|
||||||
|
|
||||||
|
SYSTEM
|
||||||
|
|
||||||
|
test_failed:
|
||||||
|
PRINT "Test failed!"
|
||||||
|
SYSTEM 1
|
||||||
|
|
||||||
|
FUNCTION CreateDummyFile$ (directory AS STRING)
|
||||||
|
DO
|
||||||
|
DIM fileName AS STRING: fileName = directory + LTRIM$(STR$(100! * (TIMER + RND))) + ".tmp"
|
||||||
|
LOOP WHILE _FILEEXISTS(fileName)
|
||||||
|
|
||||||
|
DIM h AS LONG: h = FREEFILE
|
||||||
|
|
||||||
|
OPEN fileName FOR OUTPUT AS h
|
||||||
|
PRINT #h, "Delete me!"
|
||||||
|
CLOSE h
|
||||||
|
|
||||||
|
CreateDummyFile = fileName
|
||||||
|
END FUNCTION
|
11
tests/compile_tests/filesystem/filesys_test.output
Normal file
11
tests/compile_tests/filesystem/filesys_test.output
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Creating directory temp_dir
|
||||||
|
Changing to directory temp_dir
|
||||||
|
Changing to parent directory
|
||||||
|
Renaming temp_dir to dummy_dir
|
||||||
|
_DIREXISTS(dummy_dir):-1
|
||||||
|
Creating a temporary file inside dummy_dir
|
||||||
|
_FILEEXISTS(fileName):-1
|
||||||
|
Deleting fileName
|
||||||
|
Creating 10 dummy files inside dummy_dir
|
||||||
|
Deleting all 10 dummy files
|
||||||
|
Deleting dummy_dir
|
Loading…
Reference in a new issue