Easier platform porting (#108)

* Create PORTING.md

* Creates io_platform abstraction

* removes miniposix

* remove unistd refs

* removes watcom functions

* fixes shadow rendering memory leak
This commit is contained in:
Dethrace Engineering Department 2022-03-21 12:10:49 +13:00 committed by GitHub
parent d3ebbb6f1f
commit 7e14a97692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1079 additions and 2772 deletions

View File

@ -20,7 +20,6 @@ option(BUILD_TESTS "Build unit tests." OFF)
find_package(SDL2 REQUIRED)
add_subdirectory(lib/libsmacker)
add_subdirectory(lib/miniposix)
add_subdirectory(lib/glad)
add_subdirectory(lib/cglm EXCLUDE_FROM_ALL)

68
docs/PORTING.md Normal file
View File

@ -0,0 +1,68 @@
# Porting Dethrace to other systems
## Operating Systems
See: xxx
Assuming an operating system called _foo_, follow the steps to add support for it.
1. Add a new file `os/foo.h` and implement the required functions defined in `os.h`:
- `OS_GetTime`
- `OS_Sleep`
- `OS_Basename`
- `OS_GetFirstFileInDirectory`
- `OS_GetNextFileInDirectory`
- `OS_IsDebuggerPresent`
- `OS_InstallSignalHandler`
2. Update `src/harness/CMakeLists.h` and add a new conditional section for "os/foo.h", based on existing conditions for Windows, MacOS etc.
For example:
```
...
elseif( _FOO_ )
target_sources(harness PRIVATE
os/foo.c
)
...
```
## IO Platform (windowing / input / rendering)
An `IOPlatform` in _dethrace_ implements windowing and input handling, and points to a _renderer_.
The default IO platform is `SDL_OpenGL`, which uses SDL for windowing and input, and OpenGL for rendering. See `io_platforms/sdl_gl.c`.
To add a new `IOPlatform`:
1. Create `io_platforms/my_platform.c` file and implement the required functions defined in `io_platform.h`:
- `Window_Create`
- `Window_PollEvents`
- `Window_Swap`
- `Input_GetKeyMap`
- `Input_IsKeyDown`
`Window_Create` returns a `tRenderer*`, which must implement the interface defined in `renderers/renderer.h`. See `renderers/gl` for an example.
2. Add a new conditional section in `src/harness/CMakeLists.txt` for your new platform
For example:
```
if (IO_PLATFORM STREQUAL "My_Platform")
target_sources(harness PRIVATE
io_platforms/my_platform.c
)
endif()
```
3. Run cmake to update your build with the new platform
```sh
cd build
cmake -DIO_PLATFORM=My_Platform ..
```
4. Build
```
cmake --build .
```

View File

@ -1,50 +0,0 @@
include(CheckIncludeFile)
# Check if we are missing any posix headers we consider required.
check_include_file(strings.h HAVE_STRINGS_H)
check_include_file(libgen.h HAVE_LIBGEN_H)
check_include_file(dirent.h HAVE_DIRENT_H)
check_include_file(fnmatch.h HAVE_FNMATCH_H)
check_include_file(getopt.h HAVE_GETOPT_H)
check_include_file(unistd.h HAVE_UNISTD_H)
if(HAVE_STRINGS_H AND HAVE_UNISTD_H AND HAVE_LIBGEN_H AND HAVE_DIRENT_H AND HAVE_FNMATCH_H AND HAVE_GETOPT_H)
add_library(miniposix INTERFACE)
else()
add_library(miniposix STATIC)
# Only win32 targets should really need miniposix at all, but just to be safe.
if(WIN32)
target_compile_definitions(miniposix PUBLIC _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE WIN32_LEAN_AND_MEAN)
endif()
endif()
if(NOT HAVE_STRINGS_H)
target_sources(miniposix PRIVATE strings/strings.c strings/strings.h)
target_include_directories(miniposix PUBLIC strings)
endif()
if(NOT HAVE_LIBGEN_H)
target_sources(miniposix PRIVATE libgen/libgen.c libgen/libgen.h)
target_include_directories(miniposix PUBLIC libgen)
endif()
if(NOT HAVE_DIRENT_H)
target_sources(miniposix PRIVATE dirent/dirent.c dirent/dirent.h)
target_include_directories(miniposix PUBLIC dirent)
endif()
if(NOT HAVE_FNMATCH_H)
target_sources(miniposix PRIVATE fnmatch/fnmatch.c fnmatch/fnmatch.h)
target_include_directories(miniposix PUBLIC fnmatch)
endif()
if(NOT HAVE_GETOPT_H)
target_sources(miniposix PRIVATE getopt/getopt.c getopt/getopt.h)
target_include_directories(miniposix PUBLIC getopt)
endif()
if(NOT HAVE_UNISTD_H)
target_sources(miniposix PRIVATE unistd/unistd.h)
target_include_directories(miniposix PUBLIC unistd)
endif()

View File

@ -1,382 +0,0 @@
#include <dirent.h>
#include <stdlib.h>
#include <windows.h>
#include <winioctl.h>
struct __dir
{
struct dirent *entries;
HANDLE fd;
long int count;
long int index;
};
static void __seterrno(int value)
{
#ifdef _MSC_VER
_set_errno(value);
#else /* _MSC_VER */
errno = value;
#endif /* _MSC_VER */
}
int closedir(DIR *dirp)
{
struct __dir *data = NULL;
if (!dirp) {
__seterrno(EBADF);
return -1;
}
data = (struct __dir *)dirp;
CloseHandle((HANDLE)data->fd);
free(data->entries);
free(data);
return 0;
}
static int __islink(const wchar_t *name, char *buffer)
{
DWORD io_result = 0;
DWORD bytes_returned = 0;
HANDLE hFile =
CreateFileW(name, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
io_result = DeviceIoControl(
hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL);
CloseHandle(hFile);
if (io_result == 0)
return 0;
return ((REPARSE_GUID_DATA_BUFFER *)buffer)->ReparseTag == IO_REPARSE_TAG_SYMLINK;
}
static __ino_t __inode(const wchar_t *name)
{
__ino_t value = { 0 };
BOOL result;
FILE_ID_INFO fileid;
BY_HANDLE_FILE_INFORMATION info;
HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return value;
result = GetFileInformationByHandleEx(hFile, FileIdInfo, &fileid, sizeof(fileid));
if (result) {
value.serial = fileid.VolumeSerialNumber;
memcpy(value.fileid, fileid.FileId.Identifier, 16);
} else {
result = GetFileInformationByHandle(hFile, &info);
if (result) {
value.serial = info.dwVolumeSerialNumber;
memcpy(value.fileid + 8, &info.nFileIndexHigh, 4);
memcpy(value.fileid + 12, &info.nFileIndexLow, 4);
}
}
CloseHandle(hFile);
return value;
}
static DIR *__internal_opendir(wchar_t *wname, int size)
{
struct __dir *data = NULL;
struct dirent *tmp_entries = NULL;
static char default_char = '?';
static wchar_t *prefix = L"\\\\?\\";
static wchar_t *suffix = L"\\*.*";
int extra_prefix = 4; /* use prefix "\\?\" to handle long file names */
static int extra_suffix = 4; /* use suffix "\*.*" to find everything */
WIN32_FIND_DATAW w32fd = { 0 };
HANDLE hFindFile = INVALID_HANDLE_VALUE;
static int grow_factor = 2;
char *buffer = NULL;
/* Ensure path only uses windows separator, of FindFirstFileW will fail. */
wchar_t* rep = wname;
while ((rep = wcschr(rep, L'/')) != NULL) {
*rep++ = L'\\';
}
memcpy(wname + extra_prefix + size - 1, suffix, sizeof(wchar_t) * extra_prefix);
wname[size + extra_prefix + extra_suffix - 1] = 0;
if (memcmp(wname + extra_prefix, L"\\\\?\\", sizeof(wchar_t) * extra_prefix) == 0) {
wname += extra_prefix;
extra_prefix = 0;
}
hFindFile = FindFirstFileW(wname, &w32fd);
if (INVALID_HANDLE_VALUE == hFindFile) {
__seterrno(ENOENT);
return NULL;
}
data = (struct __dir *)malloc(sizeof(struct __dir));
if (!data)
goto out_of_memory;
wname[extra_prefix + size - 1] = 0;
data->fd = CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
wname[extra_prefix + size - 1] = L'\\';
data->count = 16;
data->index = 0;
data->entries = (struct dirent *)malloc(sizeof(struct dirent) * data->count);
if (!data->entries)
goto out_of_memory;
buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
if (!buffer)
goto out_of_memory;
do {
WideCharToMultiByte(
CP_UTF8, 0, w32fd.cFileName, -1, data->entries[data->index].d_name, NAME_MAX, &default_char, NULL);
memcpy(wname + extra_prefix + size, w32fd.cFileName, sizeof(wchar_t) * NAME_MAX);
if (((w32fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
&& __islink(wname, buffer))
data->entries[data->index].d_type = DT_LNK;
else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE)
data->entries[data->index].d_type = DT_CHR;
else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
data->entries[data->index].d_type = DT_DIR;
else
data->entries[data->index].d_type = DT_REG;
data->entries[data->index].d_ino = __inode(wname);
data->entries[data->index].d_reclen = sizeof(struct dirent);
data->entries[data->index].d_namelen = (unsigned char)wcslen(w32fd.cFileName);
data->entries[data->index].d_off = 0;
if (++data->index == data->count) {
tmp_entries = (struct dirent *)realloc(data->entries, sizeof(struct dirent) * data->count * grow_factor);
if (!tmp_entries)
goto out_of_memory;
data->entries = tmp_entries;
data->count *= grow_factor;
}
} while (FindNextFileW(hFindFile, &w32fd) != 0);
free(buffer);
FindClose(hFindFile);
data->count = data->index;
data->index = 0;
return (DIR *)data;
out_of_memory:
if (data) {
if (INVALID_HANDLE_VALUE != (HANDLE)data->fd)
CloseHandle((HANDLE)data->fd);
free(data->entries);
}
free(buffer);
free(data);
if (INVALID_HANDLE_VALUE != hFindFile)
FindClose(hFindFile);
__seterrno(ENOMEM);
return NULL;
}
static wchar_t *__get_buffer()
{
wchar_t *name = malloc(sizeof(wchar_t) * (NTFS_MAX_PATH + NAME_MAX + 8));
if (name)
memcpy(name, L"\\\\?\\", sizeof(wchar_t) * 4);
return name;
}
DIR *opendir(const char *name)
{
DIR *dirp = NULL;
wchar_t *wname = __get_buffer();
int size = 0;
if (!wname) {
__seterrno(ENOMEM);
return NULL;
}
size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH);
if (0 == size) {
free(wname);
return NULL;
}
dirp = __internal_opendir(wname, size);
free(wname);
return dirp;
}
DIR *_wopendir(const wchar_t *name)
{
DIR *dirp = NULL;
wchar_t *wname = __get_buffer();
int size = 0;
if (!wname) {
__seterrno(ENOMEM);
return NULL;
}
size = (int)wcslen(name);
if (size > NTFS_MAX_PATH) {
free(wname);
return NULL;
}
memcpy(wname + 4, name, sizeof(wchar_t) * (size + 1));
dirp = __internal_opendir(wname, size + 1);
free(wname);
return dirp;
}
DIR *fdopendir(int fd)
{
DIR *dirp = NULL;
wchar_t *wname = __get_buffer();
int size = 0;
if (!wname) {
__seterrno(ENOMEM);
return NULL;
}
size = GetFinalPathNameByHandleW((HANDLE)((intptr_t)fd), wname, NTFS_MAX_PATH, FILE_NAME_NORMALIZED);
if (0 == size) {
free(wname);
__seterrno(ENOTDIR);
return NULL;
}
dirp = __internal_opendir(wname, size + 1);
free(wname);
return dirp;
}
struct dirent *readdir(DIR *dirp)
{
struct __dir *data = (struct __dir *)dirp;
if (!data) {
__seterrno(EBADF);
return NULL;
}
if (data->index < data->count) {
return &data->entries[data->index++];
}
return NULL;
}
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
struct __dir *data = (struct __dir *)dirp;
if (!data) {
return EBADF;
}
if (data->index < data->count) {
if (entry)
memcpy(entry, &data->entries[data->index++], sizeof(struct dirent));
if (result)
*result = entry;
} else if (result)
*result = NULL;
return 0;
}
void seekdir(DIR *dirp, long int offset)
{
if (dirp) {
struct __dir *data = (struct __dir *)dirp;
data->index = (offset < data->count) ? offset : data->index;
}
}
void rewinddir(DIR *dirp)
{
seekdir(dirp, 0);
}
long int telldir(DIR *dirp)
{
if (!dirp) {
__seterrno(EBADF);
return -1;
}
return ((struct __dir *)dirp)->count;
}
int dirfd(DIR *dirp)
{
if (!dirp) {
__seterrno(EINVAL);
return -1;
}
return (int)((struct __dir *)dirp)->fd;
}
int scandir(const char *dirp,
struct dirent ***namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **))
{
struct dirent **entries = NULL, **tmp_entries = NULL;
long int i = 0, index = 0, count = 16;
DIR *d = opendir(dirp);
struct __dir *data = (struct __dir *)d;
if (!data) {
closedir(d);
__seterrno(ENOENT);
return -1;
}
entries = (struct dirent **)malloc(sizeof(struct dirent *) * count);
if (!entries) {
closedir(d);
__seterrno(ENOMEM);
return -1;
}
for (i = 0; i < data->count; ++i) {
if (!filter || filter(&data->entries[i])) {
entries[index] = (struct dirent *)malloc(sizeof(struct dirent));
if (!entries[index]) {
closedir(d);
for (i = 0; i < index; ++i)
free(entries[index]);
free(entries);
__seterrno(ENOMEM);
return -1;
}
memcpy(entries[index], &data->entries[i], sizeof(struct dirent));
if (++index == count) {
tmp_entries = (struct dirent **)realloc(entries, sizeof(struct dirent *) * count * 2);
if (!tmp_entries) {
closedir(d);
for (i = 0; i < index; ++i)
free(entries[index - 1]);
free(entries);
__seterrno(ENOMEM);
return -1;
}
entries = tmp_entries;
count *= 2;
}
}
}
qsort(entries, index, sizeof(struct dirent *), compar);
entries[index] = NULL;
if (namelist)
*namelist = entries;
closedir(d);
return 0;
}
int alphasort(const void *a, const void *b)
{
struct dirent **dira = (struct dirent **)a, **dirb = (struct dirent **)b;
if (!dira || !dirb)
return 0;
return strcoll((*dira)->d_name, (*dirb)->d_name);
}
static int __strverscmp(const char *s1, const char *s2)
{
return alphasort(s1, s2);
}
int versionsort(const void *a, const void *b)
{
struct dirent **dira = (struct dirent **)a, **dirb = (struct dirent **)b;
if (!dira || !dirb)
return 0;
return __strverscmp((*dira)->d_name, (*dirb)->d_name);
}

View File

@ -1,144 +0,0 @@
/*
MIT License
Copyright (c) 2019 win32ports
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifndef WIN32PORTS_DIRENT_H
#define WIN32PORTS_DIRENT_H
#ifndef _WIN32
#pragma message("this dirent.h implementation is for Windows only!")
#elif defined __MINGW32__
#include <../include/dirent.h>
#else
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <sys/types.h>
#include <wchar.h>
#ifndef NAME_MAX
#define NAME_MAX 260
#endif /* NAME_MAX */
#ifndef DT_UNKNOWN
#define DT_UNKNOWN 0
#endif /* DT_UNKNOWN */
#ifndef DT_FIFO
#define DT_FIFO 1
#endif /* DT_FIFO */
#ifndef DT_CHR
#define DT_CHR 2
#endif /* DT_CHR */
#ifndef DT_DIR
#define DT_DIR 4
#endif /* DT_DIR */
#ifndef DT_BLK
#define DT_BLK 6
#endif /* DT_BLK */
#ifndef DT_REG
#define DT_REG 8
#endif /* DT_REF */
#ifndef DT_LNK
#define DT_LNK 10
#endif /* DT_LNK */
#ifndef DT_SOCK
#define DT_SOCK 12
#endif /* DT_SOCK */
#ifndef DT_WHT
#define DT_WHT 14
#endif /* DT_WHT */
#ifndef _DIRENT_HAVE_D_NAMLEN
#define _DIRENT_HAVE_D_NAMLEN 1
#endif /* _DIRENT_HAVE_D_NAMLEN */
#ifndef _DIRENT_HAVE_D_RECLEN
#define _DIRENT_HAVE_D_RECLEN 1
#endif /* _DIRENT_HAVE_D_RECLEN */
#ifndef _DIRENT_HAVE_D_OFF
#define _DIRENT_HAVE_D_OFF 1
#endif /* _DIRENT_HAVE_D_OFF */
#ifndef _DIRENT_HAVE_D_TYPE
#define _DIRENT_HAVE_D_TYPE 1
#endif /* _DIRENT_HAVE_D_TYPE */
#ifndef NTFS_MAX_PATH
#define NTFS_MAX_PATH 32768
#endif /* NTFS_MAX_PATH */
typedef struct __dir DIR;
typedef struct ino_t
{
unsigned long long serial;
unsigned char fileid[16];
} __ino_t;
struct dirent
{
__ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
unsigned char d_namelen;
unsigned char d_type;
char d_name[NAME_MAX];
};
int closedir(DIR *dirp);
DIR *opendir(const char *name);
DIR *_wopendir(const wchar_t *name);
DIR *fdopendir(int fd);
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
void seekdir(DIR *dirp, long int offset);
void rewinddir(DIR *dirp);
long int telldir(DIR *dirp);
int dirfd(DIR *dirp);
int scandir(const char *dirp,
struct dirent ***namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIN32 */
#endif /* WIN32PORTS_DIRENT_H */

View File

@ -1,311 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
/*
* Some notes on multibyte character support:
* 1. Patterns with illegal byte sequences match nothing.
* 2. Illegal byte sequences in the "string" argument are handled by treating
* them as single-byte characters with a value of the first byte of the
* sequence cast to wchar_t.
* 3. Multibyte conversion state objects (mbstate_t) are passed around and
* used for most, but not all, conversions. Further work will be required
* to support state-dependent encodings.
*/
#include <fnmatch.h>
#include <limits.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int rangematch(const char *, wchar_t, int, char **, mbstate_t *);
static int fnmatch1(const char *, const char *, const char *, int, mbstate_t,
mbstate_t);
int
fnmatch(const char *pattern, const char *string, int flags)
{
static const mbstate_t initial;
return (fnmatch1(pattern, string, string, flags, initial, initial));
}
static int
fnmatch1(const char *pattern, const char *string, const char *stringstart,
int flags, mbstate_t patmbs, mbstate_t strmbs)
{
const char *bt_pattern, *bt_string;
mbstate_t bt_patmbs, bt_strmbs;
char *newp;
char c;
wchar_t pc, sc;
size_t pclen, sclen;
bt_pattern = bt_string = NULL;
for (;;) {
pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (FNM_NOMATCH);
pattern += pclen;
sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs);
if (sclen == (size_t)-1 || sclen == (size_t)-2) {
sc = (unsigned char)*string;
sclen = 1;
memset(&strmbs, 0, sizeof(strmbs));
}
switch (pc) {
case EOS:
if ((flags & FNM_LEADING_DIR) && sc == '/')
return (0);
if (sc == EOS)
return (0);
goto backtrack;
case '?':
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && (flags & FNM_PATHNAME))
goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
string += sclen;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
else if (c == '/' && flags & FNM_PATHNAME) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/*
* First try the shortest match for the '*' that
* could work. We can forget any earlier '*' since
* there is no way having it match more characters
* can help us, given that we are already here.
*/
bt_pattern = pattern, bt_patmbs = patmbs;
bt_string = string, bt_strmbs = strmbs;
break;
case '[':
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && (flags & FNM_PATHNAME))
goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
switch (rangematch(pattern, sc, flags, &newp,
&patmbs)) {
case RANGE_ERROR:
goto norm;
case RANGE_MATCH:
pattern = newp;
break;
case RANGE_NOMATCH:
goto backtrack;
}
string += sclen;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
pclen = mbrtowc(&pc, pattern, MB_LEN_MAX,
&patmbs);
if (pclen == 0 || pclen == (size_t)-1 ||
pclen == (size_t)-2)
return (FNM_NOMATCH);
pattern += pclen;
}
/* FALLTHROUGH */
default:
norm:
string += sclen;
if (pc == sc)
;
else if ((flags & FNM_CASEFOLD) &&
(towlower(pc) == towlower(sc)))
;
else {
backtrack:
/*
* If we have a mismatch (other than hitting
* the end of the string), go back to the last
* '*' seen and have it match one additional
* character.
*/
if (bt_pattern == NULL)
return (FNM_NOMATCH);
sclen = mbrtowc(&sc, bt_string, MB_LEN_MAX,
&bt_strmbs);
if (sclen == (size_t)-1 ||
sclen == (size_t)-2) {
sc = (unsigned char)*bt_string;
sclen = 1;
memset(&bt_strmbs, 0,
sizeof(bt_strmbs));
}
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && flags & FNM_PATHNAME)
return (FNM_NOMATCH);
bt_string += sclen;
pattern = bt_pattern, patmbs = bt_patmbs;
string = bt_string, strmbs = bt_strmbs;
}
break;
}
}
/* NOTREACHED */
}
static int __wcollate_range_cmp(wchar_t c1, wchar_t c2)
{
wchar_t s1[2], s2[2];
s1[0] = c1;
s1[1] = L'\0';
s2[0] = c2;
s2[1] = L'\0';
return (wcscoll(s1, s2));
}
static int
rangematch(const char *pattern, wchar_t test, int flags, char **newp,
mbstate_t *patmbs)
{
int negate, ok;
wchar_t c, c2;
size_t pclen;
const char *origpat;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ((negate = (*pattern == '!' || *pattern == '^')))
++pattern;
if (flags & FNM_CASEFOLD)
test = towlower(test);
/*
* A right bracket shall lose its special meaning and represent
* itself in a bracket expression if it occurs first in the list.
* -- POSIX.2 2.8.3.2
*/
ok = 0;
origpat = pattern;
for (;;) {
if (*pattern == ']' && pattern > origpat) {
pattern++;
break;
} else if (*pattern == '\0') {
return (RANGE_ERROR);
} else if (*pattern == '/' && (flags & FNM_PATHNAME)) {
return (RANGE_NOMATCH);
} else if (*pattern == '\\' && !(flags & FNM_NOESCAPE))
pattern++;
pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (RANGE_NOMATCH);
pattern += pclen;
if (flags & FNM_CASEFOLD)
c = towlower(c);
if (*pattern == '-' && *(pattern + 1) != EOS &&
*(pattern + 1) != ']') {
if (*++pattern == '\\' && !(flags & FNM_NOESCAPE))
if (*pattern != EOS)
pattern++;
pclen = mbrtowc(&c2, pattern, MB_LEN_MAX, patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (RANGE_NOMATCH);
pattern += pclen;
if (c2 == EOS)
return (RANGE_ERROR);
if (flags & FNM_CASEFOLD)
c2 = towlower(c2);
if (__wcollate_range_cmp(c, test) <= 0
&& __wcollate_range_cmp(test, c2) <= 0
)
ok = 1;
} else if (c == test)
ok = 1;
}
*newp = (char *)pattern;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}

View File

@ -1,55 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
*/
#ifndef WIN32PORTS_FNMATCH_H
#define WIN32PORTS_FNMATCH_H
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#if __XSI_VISIBLE
#define FNM_NOSYS (-1) /* Reserved. */
#endif
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_IGNORECASE FNM_CASEFOLD
#define FNM_FILE_NAME FNM_PATHNAME
int fnmatch(const char *, const char *, int);
#endif /* !WIN32PORTS_FNMATCH_H */

View File

@ -1,361 +0,0 @@
/*
Copyright (C) 1997 Gregory Pietsch
[These files] are hereby placed in the public domain without restrictions. Just
give the author credit, don't claim you wrote it or prevent anyone else from
using it.
*/
/****************************************************************************
getopt.c - Read command line options
AUTHOR: Gregory Pietsch
CREATED Fri Jan 10 21:13:05 1997
DESCRIPTION:
The getopt() function parses the command line arguments. Its arguments argc
and argv are the argument count and array as passed to the main() function
on program invocation. The argument optstring is a list of available option
characters. If such a character is followed by a colon (`:'), the option
takes an argument, which is placed in optarg. If such a character is
followed by two colons, the option takes an optional argument, which is
placed in optarg. If the option does not take an argument, optarg is NULL.
The external variable optind is the index of the next array element of argv
to be processed; it communicates from one call to the next which element to
process.
The getopt_long() function works like getopt() except that it also accepts
long options started by two dashes `--'. If these take values, it is either
in the form
--arg=value
or
--arg value
It takes the additional arguments longopts which is a pointer to the first
element of an array of type GETOPT_LONG_OPTION_T. The last element of the
array has to be filled with NULL for the name field.
The longind pointer points to the index of the current long option relative
to longopts if it is non-NULL.
The getopt() function returns the option character if the option was found
successfully, `:' if there was a missing parameter for one of the options,
`?' for an unknown option character, and EOF for the end of the option list.
The getopt_long() function's return value is described in the header file.
The function getopt_long_only() is identical to getopt_long(), except that a
plus sign `+' can introduce long options as well as `--'.
The following describes how to deal with options that follow non-option
argv-elements.
If the caller did not specify anything, the default is REQUIRE_ORDER if the
environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
REQUIRE_ORDER means don't recognize them as options; stop option processing
when the first non-option is seen. This is what Unix does. This mode of
operation is selected by either setting the environment variable
POSIXLY_CORRECT, or using `+' as the first character of the optstring
parameter.
PERMUTE is the default. We permute the contents of ARGV as we scan, so that
eventually all the non-options are at the end. This allows options to be
given in any order, even with programs that were not written to expect this.
RETURN_IN_ORDER is an option available to programs that were written to
expect options and other argv-elements in any order and that care about the
ordering of the two. We describe each non-option argv-element as if it were
the argument of an option with character code 1. Using `-' as the first
character of the optstring parameter selects this mode of operation.
The special argument `--' forces an end of option-scanning regardless of the
value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause
getopt() and friends to return EOF with optind != argc.
COPYRIGHT NOTICE AND DISCLAIMER:
Copyright (C) 1997 Gregory Pietsch
This file and the accompanying getopt.h header file are hereby placed in the
public domain without restrictions. Just give the author credit, don't
claim you wrote it or prevent anyone else from using it.
Gregory Pietsch's current e-mail address:
gpietsch@comcast.net
****************************************************************************/
/* include files */
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* macros */
/* types */
typedef enum GETOPT_ORDERING_T
{
PERMUTE,
RETURN_IN_ORDER,
REQUIRE_ORDER
} GETOPT_ORDERING_T;
/* globally-defined variables */
char *optarg = NULL;
int optind = 0;
int opterr = 1;
int optopt = '?';
/* functions */
/* reverse_argv_elements: reverses num elements starting at argv */
static void reverse_argv_elements(char **argv, int num)
{
int i;
char *tmp;
for (i = 0; i < (num >> 1); i++) {
tmp = argv[i];
argv[i] = argv[num - i - 1];
argv[num - i - 1] = tmp;
}
}
/* permute: swap two blocks of argv-elements given their lengths */
static void permute(char **argv, int len1, int len2)
{
reverse_argv_elements(argv, len1);
reverse_argv_elements(argv, len1 + len2);
reverse_argv_elements(argv, len2);
}
/* is_option: is this argv-element an option or the end of the option list? */
static int is_option(char *argv_element, int only)
{
return ((argv_element == NULL) || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
}
/* getopt_internal: the function that does all the dirty work */
static int getopt_internal(int argc, char **argv, char *shortopts, GETOPT_LONG_OPTION_T *longopts, int *longind, int only)
{
GETOPT_ORDERING_T ordering = PERMUTE;
static size_t optwhere = 0;
size_t permute_from = 0;
int num_nonopts = 0;
int optindex = 0;
size_t match_chars = 0;
char *possible_arg = NULL;
int longopt_match = -1;
int has_arg = -1;
char *cp = NULL;
int arg_next = 0;
/* first, deal with silly parameters and easy stuff */
if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
return (optopt = '?');
if (optind >= argc || argv[optind] == NULL)
return EOF;
if (strcmp(argv[optind], "--") == 0) {
optind++;
return EOF;
}
/* if this is our first time through */
if (optind == 0) {
optind = 1;
optwhere = 1;
}
/* define ordering */
if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+')) {
ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
shortopts++;
} else
ordering = (getenv("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
/*
* based on ordering, find our next option, if we're at the beginning of
* one
*/
if (optwhere == 1) {
switch (ordering) {
case PERMUTE:
permute_from = optind;
num_nonopts = 0;
while (!is_option(argv[optind], only)) {
optind++;
num_nonopts++;
}
if (argv[optind] == NULL) {
/* no more options */
optind = (int)permute_from;
return EOF;
} else if (strcmp(argv[optind], "--") == 0) {
/* no more options, but have to get `--' out of the way */
permute(argv + permute_from, num_nonopts, 1);
optind = (int)(permute_from + 1);
return EOF;
}
break;
case RETURN_IN_ORDER:
if (!is_option(argv[optind], only)) {
optarg = argv[optind++];
return (optopt = 1);
}
break;
case REQUIRE_ORDER:
if (!is_option(argv[optind], only))
return EOF;
break;
}
}
/* we've got an option, so parse it */
/* first, is it a long option? */
if (longopts != NULL && (strncmp(argv[optind], "--", 2) == 0 || (only && argv[optind][0] == '+')) && optwhere == 1) {
/* handle long options */
if (strncmp(argv[optind], "--", 2) == 0)
optwhere = 2;
longopt_match = -1;
possible_arg = strchr(argv[optind] + optwhere, '=');
if (possible_arg == NULL) {
/* no =, so next argv might be arg */
match_chars = strlen(argv[optind]);
possible_arg = argv[optind] + match_chars;
match_chars = match_chars - optwhere;
} else
match_chars = (possible_arg - argv[optind]) - optwhere;
for (optindex = 0; longopts[optindex].name != NULL; optindex++) {
if (strncmp(argv[optind] + optwhere, longopts[optindex].name, match_chars) == 0) {
/* do we have an exact match? */
if (match_chars == strlen(longopts[optindex].name)) {
longopt_match = optindex;
break;
}
/* do any characters match? */
else {
if (longopt_match < 0)
longopt_match = optindex;
else {
/* we have ambiguous options */
if (opterr)
fprintf(stderr,
"%s: option `%s' is ambiguous "
"(could be `--%s' or `--%s')\n",
argv[0],
argv[optind],
longopts[longopt_match].name,
longopts[optindex].name);
return (optopt = '?');
}
}
}
}
if (longopt_match >= 0)
has_arg = longopts[longopt_match].has_arg;
}
/* if we didn't find a long option, is it a short option? */
if (longopt_match < 0 && shortopts != NULL) {
cp = strchr(shortopts, argv[optind][optwhere]);
if (cp == NULL) {
/* couldn't find option in shortopts */
if (opterr)
fprintf(stderr, "%s: invalid option -- `-%c'\n", argv[0], argv[optind][optwhere]);
optwhere++;
if (argv[optind][optwhere] == '\0') {
optind++;
optwhere = 1;
}
return (optopt = '?');
}
has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument : required_argument) : no_argument);
possible_arg = argv[optind] + optwhere + 1;
optopt = *cp;
}
/* get argument and reset optwhere */
arg_next = 0;
switch (has_arg) {
case optional_argument:
if (*possible_arg == '=')
possible_arg++;
if (*possible_arg != '\0') {
optarg = possible_arg;
optwhere = 1;
} else
optarg = NULL;
break;
case required_argument:
if (*possible_arg == '=')
possible_arg++;
if (*possible_arg != '\0') {
optarg = possible_arg;
optwhere = 1;
} else if (optind + 1 >= argc) {
if (opterr) {
fprintf(stderr, "%s: argument required for option `", argv[0]);
if (longopt_match >= 0)
fprintf(stderr, "--%s'\n", longopts[longopt_match].name);
else
fprintf(stderr, "-%c'\n", *cp);
}
optind++;
return (optopt = ':');
} else {
optarg = argv[optind + 1];
arg_next = 1;
optwhere = 1;
}
break;
case no_argument:
if (longopt_match < 0) {
optwhere++;
if (argv[optind][optwhere] == '\0')
optwhere = 1;
} else
optwhere = 1;
optarg = NULL;
break;
}
/* do we have to permute or otherwise modify optind? */
if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0) {
permute(argv + permute_from, num_nonopts, 1 + arg_next);
optind = (int)(permute_from + 1 + arg_next);
} else if (optwhere == 1)
optind = optind + 1 + arg_next;
/* finally return */
if (longopt_match >= 0) {
if (longind != NULL)
*longind = longopt_match;
if (longopts[longopt_match].flag != NULL) {
*(longopts[longopt_match].flag) = longopts[longopt_match].val;
return 0;
} else
return longopts[longopt_match].val;
} else
return optopt;
}
int getopt(int argc, char **argv, char *optstring)
{
return getopt_internal(argc, argv, optstring, NULL, NULL, 0);
}
int getopt_long(int argc, char **argv, const char *shortopts, const GETOPT_LONG_OPTION_T *longopts, int *longind)
{
return getopt_internal(argc, argv, (char *)shortopts, (GETOPT_LONG_OPTION_T *)longopts, longind, 0);
}
int getopt_long_only(int argc, char **argv, const char *shortopts, const GETOPT_LONG_OPTION_T *longopts, int *longind)
{
return getopt_internal(argc, argv, (char *)shortopts, (GETOPT_LONG_OPTION_T *)longopts, longind, 1);
}
/* end of file GETOPT.C */

View File

@ -1,69 +0,0 @@
/*
Copyright (C) 1997 Gregory Pietsch
[These files] are hereby placed in the public domain without restrictions. Just
give the author credit, don't claim you wrote it or prevent anyone else from
using it.
*/
#pragma once
#ifndef WIN32PORTS_GETOPT_H
#define WIN32PORTS_GETOPT_H
#ifndef _WIN32
#pragma message("this getopt.h implementation is for Windows only!")
#elif defined __MINGW32__
#include <../include/getopt.h>
#else
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* include files needed by this include file */
/* macros defined by this include file */
#define no_argument 0
#define required_argument 1
#define optional_argument 2
/* types defined by this include file */
/* GETOPT_LONG_OPTION_T: The type of long option */
typedef struct GETOPT_LONG_OPTION_T
{
const char *name; /* the name of the long option */
int has_arg; /* one of the above macros */
int *flag; /* determines if getopt_long() returns a
* value for a long option; if it is
* non-NULL, 0 is returned as a function
* value and the value of val is stored in
* the area pointed to by flag. Otherwise,
* val is returned. */
int val; /* determines the value to return if flag is
* NULL. */
} GETOPT_LONG_OPTION_T;
typedef GETOPT_LONG_OPTION_T option;
/* externally-defined variables */
extern char *optarg;
extern int optind;
extern int opterr;
extern int optopt;
/* function prototypes */
int getopt(int argc, char **argv, char *optstring);
int getopt_long(int argc, char **argv, const char *shortopts, const GETOPT_LONG_OPTION_T *longopts, int *longind);
int getopt_long_only(int argc, char **argv, const char *shortopts, const GETOPT_LONG_OPTION_T *longopts, int *longind);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIN32 */
#endif /* WIN32PORTS_GETOPT_H */

View File

@ -1,263 +0,0 @@
/* basename.c
*
* $Id: basename.c,v 1.2 2007/03/08 23:15:58 keithmarshall Exp $
*
* Provides an implementation of the "basename" function, conforming
* to SUSv3, with extensions to accommodate Win32 drive designators,
* and suitable for use on native Microsoft(R) Win32 platforms.
*
* Written by Keith Marshall <keithmarshall@users.sourceforge.net>
*
* This is free software. You may redistribute and/or modify it as you
* see fit, without restriction of copyright.
*
* This software is provided "as is", in the hope that it may be useful,
* but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of
* MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no
* time will the author accept any form of liability for any damages,
* however caused, resulting from the use of this software.
*
*/
#include <libgen.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
char *basename(char *path)
{
static char *retfail = NULL;
size_t len;
/* to handle path names for files in multibyte character locales,
* we need to set up LC_CTYPE to match the host file system locale
*/
char *locale = setlocale(LC_CTYPE, NULL);
if (locale != NULL)
locale = strdup(locale);
setlocale(LC_CTYPE, "");
if (path && *path) {
/* allocate sufficient local storage space,
* in which to create a wide character reference copy of path
*/
len = mbstowcs(NULL, path, 0);
wchar_t *refcopy = malloc(sizeof(wchar_t) * (1 + len));
/* create the wide character reference copy of path,
* and step over the drive designator, if present ...
*/
wchar_t *refpath = refcopy;
if ((len = mbstowcs(refpath, path, len)) > 1 && refpath[1] == L':') {
/* FIXME: maybe should confirm *refpath is a valid drive designator */
refpath += 2;
}
/* ensure that our wide character reference path is NUL terminated */
refcopy[len] = L'\0';
/* check again, just to ensure we still have a non-empty path name ... */
if (*refpath) {
/* and, when we do, process it in the wide character domain ...
* scanning from left to right, to the char after the final dir separator. */
wchar_t *refname;
for (refname = refpath; *refpath; ++refpath) {
if (*refpath == L'/' || *refpath == L'\\') {
/* we found a dir separator ...
* step over it, and any others which immediately follow it. */
while (*refpath == L'/' || *refpath == L'\\')
++refpath;
/* if we didn't reach the end of the path string ... */
if (*refpath)
/* then we have a new candidate for the base name. */
refname = refpath;
/* otherwise ...
* strip off any trailing dir separators which we found. */
else
while (refpath > refname && (*--refpath == L'/' || *refpath == L'\\'))
*refpath = L'\0';
}
}
/* in the wide character domain ...
* refname now points at the resolved base name ... */
if (*refname) {
/* if it's not empty,
* then we transform the full normalised path back into
* the multibyte character domain, and skip over the dirname,
* to return the resolved basename. */
if ((len = wcstombs(path, refcopy, len)) != (size_t)(-1))
path[len] = '\0';
*refname = L'\0';
if ((len = wcstombs(NULL, refcopy, 0)) != (size_t)(-1))
path += len;
} else {
/* the basename is empty, so return the default value of "/",
* transforming from wide char to multibyte char domain, and
* returning it in our own buffer. */
retfail = realloc(retfail, len = 1 + wcstombs(NULL, L"/", 0));
wcstombs(path = retfail, L"/", len);
}
/* restore the caller's locale, clean up, and return the result */
setlocale(LC_CTYPE, locale);
free(locale);
free(refcopy);
return path;
}
/* or we had an empty residual path name, after the drive designator,
* in which case we simply fall through ... */
free(refcopy);
}
/* and, if we get to here ...
* the path name is either NULL, or it decomposes to an empty string;
* in either case, we return the default value of "." in our own buffer,
* reloading it with the correct value, transformed from the wide char
* to the multibyte char domain, just in case the caller trashed it
* after a previous call.
*/
retfail = realloc(retfail, len = 1 + wcstombs(NULL, L".", 0));
wcstombs(retfail, L".", len);
/* restore the caller's locale, clean up, and return the result. */
setlocale(LC_CTYPE, locale);
free(locale);
return retfail;
}
char *dirname(char *path)
{
static char *retfail = NULL;
size_t len;
/* to handle path names for files in multibyte character locales,
* we need to set up LC_CTYPE to match the host file system locale. */
char *locale = setlocale(LC_CTYPE, NULL);
if (locale != NULL)
locale = strdup(locale);
setlocale(LC_CTYPE, "");
if (path && *path) {
/* allocate sufficient local storage space,
* in which to create a wide character reference copy of path. */
len = mbstowcs(NULL, path, 0);
wchar_t *refcopy = malloc(sizeof(wchar_t) * (1 + len));
/* create the wide character reference copy of path */
wchar_t *refpath = refcopy;
len = mbstowcs(refpath, path, len);
refcopy[len] = L'\0';
/* SUSv3 identifies a special case, where path is exactly equal to "//";
* (we will also accept "\\" in the Win32 context, but not "/\" or "\/",
* and neither will we consider paths with an initial drive designator).
* For this special case, SUSv3 allows the implementation to choose to
* return "/" or "//", (or "\" or "\\", since this is Win32); we will
* simply return the path unchanged, (i.e. "//" or "\\"). */
if (len > 1 && (refpath[0] == L'/' || refpath[0] == L'\\')) {
if (refpath[1] == refpath[0] && refpath[2] == L'\0') {
setlocale(LC_CTYPE, locale);
free(locale);
free(refcopy);
return path;
}
}
/* For all other cases ...
* step over the drive designator, if present ... */
else if (len > 1 && refpath[1] == L':') {
/* FIXME: maybe should confirm *refpath is a valid drive designator. */
refpath += 2;
}
/* check again, just to ensure we still have a non-empty path name ... */
if (*refpath) {
/* reproduce the scanning logic of the "basename" function
* to locate the basename component of the current path string,
* (but also remember where the dirname component starts). */
wchar_t *refname, *the_basename;
for (refname = the_basename = refpath; *refpath; ++refpath) {
if (*refpath == L'/' || *refpath == L'\\') {
/* we found a dir separator ...
* step over it, and any others which immediately follow it. */
while (*refpath == L'/' || *refpath == L'\\')
++refpath;
/* if we didn't reach the end of the path string ... */
if (*refpath)
/* then we have a new candidate for the base name. */
the_basename = refpath;
else
/* we struck an early termination of the path string,
* with trailing dir separators following the base name,
* so break out of the for loop, to avoid overrun. */
break;
}
}
/* now check,
* to confirm that we have distinct dirname and basename components. */
if (the_basename > refname) {
/* and, when we do ...
* backtrack over all trailing separators on the dirname component,
* (but preserve exactly two initial dirname separators, if identical),
* and add a NUL terminator in their place. */
do
--the_basename;
while (the_basename > refname && (*the_basename == L'/' || *the_basename == L'\\'));
if (the_basename == refname && (refname[0] == L'/' || refname[0] == L'\\') && refname[1] == refname[0]
&& refname[2] != L'/' && refname[2] != L'\\')
++the_basename;
*++the_basename = L'\0';
/* if the resultant dirname begins with EXACTLY two dir separators,
* AND both are identical, then we preserve them. */
refpath = refcopy;
while ((*refpath == L'/' || *refpath == L'\\'))
++refpath;
if ((refpath - refcopy) > 2 || refcopy[1] != refcopy[0])
refpath = refcopy;
/* and finally ...
* we remove any residual, redundantly duplicated separators from the dirname,
* reterminate, and return it. */
refname = refpath;
while (*refpath) {
if ((*refname++ = *refpath) == L'/' || *refpath++ == L'\\') {
while (*refpath == L'/' || *refpath == L'\\')
++refpath;
}
}
*refname = L'\0';
/* finally ...
* transform the resolved dirname back into the multibyte char domain,
* restore the caller's locale, and return the resultant dirname. */
if ((len = wcstombs(path, refcopy, len)) != (size_t)(-1))
path[len] = '\0';
} else {
/* either there were no dirname separators in the path name,
* or there was nothing else ... */
if (*refname == L'/' || *refname == L'\\') {
/* it was all separators, so return one. */
++refname;
} else {
/* there were no separators, so return '.'. */
*refname++ = L'.';
}
/* add a NUL terminator, in either case,
* then transform to the multibyte char domain,
* using our own buffer. */
*refname = L'\0';
retfail = realloc(retfail, len = 1 + wcstombs(NULL, refcopy, 0));
wcstombs(path = retfail, refcopy, len);
}
/* restore caller's locale, clean up, and return the resolved dirname. */
setlocale(LC_CTYPE, locale);
free(locale);
free(refcopy);
return path;
}
}
/* path is NULL, or an empty string; default return value is "." ...
* return this in our own buffer, regenerated by wide char transform,
* in case the caller trashed it after a previous call.
*/
retfail = realloc(retfail, len = 1 + wcstombs(NULL, L".", 0));
wcstombs(retfail, L".", len);
/* restore caller's locale, clean up, and return the default dirname. */
setlocale(LC_CTYPE, locale);
free(locale);
return retfail;
}

View File

@ -1,27 +0,0 @@
#ifndef WIN32PORTS_LIBGEN_H
#define WIN32PORTS_LIBGEN_H
#ifndef _WIN32
#pragma message("this libgen.h implementation is for Windows only!")
#elif defined __MINGW32__
#include <../include/libgen.h>
#else
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
char *basename(char *);
char *dirname(char *);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIN32 */
#endif /* WIN32PORTS_LIBGEN_H */

View File

@ -1,25 +0,0 @@
/*
MIT License
Copyright (c) 2019 win32ports
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <strings.h>
extern inline void explicit_bzero(void *s, size_t n);
extern inline int ffs(int i);
extern inline int ffsl(long i);
extern inline int ffsll(long long i);

View File

@ -1,105 +0,0 @@
/*
MIT License
Copyright (c) 2019 win32ports
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifndef WIN32PORTS_STRINGS_H
#define WIN32PORTS_STRINGS_H
#ifndef _WIN32
#pragma message("this strings.h implementation is for Windows only!")
#elif defined __MINGW32__
#include <../include/strings.h>
#else
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <ctype.h>
#include <string.h>
#define bcmp(s1, s2, n) memcmp(s1, s2, n)
#define bcopy(s, d, n) memcpy(d, s, n)
#define bzero(s, n) memset(s, 0, n)
inline void explicit_bzero(void *s, size_t n)
{
volatile char *vs = (volatile char *)s;
while (n) {
*vs++ = 0;
n--;
}
}
#define index(s, c) strchr(s, c)
#define rindex(s, c) strrchr(s, c)
inline int ffs(int i)
{
int bit;
if (0 == i)
return 0;
for (bit = 1; !(i & 1); ++bit)
i >>= 1;
return bit;
}
inline int ffsl(long i)
{
int bit;
if (0 == i)
return 0;
for (bit = 1; !(i & 1); ++bit)
i >>= 1;
return bit;
}
inline int ffsll(long long i)
{
int bit;
if (0 == i)
return 0;
for (bit = 1; !(i & 1); ++bit)
i >>= 1;
return bit;
}
#define strcasecmp(s1, s2) _stricmp(s1, s2)
#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
#define strcasecmp_l(s1, s2, loc) _stricmp_l(s1, s2, loc)
#define strncasecmp_l(s1, s2, n, loc) _strnicmp_l(s1, s2, n, loc)
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIN32 */
#endif /* WIN32PORTS_STRINGS_H */

View File

@ -1,265 +0,0 @@
/*
MIT License
Copyright (c) 2019 win32ports
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifndef WIN32PORTS_UNISTD_H
#define WIN32PORTS_UNISTD_H
#ifndef _WIN32
#pragma message("this unistd.h implementation is for Windows only!")
#else
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <windows.h>
#ifndef _INC_IO
#include <io.h> /* _access() */
#endif /* _INC_IO */
#ifndef _INC_DIRECT
#include <direct.h> /* _chdir() */
#endif /* _INC_DIRECT */
#ifndef _INC_PROCESS
#include <process.h> /* _execl() */
#endif /* _INC_PROCESS */
#include <sys/stat.h> /* */
#include <getopt.h>
#ifndef R_OK
#define R_OK 04
#endif /* R_OK */
#ifndef W_OK
#define W_OK 02
#endif /* W_OK */
#ifndef X_OK
#define X_OK R_OK
#endif /* X_OK */
#ifndef F_OK
#define F_OK 00
#endif /* F_OK */
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif /* STDIN_FILENO */
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif /* STDOUT_FILENO */
#ifndef STDERR_FILENO
#define STDERR_FILENO 2
#endif /* STDERR_FILENO */
/* permission bits below must be defined in sys/stat.h, but MSVC lacks them */
#ifndef S_IRWXU
#define S_IRWXU 0700
#endif /* S_IRWXU */
#ifndef S_IRUSR
#define S_IRUSR 0400
#endif /* S_IRUSR */
#ifndef S_IWUSR
#define S_IWUSR 0200
#endif /* S_IWUSR */
#ifndef S_IXUSR
#define S_IXUSR 0100
#endif /* S_IXUSR */
#ifndef S_IRWXG
#define S_IRWXG 070
#endif /* S_IRWXG */
#ifndef S_IRGRP
#define S_IRGRP 040
#endif /* S_IRGRP */
#ifndef S_IWGRP
#define S_IWGRP 020
#endif /* S_IWGRP */
#ifndef S_IXGRP
#define S_IXGRP 010
#endif /* S_IXGRP */
#ifndef S_IRWXO
#define S_IRWXO 07
#endif /* S_IRWXO */
#ifndef S_IROTH
#define S_IROTH 04
#endif /* S_IROTH */
#ifndef S_IWOTH
#define S_IWOTH 02
#endif /* S_IWOTH */
#ifndef S_IXOTH
#define S_IXOTH 01
#endif /* S_IXOTH */
#ifndef S_ISUID
#define S_ISUID 04000
#endif /* S_ISUID */
#ifndef S_ISGID
#define S_ISGID 02000
#endif /* S_ISGID */
#ifndef S_ISVTX
#define S_ISVTX 01000
#endif /* S_ISVTX */
#ifndef S_IRWXUGO
#define S_IRWXUGO 0777
#endif /* S_IRWXUGO */
#ifndef S_IALLUGO
#define S_IALLUGO 0777
#endif /* S_IALLUGO */
#ifndef S_IRUGO
#define S_IRUGO 0444
#endif /* S_IRUGO */
#ifndef S_IWUGO
#define S_IWUGO 0222
#endif /* S_IWUGO */
#ifndef S_IXUGO
#define S_IXUGO 0111
#endif /* S_IXUGO */
#ifndef _S_IFMT
#define _S_IFMT 0xF000
#endif /* _S_IFMT */
#ifndef _S_IFIFO
#define _S_IFIFO 0x1000
#endif /* _S_IFIFO */
#ifndef _S_IFCHR
#define _S_IFCHR 0x2000
#endif /* _S_IFCHR */
#ifndef _S_IFDIR
#define _S_IFDIR 0x4000
#endif /* _S_IFDIR */
#ifndef _S_IFBLK
#define _S_IFBLK 0x6000
#endif /* _S_IFBLK */
#ifndef _S_IFREG
#define _S_IFREG 0x8000
#endif /* _S_IFREG */
#ifndef _S_IFLNK
#define _S_IFLNK 0xA000
#endif /* _S_IFLNK */
#ifndef _S_IFSOCK
#define _S_IFSOCK 0xC000
#endif /* _S_IFSOCK */
#ifndef S_IFMT
#define S_IFMT _S_IFMT
#endif /* S_IFMT */
#ifndef S_IFIFO
#define S_IFIFO _S_IFIFO
#endif /* S_IFIFO */
#ifndef S_IFCHR
#define S_IFCHR _S_IFCHR
#endif /* S_IFCHR */
#ifndef S_IFDIR
#define S_IFDIR _S_IFDIR
#endif /* S_IFDIR */
#ifndef S_IFBLK
#define S_IFBLK _S_IFBLK
#endif /* S_IFBLK */
#ifndef S_IFREG
#define S_IFREG _S_IFREG
#endif /* S_IFREG */
#ifndef S_IFLNK
#define S_IFLNK _S_IFLNK
#endif /* S_IFLNK */
#ifndef S_IFSOCK
#define S_IFSOCK _S_IFSOCK
#endif /* S_IFSOCK */
#ifndef S_ISTYPE
#define S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask))
#endif /* S_ISTYPE */
#ifndef S_ISFIFO
#define S_ISFIFO(mode) S_ISTYPE(mode, S_IFIFO)
#endif /* S_ISFIFO */
#ifndef S_ISCHR
#define S_ISCHR(mode) S_ISTYPE(mode, S_IFCHR)
#endif /* S_ISCHR */
#ifndef S_ISDIR
#define S_ISDIR(mode) S_ISTYPE(mode, S_IFDIR)
#endif /* S_ISDIR */
#ifndef S_ISBLK
#define S_ISBLK(mode) S_ISTYPE(mode, S_IFBLK)
#endif /* S_ISBLK */
#ifndef S_ISREG
#define S_ISREG(mode) S_ISTYPE(mode, S_IFREG)
#endif /* S_ISREG */
#ifndef S_ISLNK
#define S_ISLNK(mode) S_ISTYPE(mode, S_IFLNK)
#endif /* S_ISLNK */
#ifndef S_ISSOCK
#define S_ISSOCK(mode) S_ISTYPE(mode, S_IFSOCK)
#endif /* S_ISSOCK */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIN32 */
#endif /* WIN32PORTS_UNISTD_H */

View File

@ -10,7 +10,7 @@ target_include_directories(brender
include
)
target_link_libraries(brender PRIVATE harness miniposix)
target_link_libraries(brender PRIVATE harness)
if(NOT MSVC)
target_compile_options(brender PRIVATE

View File

@ -2,7 +2,7 @@
#include "harness/trace.h"
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
// IDA: void __cdecl BrNewList(br_list *list)
void BrNewList(br_list* list) {

View File

@ -11,38 +11,38 @@
br_file_primitives _BrFilePrimsNull = {
"NULL",
(int(*)(br_datafile*,br_uint_32))&BrNullOther,
(int(*)(br_datafile*, br_uint_32, br_uint_32))&BrNullOther,
(int(*)(br_datafile*, br_uint_32*))&BrNullOther,
(void(*)(br_datafile*, br_uint_32))&BrNullOther,
(br_uint_32(*)(br_datafile*))&BrNullOther,
(int(*)(br_datafile*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(int(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(int(*)(br_datafile*, void*, int, int, int, int))&BrNullOther,
(void*(*)(br_datafile*, void*, int*, int, int))&BrNullOther,
(int(*)(br_datafile*, void*, int, int, int, int))&BrNullOther,
(int(*)(br_datafile*, char*))&BrNullOther,
(char*(*)(br_datafile*, char*))&BrNullOther,
(int(*)(br_datafile*, char*))&BrNullOther,
(int (*)(br_datafile*, br_uint_32)) & BrNullOther,
(int (*)(br_datafile*, br_uint_32, br_uint_32)) & BrNullOther,
(int (*)(br_datafile*, br_uint_32*)) & BrNullOther,
(void (*)(br_datafile*, br_uint_32)) & BrNullOther,
(br_uint_32(*)(br_datafile*)) & BrNullOther,
(int (*)(br_datafile*)) & BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
(int (*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
(int (*)(br_datafile*, void*, int, int, int, int)) & BrNullOther,
(void* (*)(br_datafile*, void*, int*, int, int)) & BrNullOther,
(int (*)(br_datafile*, void*, int, int, int, int)) & BrNullOther,
(int (*)(br_datafile*, char*)) & BrNullOther,
(char* (*)(br_datafile*, char*)) & BrNullOther,
(int (*)(br_datafile*, char*)) & BrNullOther,
};
br_file_primitives _BrFilePrimsReadBinary = {
"Read Binary",
&DfSkipBinary,
(int(*)(br_datafile*, br_uint_32, br_uint_32))&BrNullOther,
(int (*)(br_datafile*, br_uint_32, br_uint_32)) & BrNullOther,
&DfChunkReadBinary,
(void(*)(br_datafile*, br_uint_32))&BrNullOther,
(void (*)(br_datafile*, br_uint_32)) & BrNullOther,
&DfCountReadBinary,
&DfCountSizeBinary,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
&DfStructReadBinary,
&DfStructSizeBinary,
(int(*)(br_datafile*, void*, int, int, int, int))&BrNullOther,
(int (*)(br_datafile*, void*, int, int, int, int)) & BrNullOther,
&DfBlockReadBinary,
&DfBlockSizeBinary,
(int(*)(br_datafile*, char*))&BrNullOther,
(int (*)(br_datafile*, char*)) & BrNullOther,
&DfNameReadBinary,
&DfNameSizeBinary,
};
@ -51,36 +51,36 @@ br_file_primitives _BrFilePrimsWriteBinary = {
"Write Binary",
&DfSkipBinary,
&DfChunkWriteBinary,
(int(*)(br_datafile*, br_uint_32*))&BrNullOther,
(int (*)(br_datafile*, br_uint_32*)) & BrNullOther,
&DfCountWriteBinary,
(br_uint_32(*)(br_datafile*))&BrNullOther,
(br_uint_32(*)(br_datafile*)) & BrNullOther,
&DfCountSizeBinary,
&DfStructWriteBinary,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
&DfStructSizeBinary,
&DfBlockWriteBinary,
(void*(*)(br_datafile*, void*, int*, int, int))&BrNullOther,
(void* (*)(br_datafile*, void*, int*, int, int)) & BrNullOther,
&DfBlockSizeBinary,
&DfNameWriteBinary,
(char*(*)(br_datafile*, char*))&BrNullOther,
(char* (*)(br_datafile*, char*)) & BrNullOther,
&DfNameSizeBinary,
};
br_file_primitives _BrFilePrimsReadText = {
"Read Text",
&DfSkipText,
(int(*)(br_datafile*, br_uint_32, br_uint_32))&BrNullOther,
(int (*)(br_datafile*, br_uint_32, br_uint_32)) & BrNullOther,
&DfChunkReadText,
(void(*)(br_datafile*, br_uint_32))&BrNullOther,
(void (*)(br_datafile*, br_uint_32)) & BrNullOther,
&DfCountReadText,
&DfCountSizeText,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
&DfStructReadText,
&DfStructSizeText,
(int(*)(br_datafile*, void*, int, int, int, int))&BrNullOther,
(int (*)(br_datafile*, void*, int, int, int, int)) & BrNullOther,
&DfBlockReadText,
&DfBlockSizeText,
(int(*)(br_datafile*, char*))&BrNullOther,
(int (*)(br_datafile*, char*)) & BrNullOther,
&DfNameReadText,
&DfNameSizeText,
};
@ -89,18 +89,18 @@ br_file_primitives _BrFilePrimsWriteText = {
"Write Text",
&DfSkipText,
&DfChunkWriteText,
(int(*)(br_datafile*, br_uint_32*))&BrNullOther,
(int (*)(br_datafile*, br_uint_32*)) & BrNullOther,
&DfCountWriteText,
(br_uint_32(*)(br_datafile*))&BrNullOther,
(br_uint_32(*)(br_datafile*)) & BrNullOther,
&DfCountSizeText,
&DfStructWriteText,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*))&BrNullOther,
(br_uint_32(*)(br_datafile*, br_file_struct*, void*)) & BrNullOther,
&DfStructSizeText,
&DfBlockWriteText,
(void*(*)(br_datafile*, void*, int*, int, int))&BrNullOther,
(void* (*)(br_datafile*, void*, int*, int, int)) & BrNullOther,
&DfBlockSizeText,
&DfNameWriteText,
(char*(*)(br_datafile*, char*))&BrNullOther,
(char* (*)(br_datafile*, char*)) & BrNullOther,
&DfNameSizeText,
};
@ -289,7 +289,8 @@ int TextReadLine(br_datafile* df, char** ident, char** data) {
}
for (; (*cp == ' ') || (*cp == '\t'); cp++) {
}
if (*cp != '\0') break;
if (*cp != '\0')
break;
}
*ident = cp;
while ((*cp != ' ') && (*cp != '\t') && (*cp != '\0')) {
@ -320,7 +321,7 @@ br_uint_16 scalarTypeConvert(br_datafile* df, br_uint_16 t) {
LOG_TRACE9("(%p, %d)", df, t);
if (df->scalar_type == BRT_FIXED) {
switch(t) {
switch (t) {
case DF_TYPE_BR_SCALAR:
return DF_TYPE_BR_FIXED;
case DF_TYPE_BR_FRACTION:
@ -418,8 +419,8 @@ br_uint_32 DfStructWriteBinary(br_datafile* df, br_file_struct* str, void* base)
DfStructWriteBinary(df, sm->extra, mp);
break;
case DF_TYPE_ASCIZ:
if (*(char **)mp != NULL) {
BrFileWrite(*(char **)mp, 1, BrStrLen(*(char**)mp), df->h);
if (*(char**)mp != NULL) {
BrFileWrite(*(char**)mp, 1, BrStrLen(*(char**)mp), df->h);
}
BrFilePutChar('\0', df->h);
break;
@ -822,14 +823,14 @@ br_uint_32 StructWriteTextSub(br_datafile* df, br_file_struct* str, void* base,
StructWriteTextSub(df, sm->extra, mp, indent + 2);
break;
case DF_TYPE_ASCIZ:
if (*(char **)mp == NULL) {
if (*(char**)mp == NULL) {
w = BrFilePrintf(df->h, "NULL");
} else {
w = BrFilePrintf(df->h, "\"%s\"", *(char **)mp);
w = BrFilePrintf(df->h, "\"%s\"", *(char**)mp);
}
break;
case DF_TYPE_BR_COLOUR:
w = BrFilePrintf(df->h, "%d,%d,%d", (br_uint_8)((*(br_uint_32 *)mp) >> 16), (br_uint_8)((*(br_uint_32 *)mp) >> 8), (br_uint_8)((*(br_uint_32 *)mp)));
w = BrFilePrintf(df->h, "%d,%d,%d", (br_uint_8)((*(br_uint_32*)mp) >> 16), (br_uint_8)((*(br_uint_32*)mp) >> 8), (br_uint_8)((*(br_uint_32*)mp)));
break;
case DF_TYPE_BR_FRACTION_X:
w = BrFilePrintf(df->h, "%g", (double)BrFixedFractionToFloat(*(br_fraction_x*)mp));
@ -1499,7 +1500,7 @@ int DfChunksInterpret(br_datafile* df, br_chunks_table* table) {
while (1) {
id = df->prims->chunk_read(df, &length);
//LOG_DEBUG("chunk id=%d, len=%d", id, length);
// LOG_DEBUG("chunk id=%d, len=%d", id, length);
if (id == (br_uint_32)-1) {
break;
}
@ -1563,7 +1564,6 @@ br_datafile* DfOpen(char* name, int write, br_token scalar_type) {
h = BrFileOpenRead(name, 8u, DfFileIdentify, &mode);
}
if (h == NULL) {
LOG_WARN("returning NULL");
return NULL;
}
df = BrResAllocate(fw.res, sizeof(br_datafile), BR_MEMORY_DATAFILE);

View File

@ -10,7 +10,7 @@ target_include_directories(dethrace_obj
pd
)
target_link_libraries(dethrace_obj PUBLIC miniposix SDL2::SDL2 smacker harness brender s3 cglm)
target_link_libraries(dethrace_obj PUBLIC SDL2::SDL2 smacker harness brender s3 cglm)
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(dethrace_obj PRIVATE
@ -154,8 +154,6 @@ target_sources(dethrace_obj PRIVATE
pd/net.h
pc-dos/dossys.c
pd/sys.h
watcom_functions.c
watcom_functions.h
)
# Create our main game binary.

View File

@ -4,6 +4,7 @@
#include "globvrpb.h"
#include "graphics.h"
#include "harness/config.h"
#include "harness/os.h"
#include "harness/trace.h"
#include "input.h"
#include "loading.h"
@ -13,7 +14,6 @@
#include "utility.h"
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
tS32 gLast_demo_end_anim;
@ -38,7 +38,7 @@ void PlaySmackerFile(char* pSmack_name) {
tPath_name the_path;
br_colour* br_colours_ptr;
tU8* smack_colours_ptr;
//Smack* smk;
// Smack* smk;
int i;
int j;
int len;
@ -83,10 +83,7 @@ void PlaySmackerFile(char* pSmack_name) {
smk_info_video(s, &w, &h, NULL);
double fps = 1000000.0 / usf;
int delay_ms = (1 / fps) * 1000;
#ifndef _WIN32
ts.tv_sec = delay_ms / 1000;
ts.tv_nsec = (delay_ms % 1000) * 1000000;
#endif
smk_enable_video(s, 1);
smk_first(s);
@ -112,13 +109,8 @@ void PlaySmackerFile(char* pSmack_name) {
if (AnyKeyDown() || EitherMouseButtonDown()) {
break;
}
// wait until its time for the next frame
#ifndef _WIN32
nanosleep(&ts, &ts);
#else
Sleep(delay_ms);
#endif
OS_Sleep(delay_ms);
} while (smk_next(s) == SMK_MORE);
smk_close(s);
@ -138,7 +130,7 @@ void DoOpeningAnimation() {
LOG_TRACE("()");
PlaySmackerFile("LOGO.SMK");
PlaySmackerFile(harness_game_info.defines.INTRO_SMK_FILE);
return WaitForNoKeys();
WaitForNoKeys();
}
// IDA: void __cdecl DoNewGameAnimation()

View File

@ -282,8 +282,6 @@ void MAMSInitMem() {
FILE* f;
tPath_name the_path;
LOG_TRACE("()");
LOG_WARN("nop in Windows (doing something for DOS?)");
}
// IDA: void __usercall PrintMemoryDump(int pFlags@<EAX>, char *pTitle@<EDX>)

View File

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include "globvars.h"
#include "graphics.h"
#include "harness/trace.h"
#include "network.h"
@ -148,27 +149,26 @@ void FatalError(int pStr_index, ...) {
va_start(ap, pStr_index);
int read_timer = PDGetTotalTime();
gError_code = 0x20000000 + read_timer;
strcpy(the_str, gError_messages[pStr_index - 1]);
gError_code = 0x20000000 + PDGetTotalTime();
strcpy(the_str, gError_messages[pStr_index]);
sub_pt = temp_str;
for (i = 0; i < strlen(the_str); i++) {
if (the_str[i] == '%') {
sub_str = va_arg(ap, char*);
StripCR(sub_str);
strcpy(sub_pt, sub_str);
sub_pt += strlen(sub_str);
} else {
*sub_pt = the_str[i];
sub_pt++;
while (1) {
sub_pt = strchr(the_str, '%');
if (!sub_pt) {
break;
}
sub_str = va_arg(ap, char*);
StripCR(sub_str);
strcpy(temp_str, sub_pt + 1);
strcpy(sub_pt, sub_str);
strcat(the_str, temp_str);
}
*sub_pt = 0;
va_end(ap);
dr_dprintf(the_str);
FadePaletteUp();
PDFatalError(temp_str);
PDFatalError(the_str);
}
// IDA: void __cdecl NonFatalError(int pStr_index, ...)
@ -205,22 +205,40 @@ void NonFatalError(int pStr_index, ...) {
// IDA: void __cdecl CloseDiagnostics()
void CloseDiagnostics() {
LOG_TRACE("()");
fclose(gDiagnostic_file);
}
// IDA: void __cdecl OpenDiagnostics()
void OpenDiagnostics() {
LOG_TRACE("()");
gDiagnostic_file = fopen("DIAGNOST.TXT", "w");
fputs("DIAGNOSTIC OUTPUT\n", gDiagnostic_file);
// todo: generate a real date
fprintf(gDiagnostic_file, "Date / time : %s\n\n\n", "Mon Mar 24 16 : 32 : 33 1997");
}
// Renamed from dprintf to avoid collisions to stdio
// This function is stripped from the retail binary, we've guessed at the implementation
void dr_dprintf(char* fmt_string, ...) {
static tU32 first_time = 0;
va_list args;
tU32 the_time;
if (first_time == 0) {
first_time = GetTotalTime();
}
the_time = GetTotalTime() - first_time;
fprintf(gDiagnostic_file, "%7d.%02d: ", the_time / 1000, the_time % 100);
printf("dprintf: ");
va_start(args, fmt_string);
vprintf(fmt_string, args);
vfprintf(gDiagnostic_file, fmt_string, args);
va_end(args);
printf("\n");
fputs("\n", gDiagnostic_file);
}
// IDA: int __usercall DoErrorInterface@<EAX>(int pMisc_text_index@<EAX>)

View File

@ -12,7 +12,6 @@
#include "sound.h"
#include "utility.h"
#include <stdlib.h>
#include <unistd.h>
int gPalette_allocate_count;
int gPalette_fuck_prevention;
@ -694,7 +693,7 @@ int StartFlic(char* pFile_name, int pIndex, tFlic_descriptor_ptr pFlic_info, tU3
} else {
pFlic_info->f = NULL;
pFlic_info->data = (char*)pData_ptr;
//TOOD: remove this - we added this line because of the padding hack in PlayNextFlicFrame2
// TOOD: remove this - we added this line because of the padding hack in PlayNextFlicFrame2
pFlic_info->data_start = (char*)pData_ptr;
}
pFlic_info->bytes_remaining = MemReadU32(&pFlic_info->data);
@ -746,7 +745,7 @@ int StartFlic(char* pFile_name, int pIndex, tFlic_descriptor_ptr pFlic_info, tU3
if (pDest_pixelmap) {
pFlic_info->first_pixel = (tU8*)pDest_pixelmap->pixels + pDest_pixelmap->row_bytes * pFlic_info->y_offset + pFlic_info->x_offset;
}
//LOG_DEBUG("first pixel %p %p", pFlic_info->first_pixel, pDest_pixelmap->pixels);
// LOG_DEBUG("first pixel %p %p", pFlic_info->first_pixel, pDest_pixelmap->pixels);
pFlic_info->the_pixelmap = pDest_pixelmap;
return 0;
}
@ -809,7 +808,7 @@ void DoColourMap(tFlic_descriptor_ptr pFlic_info, tU32 chunk_length) {
red = MemReadU8(&pFlic_info->data);
blue = MemReadU8(&pFlic_info->data);
green = MemReadU8(&pFlic_info->data);
//argb
// argb
palette_pixels[0] = green * 4;
palette_pixels[1] = blue * 4;
palette_pixels[2] = red * 4;
@ -887,13 +886,13 @@ void DoColour256(tFlic_descriptor* pFlic_info, tU32 chunk_length) {
red = MemReadU8(&pFlic_info->data);
blue = MemReadU8(&pFlic_info->data);
green = MemReadU8(&pFlic_info->data);
//argb
// argb
palette_pixels[0] = green;
palette_pixels[1] = blue;
palette_pixels[2] = red;
palette_pixels[3] = 0;
palette_pixels += 4;
//LOG_DEBUG("color %d", current_colour);
// LOG_DEBUG("color %d", current_colour);
}
if (!gPalette_fuck_prevention) {
DRSetPaletteEntries(gPalette, current_colour, change_count);
@ -1172,7 +1171,7 @@ int PlayNextFlicFrame2(tFlic_descriptor* pFlic_info, int pPanel_flic) {
int data_knocked_off;
int read_amount;
//LOG_DEBUG("%d (%p), frames left: %d offset: %d", pFlic_info->the_index, pFlic_info, pFlic_info->frames_left, (pFlic_info->data - pFlic_info->data_start) + 4);
// LOG_DEBUG("%d (%p), frames left: %d offset: %d", pFlic_info->the_index, pFlic_info, pFlic_info->frames_left, (pFlic_info->data - pFlic_info->data_start) + 4);
PossibleService();
frame_length = MemReadU32(&pFlic_info->data);
magic_bytes = MemReadU16(&pFlic_info->data);
@ -1229,7 +1228,7 @@ int PlayNextFlicFrame2(tFlic_descriptor* pFlic_info, int pPanel_flic) {
MemSkipBytes(&pFlic_info->data, chunk_length - 6);
break;
}
//TODO: something like // p &= 0xfffffffffffffffe;
// TODO: something like // p &= 0xfffffffffffffffe;
int a = (pFlic_info->data - pFlic_info->data_start);
if (a % 2 == 1) {
pFlic_info->data++;

View File

@ -32,7 +32,6 @@
#include "utility.h"
#include "world.h"
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
@ -2389,7 +2388,7 @@ void DropInImageFromTop(br_pixelmap* pImage, int pLeft, int pTop, int pTop_clip,
tS32 the_time;
int drop_distance;
LOG_TRACE("(%p, %d, %d, %d, %d)", pImage, pLeft, pTop, pTop_clip, pBottom_clip);
start_time = PDGetTotalTime();
drop_distance = pImage->height - pTop_clip + pTop;
while (1) {
@ -2413,7 +2412,7 @@ void DropOutImageThruBottom(br_pixelmap* pImage, int pLeft, int pTop, int pTop_c
tS32 the_time;
int drop_distance;
LOG_TRACE("(%p, %d, %d, %d, %d)", pImage, pLeft, pTop, pTop_clip, pBottom_clip);
start_time = PDGetTotalTime();
drop_distance = pBottom_clip - pTop;
while (1) {
@ -2437,7 +2436,7 @@ void DropInImageFromBottom(br_pixelmap* pImage, int pLeft, int pTop, int pTop_cl
tS32 the_time;
int drop_distance;
LOG_TRACE("(%p, %d, %d, %d, %d)", pImage, pLeft, pTop, pTop_clip, pBottom_clip);
start_time = PDGetTotalTime();
drop_distance = pBottom_clip - pTop;
while (1) {
@ -2461,7 +2460,7 @@ void DropOutImageThruTop(br_pixelmap* pImage, int pLeft, int pTop, int pTop_clip
tS32 the_time;
int drop_distance;
LOG_TRACE("(%p, %d, %d, %d, %d)", pImage, pLeft, pTop, pTop_clip, pBottom_clip);
start_time = PDGetTotalTime();
drop_distance = pImage->height - pTop_clip + pTop;
while (1) {

View File

@ -3195,9 +3195,6 @@ FILE* DRfopen(char* pFilename, char* pMode) {
PDFatalError(msg);
}
}
if (result == NULL) {
LOG_WARN("failed for %d", errno);
}
return result;
}

View File

@ -638,8 +638,6 @@ tRace_result MainGameLoop() {
gAbandon_game = 0;
}
Harness_Hook_MainGameLoop();
} while (gProgram_state.prog_status == eProg_game_ongoing
&& !MungeRaceFinished()
&& !gAbandon_game

View File

@ -21,7 +21,6 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// Added >>
#define MIN_SERVICE_INTERVAL 200
@ -1312,7 +1311,7 @@ void EncodeFile(char* pThe_path) {
fclose(d);
PDFileUnlock(pThe_path);
unlink(pThe_path);
remove(pThe_path);
rename(new_file, pThe_path);
}

View File

@ -1,7 +1,6 @@
#include "harness/hooks.h"
#include "pd/sys.h"
#include <stdlib.h>
#include <unistd.h>
extern int original_main(int pArgc, char* pArgv[]);
@ -10,15 +9,15 @@ int main(int argc, char* argv[]) {
/* Attach to the console that started us if any */
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
/* We attached successfully, lets redirect IO to the consoles handles if not already redirected */
if (_fileno(stdout) == -2 || _get_osfhandle(fileno(stdout)) == -2) {
if (_fileno(stdout) == -2 || _get_osfhandle(_fileno(stdout)) == -2) {
freopen("CONOUT$", "w", stdout);
}
if (_fileno(stderr) == -2 || _get_osfhandle(fileno(stderr)) == -2) {
if (_fileno(stderr) == -2 || _get_osfhandle(_fileno(stderr)) == -2) {
freopen("CONOUT$", "w", stderr);
}
if (_fileno(stdin) == -2 || _get_osfhandle(fileno(stdin)) == -2) {
if (_fileno(stdin) == -2 || _get_osfhandle(_fileno(stdin)) == -2) {
freopen("CONIN$", "r", stdin);
}
}

View File

@ -352,6 +352,7 @@ void PDNetObtainSystemUserName(char* pName, int pMax_length) {
int size;
char buffer[16];
BOOL result;
#endif
dr_dprintf("PDNetObtainSystemUserName()\n");

View File

@ -6,6 +6,7 @@
#include "graphics.h"
#include "harness/config.h"
#include "harness/hooks.h"
#include "harness/os.h"
#include "harness/trace.h"
#include "input.h"
#include "loadsave.h"
@ -13,43 +14,12 @@
#include "pd/sys.h"
#include "sound.h"
#include "utility.h"
#include "watcom_functions.h"
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#ifdef _WIN32
#define NS_PER_SEC (1000ULL * 1000ULL * 1000ULL)
#define CLOCK_MONOTONIC 1
int clock_gettime(int dummy, struct timespec* tv) {
static LARGE_INTEGER ticksPerSec;
LARGE_INTEGER ticks;
double seconds;
if (!ticksPerSec.QuadPart) {
QueryPerformanceFrequency(&ticksPerSec);
if (!ticksPerSec.QuadPart) {
errno = ENOTSUP;
return -1;
}
}
QueryPerformanceCounter(&ticks);
seconds = (double)ticks.QuadPart / (double)ticksPerSec.QuadPart;
tv->tv_sec = (time_t)seconds;
tv->tv_nsec = (long)((ULONGLONG)(seconds * NS_PER_SEC) % NS_PER_SEC);
return 0;
}
#endif
#ifdef __DOS__
#define GFX_INIT_STRING_32X20X8 "MCGA,W:320,H:200,B:8"
@ -230,9 +200,9 @@ void KeyBegin() {
// *(_WORD *)gScan_code[9] = 56;
// *(_WORD *)gScan_code[10] = 29;
//gPrev_keyboard_handler = (void (__fastcall *)())dos_getvect(9);
//unk_142E6C = v2;
//dos_setvect(9, KeyboardHandler);
// gPrev_keyboard_handler = (void (__fastcall *)())dos_getvect(9);
// unk_142E6C = v2;
// dos_setvect(9, KeyboardHandler);
}
// IDA: void __cdecl KeyEnd()
@ -297,8 +267,6 @@ void PDFatalError(char* pThe_str) {
// wait for keypress
Harness_Debug_PrintStack();
if (!_unittest_do_not_exit) {
exit(1);
}
@ -318,9 +286,9 @@ void PDInitialiseSystem() {
KeyBegin();
//v4 = DOSMouseBegin();
// v4 = DOSMouseBegin();
gJoystick_deadzone = 8000;
//gUpper_loop_limit = sub_A1940(v4, v5, v3, v6) / 2;
// gUpper_loop_limit = sub_A1940(v4, v5, v3, v6) / 2;
// Demo does not ship with KEYBOARD.COK file
if (harness_game_info.mode != eGame_carmageddon_demo) {
@ -520,20 +488,12 @@ void PDBuildAppPath(char* pThe_path) {
void PDForEveryFile(char* pThe_path, void (*pAction_routine)(char*)) {
char find_path[256];
char found_path[256];
//find_t the_find_buffer;
DIR* d;
struct dirent* entry;
d = opendir(pThe_path);
if (d) {
while ((entry = readdir(d)) != NULL) {
// only files, and only files that don't start with '.'
if (entry->d_type == DT_REG && entry->d_name[0] != '.') {
PathCat(found_path, pThe_path, entry->d_name);
pAction_routine(found_path);
}
}
closedir(d);
char* found = OS_GetFirstFileInDirectory(pThe_path);
while (found != NULL) {
PathCat(found_path, pThe_path, found);
pAction_routine(found_path);
found = OS_GetNextFileInDirectory();
}
}
@ -592,9 +552,7 @@ void PDGetMousePosition(int* pX_coord, int* pY_coord) {
// IDA: int __cdecl PDGetTotalTime()
int PDGetTotalTime() {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return spec.tv_sec * 1000 + spec.tv_nsec / 1000000;
return OS_GetTime();
}
// IDA: int __usercall PDServiceSystem@<EAX>(tU32 pTime_since_last_call@<EAX>)
@ -637,9 +595,10 @@ void PDDisposeActionReplayBuffer(char* pBuffer) {
// IDA: void __usercall Usage(char *pProgpath@<EAX>)
void Usage(char* pProgpath) {
char basename[9];
// char basename[9];
char basename[256]; // fix: changed from 9 to avoid overflow on longer filenames
splitpath(pProgpath, NULL, NULL, basename, NULL);
OS_Basename(pProgpath, basename);
fprintf(stderr,
"Usage: %s [%s] [%s YonFactor] [%s CarSimplificationLevel] [%s SoundDetailLevel] [%s] [%s] [%s] [%s] [%s] [%s]\nWhere YonFactor is between 0 and 1,\nCarSimplificationLevel is a whole number between 0 and %d,\nand SoundDetailLevel is a whole number.\n",

View File

@ -1,26 +0,0 @@
#include "watcom_functions.h"
#include <ctype.h>
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#else
#include <strings.h>
#endif
void splitpath(char* path, char* drive, char* dir, char* fname, char* ext) {
#if defined(_WIN32) || defined(_WIN64)
_splitpath(path, drive, dir, fname, ext);
#else
// shortcut - we only ever call this method asking for 'fname'
// 9 is hardcoded to match `basename` defined in `Usage`
strncpy(fname, basename(path), 9);
#endif
}

View File

@ -1,6 +0,0 @@
#ifndef _WATCOM_FUNCTIONS_H_
#define _WATCOM_FUNCTIONS_H_
void splitpath(char* path, char* drive, char* dir, char* fname, char* ext);
#endif

View File

@ -1,5 +1,9 @@
add_library(harness STATIC)
if (NOT DEFINED IO_PLATFORM)
set(IO_PLATFORM "SDL_OpenGL")
endif()
target_include_directories(harness
PRIVATE
.
@ -9,7 +13,7 @@ target_include_directories(harness
include
)
target_link_libraries(harness PRIVATE miniposix brender SDL2::SDL2 glad s3 cglm_headers)
target_link_libraries(harness PRIVATE brender s3 cglm_headers)
if(WIN32)
target_link_libraries(harness PRIVATE dbghelp)
@ -30,39 +34,45 @@ target_sources(harness PRIVATE
include/harness/hooks.h
include/harness/trace.h
include/harness/config.h
include/harness/os.h
cameras/debug_camera.c
cameras/debug_camera.h
harness_trace.c
harness.c
harness.h
sdl/gl_renderer.c
sdl/gl_renderer.h
sdl/gl_renderer_shaders.c
sdl/gl_renderer_shaders.h
sdl/gl_brender_stored_context.c
sdl/gl_brender_stored_context.h
io_platforms/io_platform.h
renderers/null.h
renderers/renderer.h
brender_emu/renderer_impl.c
brender_emu/renderer_impl.h
sound/sound.c
sound/sound.h
sdl/common.c
sdl/common.h
platforms/sdl_gl.h
platforms/null.h
stack_trace_handler.h
)
if (IO_PLATFORM STREQUAL "SDL_OpenGL")
target_sources(harness PRIVATE
io_platforms/sdl_gl.c
renderers/gl/gl_renderer.c
renderers/gl/gl_renderer.h
renderers/gl/shaders.c
renderers/gl/shaders.h
renderers/gl/stored_context.c
renderers/gl/stored_context.h
)
target_link_libraries(harness PRIVATE SDL2::SDL2 glad)
endif()
if(WIN32)
target_sources(harness PRIVATE
platforms/platform_windows.c
os/windows.c
)
elseif(APPLE)
target_sources(harness PRIVATE
platforms/platform_macosx.c
os/macos.c
)
else()
target_sources(harness PRIVATE
platforms/platform_linux.c
os/linux.c
)
endif()

View File

@ -1,16 +1,17 @@
#include "harness.h"
#include "brender_emu/renderer_impl.h"
#include "include/harness/config.h"
#include "platforms/null.h"
#include "platforms/sdl_gl.h"
#include "include/harness/os.h"
#include "io_platforms/io_platform.h"
#include "renderers/null.h"
#include "sound/sound.h"
#include "stack_trace_handler.h"
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
tPlatform* platform;
tRenderer* renderer;
br_pixelmap* palette;
uint32_t* screen_buffer;
harness_br_renderer* renderer_state;
@ -21,6 +22,7 @@ br_pixelmap* last_src = NULL;
br_pixelmap *last_colour_buffer, *last_depth_buffer;
unsigned int last_frame_time = 0;
int force_nullrenderer = 0;
extern unsigned int GetTotalTime();
extern uint8_t gScan_code[123][2];
@ -71,11 +73,10 @@ void Harness_Init(int* argc, char* argv[]) {
Harness_ProcessCommandLine(argc, argv);
if (harness_game_config.install_signalhandler) {
install_signal_handler(argv[0]);
OS_InstallSignalHandler(argv[0]);
}
platform->Init();
int* keymap = platform->GetKeyMap();
int* keymap = Input_GetKeyMap();
if (keymap != NULL) {
for (int i = 0; i < 123; i++) {
gScan_code[i][0] = keymap[i];
@ -98,15 +99,13 @@ void Harness_Init(int* argc, char* argv[]) {
}
}
void Harness_Debug_PrintStack() {
#ifndef _WIN32
posix_print_stack_trace();
#endif
// used by unit tests
void Harness_ForceNullRenderer() {
force_nullrenderer = 1;
renderer = &null_renderer;
}
int Harness_ProcessCommandLine(int* argc, char* argv[]) {
char* platform_name = NULL;
for (int i = 1; i < *argc; i++) {
int handled = 0;
@ -118,10 +117,6 @@ int Harness_ProcessCommandLine(int* argc, char* argv[]) {
harness_debug_level = atoi(s + 1);
LOG_INFO("debug level set to %d", harness_debug_level);
handled = 1;
} else if (strstr(argv[i], "--platform=") != NULL) {
platform_name = strstr(argv[i], "=") + 1;
LOG_INFO("Platform set to: %s", platform_name);
handled = 1;
} else if (strstr(argv[i], "--physics-step-time=") != NULL) {
char* s = strstr(argv[i], "=");
harness_game_config.physics_step_time = atof(s + 1);
@ -152,39 +147,31 @@ int Harness_ProcessCommandLine(int* argc, char* argv[]) {
}
}
if (platform_name == NULL) {
platform_name = "sdl_gl";
}
if (strcmp(platform_name, "sdl_gl") == 0) {
platform = &sdl_gl_platform;
} else if (strcmp(platform_name, "null") == 0) {
platform = &null_platform;
} else {
LOG_PANIC("Invalid platform: %s", platform_name);
}
return 0;
}
void Harness_Hook_DOSGfxBegin() {
platform->NewWindow("Dethrace", 640, 400, 320, 200);
if (force_nullrenderer) {
return;
}
renderer = Window_Create("Dethrace", 640, 400, 320, 200);
}
// Render 2d back buffer
void Harness_RenderScreen(br_pixelmap* dst, br_pixelmap* src) {
platform->RenderFullScreenQuad((uint8_t*)src->pixels, 320, 200);
renderer->FullScreenQuad((uint8_t*)src->pixels, 320, 200);
last_dst = dst;
last_src = src;
}
void Harness_Hook_BrDevPaletteSetOld(br_pixelmap* pm) {
platform->SetPalette((uint8_t*)pm->pixels);
renderer->SetPalette((uint8_t*)pm->pixels);
palette = pm;
if (last_dst) {
Harness_RenderScreen(last_dst, last_src);
platform->Swap();
Window_Swap(0);
}
}
@ -200,74 +187,78 @@ void Harness_Hook_BrV1dbRendererBegin(br_v1db_state* v1db) {
v1db->renderer = (br_renderer*)renderer_state;
}
void Harness_Hook_MainGameLoop() {
int Harness_CalculateFrameDelay() {
if (harness_game_config.fps == 0) {
return;
return 0;
}
if (last_frame_time) {
unsigned int frame_time = GetTotalTime() - last_frame_time;
unsigned int now = GetTotalTime();
if (last_frame_time != 0) {
unsigned int frame_time = now - last_frame_time;
last_frame_time = now;
if (frame_time < 100) {
int sleep_time = (1000 / harness_game_config.fps) - frame_time;
if (sleep_time > 5) {
SDL_Delay(sleep_time);
return sleep_time;
}
}
}
last_frame_time = GetTotalTime();
return 0;
}
// Begin 3d scene
void Harness_Hook_BrZbSceneRenderBegin(br_actor* world, br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* depth_buffer) {
last_colour_buffer = colour_buffer;
last_depth_buffer = depth_buffer;
platform->BeginScene(camera, colour_buffer);
renderer->BeginScene(camera, colour_buffer);
}
void Harness_Hook_BrZbSceneRenderAdd(br_actor* tree) {
}
void Harness_Hook_renderFaces(br_actor* actor, br_model* model, br_material* material, br_token type) {
platform->RenderModel(actor, model, renderer_state->state.matrix.model_to_view);
renderer->Model(actor, model, renderer_state->state.matrix.model_to_view);
}
void Harness_Hook_BrZbSceneRenderEnd() {
platform->EndScene();
renderer->EndScene();
}
// Called by game to swap buffers at end of frame rendering
void Harness_Hook_BrPixelmapDoubleBuffer(br_pixelmap* dst, br_pixelmap* src) {
// draw the current colour_buffer (2d screen) contents
platform->FlushBuffers(last_colour_buffer, last_depth_buffer);
renderer->FlushBuffers(last_colour_buffer, last_depth_buffer);
Harness_RenderScreen(dst, src);
platform->Swap();
platform->PollEvents();
int delay_ms = Harness_CalculateFrameDelay();
Window_Swap(delay_ms);
renderer->ClearBuffers();
Window_PollEvents();
last_frame_time = GetTotalTime();
}
int Harness_Hook_KeyDown(unsigned char pScan_code) {
return platform->IsKeyDown(pScan_code);
return Input_IsKeyDown(pScan_code);
}
void Harness_Hook_PDServiceSystem() {
platform->PollEvents();
Window_PollEvents();
}
void Harness_Hook_PDSetKeyArray() {
platform->PollEvents();
Window_PollEvents();
}
void Harness_Hook_BrMaterialUpdate(br_material* mat, br_uint_16 flags) {
// LOG_DEBUG("buffermat %s", mat->identifier);
platform->BufferMaterial(mat);
renderer->BufferMaterial(mat);
}
void Harness_Hook_BrBufferUpdate(br_pixelmap* pm, br_token use, br_uint_16 flags) {
if (use == BRT_COLOUR_MAP_O || use == BRT_UNKNOWN) {
platform->BufferTexture(pm);
renderer->BufferTexture(pm);
} else {
LOG_PANIC("use %d", use);
}
@ -281,6 +272,5 @@ void Harness_Hook_S3StopAllOutletSounds() {
}
void Harness_Hook_FlushRenderer() {
platform->FlushBuffers(last_colour_buffer, last_depth_buffer);
renderer->FlushBuffers(last_colour_buffer, last_depth_buffer);
}

View File

@ -4,23 +4,7 @@
#include "brender/br_types.h"
#include "harness/trace.h"
typedef struct tPlatform {
void (*Init)();
void (*NewWindow)(char* title, int width, int height, int render_width, int render_height);
void (*PollEvents)();
int* (*GetKeyMap)();
int (*IsKeyDown)(unsigned char pScan_code);
void (*BeginScene)(br_actor* camera, br_pixelmap* colour_buffer);
void (*EndScene)();
void (*SetPalette)(uint8_t* palette);
void (*RenderFullScreenQuad)(uint8_t* src, int width, int height);
void (*RenderModel)(br_actor* actor, br_model* model, br_matrix34 model_matrix);
void (*Swap)();
void (*BufferTexture)(br_pixelmap* pm);
void (*BufferMaterial)(br_material* mat);
void (*FlushBuffers)(br_pixelmap* color_buffer, br_pixelmap* depth_buffer);
} tPlatform;
void Harness_ForceNullRenderer();
typedef struct tCamera {
void (*update)();
@ -29,4 +13,6 @@ typedef struct tCamera {
void (*setPosition)();
} tCamera;
#endif

View File

@ -1,6 +1,5 @@
#include "harness/trace.h"
#include <dirent.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>

View File

@ -3,14 +3,15 @@
#include "brender/br_types.h"
void Harness_Init(int* argc, char* argv[]);
// Hooks are called from original game code.
// Dethrace hooks
void Harness_Init(int* argc, char* argv[]);
int Harness_Hook_KeyDown(unsigned char pScan_code);
void Harness_Hook_PDServiceSystem();
void Harness_Hook_PDSetKeyArray();
void Harness_Hook_MainGameLoop(); // limit FPS
// void Harness_Hook_MainGameLoop(); // limit FPS
void Harness_Hook_FlushRenderer(); // synchronize in-memory framebuffer and depthbuffer
// BRender hooks

View File

@ -0,0 +1,46 @@
#ifndef HARNESS_OS_H
#define HARNESS_OS_H
#include <stdint.h>
#if defined(_WIN32) || defined(_WIN64)
#include <direct.h>
#include <io.h>
#define getcwd _getcwd
#define chdir _chdir
#define access _access
#define F_OK 0
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#if _MSC_VER < 1900
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
#else
#include <unistd.h>
#endif
// Required: return timestamp in milliseconds.
uint32_t OS_GetTime(void);
// Required: sleep for specified milliseconds
void OS_Sleep(int ms);
// Required: begin a directory iteration and return name of first file
char* OS_GetFirstFileInDirectory(char* path);
// Required: continue directory iteration. If no more files, return NULL
char* OS_GetNextFileInDirectory(void);
// Required: copy the `basename` component of `path` into `base`
void OS_Basename(char* path, char* base);
// Optional: return true if a debugger is detected
int OS_IsDebuggerPresent(void);
// Optional: install a handler to print stack trace during a crash
void OS_InstallSignalHandler(char* program_name);
#endif

View File

@ -3,16 +3,9 @@
#include "brender/br_types.h"
#include <stdlib.h>
#include <unistd.h>
#if defined _WIN32 && !defined sleep
#define sleep(x) _sleep(x)
#endif
extern int harness_debug_level;
extern int PlatformIsDebuggerPresent(void);
void Harness_Debug_PrintStack();
extern int OS_IsDebuggerPresent(void);
void debug_printf(const char* fmt, const char* fn, const char* fmt2, ...);
void debug_print_vector3(const char* fmt, const char* fn, char* msg, br_vector3* v);
@ -46,26 +39,26 @@ void debug_print_matrix4(const char* fmt, const char* fn, char* name, br_matrix4
#define LOG_MATRIX4(msg, m) debug_print_matrix4("\033[0;34m[DEBUG] %s ", __FUNCTION__, msg, m)
#define LOG_INFO(...) debug_printf("\033[0;34m[INFO] %s ", __FUNCTION__, __VA_ARGS__)
#define LOG_WARN(...) debug_printf("\033[0;33m[WARN] %s ", __FUNCTION__, __VA_ARGS__)
#define LOG_PANIC(...) \
do { \
debug_printf("\033[0;31m[PANIC] %s ", __FUNCTION__, __VA_ARGS__); \
if (PlatformIsDebuggerPresent()) \
abort(); \
else \
exit(1); \
#define LOG_PANIC(...) \
do { \
debug_printf("\033[0;31m[PANIC] %s ", __FUNCTION__, __VA_ARGS__); \
if (OS_IsDebuggerPresent()) \
abort(); \
else \
exit(1); \
} while (0)
#define LOG_WARN_ONCE(...) \
static int warn_printed = 0; \
if (!warn_printed) { \
debug_printf("\033[0;33m[WARN] %s ", __FUNCTION__, __VA_ARGS__); \
warn_printed = 1; \
#define LOG_WARN_ONCE(...) \
static int warn_printed = 0; \
if (!warn_printed) { \
debug_printf("\033[0;33m[WARN] %s ", __FUNCTION__, __VA_ARGS__); \
warn_printed = 1; \
}
#define NOT_IMPLEMENTED() \
#define NOT_IMPLEMENTED() \
LOG_PANIC("not implemented")
#define TELL_ME_IF_WE_PASS_THIS_WAY() \
#define TELL_ME_IF_WE_PASS_THIS_WAY() \
LOG_PANIC("code path not expected")
#define STUB() \
@ -78,6 +71,6 @@ void debug_print_matrix4(const char* fmt, const char* fn, char* name, br_matrix4
stub_printed = 1; \
}
//int count_open_fds();
// int count_open_fds();
#endif

View File

@ -0,0 +1,12 @@
#ifndef PLATFORM_H
#define PLATFORM_H
#include "../renderers/renderer.h"
tRenderer* Window_Create(char* title, int width, int height, int pRender_width, int pRender_height);
void Window_PollEvents(void);
void Window_Swap(int delay_ms_after_swap);
int* Input_GetKeyMap(void);
int Input_IsKeyDown(unsigned char scan_code);
#endif

View File

@ -1,5 +1,12 @@
#include "common.h"
#include <glad/glad.h>
// this needs to be included after glad.h
#include <SDL.h>
#include <SDL_opengl.h>
#include "../renderers/gl/gl_renderer.h"
#include "../renderers/renderer.h"
#include "harness/trace.h"
// Errol's keymap
int keymap[123] = {
@ -112,15 +119,69 @@ int keymap[123] = {
SDL_SCANCODE_SPACE
};
SDL_Window* window;
SDL_GLContext context;
uint8_t sdl_key_state[256];
void SDLPlatform_Init() {
if (SDL_Init(SDL_INIT_TIMER) != 0) {
LOG_PANIC("SDL_INIT_TIMER error: %s", SDL_GetError());
tRenderer gl_renderer = {
GLRenderer_Init,
GLRenderer_BeginScene,
GLRenderer_EndScene,
GLRenderer_SetPalette,
GLRenderer_FullScreenQuad,
GLRenderer_Model,
GLRenderer_ClearBuffers,
GLRenderer_BufferTexture,
GLRenderer_BufferMaterial,
GLRenderer_FlushBuffers
};
tRenderer* Window_Create(char* title, int width, int height, int pRender_width, int pRender_height) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError());
}
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) {
LOG_PANIC("Failed to set SDL_GL_CONTEXT_PROFILE_MASK attribute. %s", SDL_GetError());
};
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetSwapInterval(1);
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
width, height,
SDL_WINDOW_OPENGL);
if (!window) {
LOG_PANIC("Failed to create window");
}
// Don't grab the mouse when a debugger is present
if (!OS_IsDebuggerPresent()) {
SDL_SetRelativeMouseMode(SDL_TRUE);
}
// SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
context = SDL_GL_CreateContext(window);
if (!context) {
LOG_PANIC("Failed to call SDL_GL_CreateContext. %s", SDL_GetError());
}
// Load GL extensions using glad
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
LOG_PANIC("Failed to initialize the OpenGL context with GLAD.");
exit(1);
}
gl_renderer.Init(width, height, pRender_width, pRender_height);
return &gl_renderer;
}
void SDLPlatform_PollEvents() {
void Window_PollEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
@ -134,7 +195,7 @@ void SDLPlatform_PollEvents() {
}
if (event.key.type == SDL_KEYDOWN) {
sdl_key_state[event.key.keysym.scancode] = 1;
//LOG_DEBUG("key %d", key->keysym.scancode);
// LOG_DEBUG("key %d", key->keysym.scancode);
} else {
sdl_key_state[event.key.keysym.scancode] = 0;
}
@ -148,10 +209,18 @@ void SDLPlatform_PollEvents() {
}
}
int* SDLPlatform_GetKeyMap() {
void Window_Swap(int delay_ms_after_swap) {
SDL_GL_SwapWindow(window);
if (delay_ms_after_swap != 0) {
SDL_Delay(delay_ms_after_swap);
}
}
int* Input_GetKeyMap() {
return (int*)keymap;
}
int SDLPlatform_IsKeyDown(unsigned char scan_code) {
int Input_IsKeyDown(unsigned char scan_code) {
return sdl_key_state[scan_code];
}

View File

@ -1,7 +1,14 @@
// Based on https://gist.github.com/jvranish/4441299
#include "harness/os.h"
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
@ -9,158 +16,107 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifdef _WIN32
#include <windows.h>
#include <imagehlp.h>
#ifdef _WIN64
#define Esp Rsp
#define Eip Rip
#define Ebp Rbp
#endif
#else
#include <err.h>
#include <execinfo.h>
#endif
static int stack_nbr = 0;
static char _program_name[1024];
#define MAX_STACK_FRAMES 64
static void* stack_traces[MAX_STACK_FRAMES];
#define TRACER_PID_STRING "TracerPid:"
DIR* directory_iterator;
/* Resolve symbol name and source location given the path to the executable
and an address */
uint32_t OS_GetTime() {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return spec.tv_sec * 1000 + spec.tv_nsec / 1000000;
}
void OS_Sleep(int delay_ms) {
struct timespec ts;
ts.tv_sec = delay_ms / 1000;
ts.tv_nsec = (delay_ms % 1000) * 1000000;
nanosleep(&ts, &ts);
}
char* OS_GetFirstFileInDirectory(char* path) {
directory_iterator = opendir(path);
if (directory_iterator == NULL) {
return NULL;
}
return OS_GetNextFileInDirectory();
}
char* OS_GetNextFileInDirectory(void) {
struct dirent* entry;
if (directory_iterator == NULL) {
return NULL;
}
while ((entry = readdir(directory_iterator)) != NULL) {
if (entry->d_type == DT_REG) {
return entry->d_name;
}
}
closedir(directory_iterator);
directory_iterator = NULL;
return NULL;
}
void OS_Basename(char* path, char* base) {
strcpy(base, basename(path));
}
int OS_IsDebuggerPresent() {
char buf[4096];
int status_fd;
ssize_t num_read;
char* tracer_pid_ptr;
char* char_ptr;
status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1) {
return 0;
}
num_read = read(status_fd, buf, sizeof(buf) - 1);
close(status_fd);
if (num_read <= 0) {
return 0;
}
buf[num_read] = '\0';
tracer_pid_ptr = strstr(buf, TRACER_PID_STRING);
if (tracer_pid_ptr == NULL) {
return 0;
}
for (char_ptr = tracer_pid_ptr + sizeof(TRACER_PID_STRING) - 1; char_ptr <= buf + num_read; ++char_ptr) {
if (isspace(*char_ptr)) {
continue;
} else {
return isdigit(*char_ptr) != 0 && *char_ptr != '0';
}
}
return 0;
}
// Resolve symbol name and source location given the path to the executable and an address
int addr2line(char const* const program_name, void const* const addr) {
char addr2line_cmd[512] = { 0 };
/* have addr2line map the address to the relent line in the code */
#ifdef __APPLE__
/* apple does things differently... */
sprintf(addr2line_cmd, "atos -o %.256s %p", program_name, addr);
#else
/* have addr2line map the address to the related line in the code */
sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
#endif
fprintf(stderr, "%d: ", stack_nbr++);
return system(addr2line_cmd);
}
#ifdef _WIN32
void windows_print_stacktrace(CONTEXT* context) {
SymInitialize(GetCurrentProcess(), 0, true);
STACKFRAME frame = { 0 };
/* setup initial stack frame */
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
while (StackWalk(IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
addr2line(_program_name, (void*)frame.AddrPC.Offset);
}
SymCleanup(GetCurrentProcess());
}
LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
fputs("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
break;
case EXCEPTION_BREAKPOINT:
fputs("Error: EXCEPTION_BREAKPOINT\n", stderr);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
fputs("Error: EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
fputs("Error: EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
fputs("Error: EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
fputs("Error: EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
break;
case EXCEPTION_FLT_OVERFLOW:
fputs("Error: EXCEPTION_FLT_OVERFLOW\n", stderr);
break;
case EXCEPTION_FLT_STACK_CHECK:
fputs("Error: EXCEPTION_FLT_STACK_CHECK\n", stderr);
break;
case EXCEPTION_FLT_UNDERFLOW:
fputs("Error: EXCEPTION_FLT_UNDERFLOW\n", stderr);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
fputs("Error: EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
break;
case EXCEPTION_IN_PAGE_ERROR:
fputs("Error: EXCEPTION_IN_PAGE_ERROR\n", stderr);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_INT_OVERFLOW:
fputs("Error: EXCEPTION_INT_OVERFLOW\n", stderr);
break;
case EXCEPTION_INVALID_DISPOSITION:
fputs("Error: EXCEPTION_INVALID_DISPOSITION\n", stderr);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
fputs("Error: EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
break;
case EXCEPTION_PRIV_INSTRUCTION:
fputs("Error: EXCEPTION_PRIV_INSTRUCTION\n", stderr);
break;
case EXCEPTION_SINGLE_STEP:
fputs("Error: EXCEPTION_SINGLE_STEP\n", stderr);
break;
case EXCEPTION_STACK_OVERFLOW:
fputs("Error: EXCEPTION_STACK_OVERFLOW\n", stderr);
break;
default:
fputs("Error: Unrecognized Exception\n", stderr);
break;
}
fflush(stderr);
/* If this is a stack overflow then we can't walk the stack, so just show
where the error happened */
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
windows_print_stacktrace(ExceptionInfo->ContextRecord);
} else {
addr2line(_program_name, (void*)ExceptionInfo->ContextRecord->Eip);
}
return EXCEPTION_EXECUTE_HANDLER;
}
void install_signal_handler(char* program_name) {
strcpy(_program_name, program_name);
SetUnhandledExceptionFilter(windows_exception_handler);
}
#else
#define MAX_STACK_FRAMES 64
static void* stack_traces[MAX_STACK_FRAMES];
void posix_print_stack_trace() {
void print_stack_trace() {
int i, trace_size = 0;
char** messages = (char**)NULL;
@ -181,7 +137,7 @@ void posix_print_stack_trace() {
}
}
void posix_signal_handler(int sig, siginfo_t* siginfo, void* context) {
void signal_handler(int sig, siginfo_t* siginfo, void* context) {
(void)context;
fputs("\n******************\n", stderr);
@ -264,7 +220,7 @@ void posix_signal_handler(int sig, siginfo_t* siginfo, void* context) {
break;
}
fputs("******************\n", stderr);
posix_print_stack_trace();
print_stack_trace();
exit(1);
}
@ -283,7 +239,7 @@ void resolve_full_path(char* path, const char* argv0) {
}
}
void install_signal_handler(char* program_name) {
void OS_InstallSignalHandler(char* program_name) {
resolve_full_path(_program_name, program_name);
@ -304,16 +260,10 @@ void install_signal_handler(char* program_name) {
/* register our signal handlers */
{
struct sigaction sig_action = {};
sig_action.sa_sigaction = posix_signal_handler;
sig_action.sa_sigaction = signal_handler;
sigemptyset(&sig_action.sa_mask);
#ifdef __APPLE__
/* for some reason we backtrace() doesn't work on osx
when we use an alternate stack */
sig_action.sa_flags = SA_SIGINFO;
#else
sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
#endif
if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
err(1, "sigaction");
@ -335,4 +285,3 @@ void install_signal_handler(char* program_name) {
}
}
}
#endif

286
src/harness/os/macos.c Normal file
View File

@ -0,0 +1,286 @@
// Based on https://gist.github.com/jvranish/4441299
#include "harness/os.h"
#include <assert.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <execinfo.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static int stack_nbr = 0;
static char _program_name[1024];
#define MAX_STACK_FRAMES 64
static void* stack_traces[MAX_STACK_FRAMES];
DIR* directory_iterator;
uint32_t OS_GetTime() {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return spec.tv_sec * 1000 + spec.tv_nsec / 1000000;
}
void OS_Sleep(int delay_ms) {
struct timespec ts;
ts.tv_sec = delay_ms / 1000;
ts.tv_nsec = (delay_ms % 1000) * 1000000;
nanosleep(&ts, &ts);
}
char* OS_GetFirstFileInDirectory(char* path) {
directory_iterator = opendir(path);
if (directory_iterator == NULL) {
return NULL;
}
return OS_GetNextFileInDirectory();
}
char* OS_GetNextFileInDirectory(void) {
struct dirent* entry;
if (directory_iterator == NULL) {
return NULL;
}
while ((entry = readdir(directory_iterator)) != NULL) {
if (entry->d_type == DT_REG) {
return entry->d_name;
}
}
closedir(directory_iterator);
directory_iterator = NULL;
return NULL;
}
void OS_Basename(char* path, char* base) {
strcpy(base, basename(path));
}
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
// FIXME:
// Important: Because the definition of the kinfo_proc structure (in <sys/sysctl.h>) is conditionalized by __APPLE_API_UNSTABLE,
// you should restrict use of the above code to the debug build of your program.
int OS_IsDebuggerPresent()
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
{
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
// Resolve symbol name and source location given the path to the executable and an address
int addr2line(char const* const program_name, void const* const addr) {
char addr2line_cmd[512] = { 0 };
/* have addr2line map the address to the related line in the code */
sprintf(addr2line_cmd, "atos -o %.256s %p", program_name, addr);
fprintf(stderr, "%d: ", stack_nbr++);
return system(addr2line_cmd);
}
void print_stack_trace() {
int i, trace_size = 0;
char** messages = (char**)NULL;
fputs("\nStack trace:\n", stderr);
trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
messages = backtrace_symbols(stack_traces, trace_size);
/* skip the first couple stack frames (as they are this function and
our handler) and also skip the last frame as it's (always?) junk. */
for (i = 3; i < (trace_size - 1); ++i) {
if (addr2line(_program_name, stack_traces[i]) != 0) {
printf(" error determining line # for: %s\n", messages[i]);
}
}
if (messages) {
free(messages);
}
}
void signal_handler(int sig, siginfo_t* siginfo, void* context) {
(void)context;
fputs("\n******************\n", stderr);
switch (sig) {
case SIGSEGV:
fputs("Caught SIGSEGV\n", stderr);
break;
case SIGINT:
fputs("Caught SIGINT\n", stderr);
break;
case SIGFPE:
switch (siginfo->si_code) {
case FPE_INTDIV:
fputs("Caught SIGFPE: FPE_INTDIV\n", stderr);
break;
case FPE_INTOVF:
fputs("Caught SIGFPE: FPE_INTOVF\n", stderr);
break;
case FPE_FLTDIV:
fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr);
break;
case FPE_FLTOVF:
fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr);
break;
case FPE_FLTUND:
fputs("Caught SIGFPE: FPE_FLTUND\n", stderr);
break;
case FPE_FLTRES:
fputs("Caught SIGFPE: FPE_FLTRES\n", stderr);
break;
case FPE_FLTINV:
fputs("Caught SIGFPE: FPE_FLTINV\n", stderr);
break;
case FPE_FLTSUB:
fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr);
break;
default:
fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
break;
}
break;
case SIGILL:
switch (siginfo->si_code) {
case ILL_ILLOPC:
fputs("Caught SIGILL: ILL_ILLOPC\n", stderr);
break;
case ILL_ILLOPN:
fputs("Caught SIGILL: ILL_ILLOPN\n", stderr);
break;
case ILL_ILLADR:
fputs("Caught SIGILL: ILL_ILLADR\n", stderr);
break;
case ILL_ILLTRP:
fputs("Caught SIGILL: ILL_ILLTRP\n", stderr);
break;
case ILL_PRVOPC:
fputs("Caught SIGILL: ILL_PRVOPC\n", stderr);
break;
case ILL_PRVREG:
fputs("Caught SIGILL: ILL_PRVREG\n", stderr);
break;
case ILL_COPROC:
fputs("Caught SIGILL: ILL_COPROC\n", stderr);
break;
case ILL_BADSTK:
fputs("Caught SIGILL: ILL_BADSTK\n", stderr);
break;
default:
fputs("Caught SIGILL: Illegal Instruction\n", stderr);
break;
}
break;
case SIGTERM:
fputs("Caught SIGTERM\n", stderr);
break;
case SIGABRT:
fputs("Caught SIGABRT\n", stderr);
break;
default:
break;
}
fputs("******************\n", stderr);
print_stack_trace();
exit(1);
}
static uint8_t alternate_stack[SIGSTKSZ];
void resolve_full_path(char* path, const char* argv0) {
if (argv0[0] == '/') { // run with absolute path
strcpy(path, argv0);
} else { // run with relative path
if (NULL == getcwd(path, PATH_MAX)) {
perror("getcwd error");
return;
}
strcat(path, "/");
strcat(path, argv0);
}
}
void OS_InstallSignalHandler(char* program_name) {
resolve_full_path(_program_name, program_name);
/* setup alternate stack */
{
stack_t ss = {};
/* malloc is usually used here, I'm not 100% sure my static allocation
is valid but it seems to work just fine. */
ss.ss_sp = (void*)alternate_stack;
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) != 0) {
err(1, "sigaltstack");
}
}
/* register our signal handlers */
{
struct sigaction sig_action = {};
sig_action.sa_sigaction = signal_handler;
sigemptyset(&sig_action.sa_mask);
sig_action.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGINT, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGILL, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
}
}

206
src/harness/os/windows.c Normal file
View File

@ -0,0 +1,206 @@
// Based on https://gist.github.com/jvranish/4441299
// this has to be first
#include <windows.h>
#include <imagehlp.h>
#include "harness/os.h"
#include <assert.h>
#include <direct.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN64
#define Esp Rsp
#define Eip Rip
#define Ebp Rbp
#endif
static int stack_nbr = 0;
static char _program_name[1024];
LARGE_INTEGER qpc_start_time, EndingTime, ElapsedMicroseconds;
LARGE_INTEGER qpc_ticks_per_sec;
HANDLE directory_handle = NULL;
char last_found_file[260];
uint32_t OS_GetTime() {
LARGE_INTEGER now;
if (qpc_start_time.QuadPart == 0) {
QueryPerformanceFrequency(&qpc_ticks_per_sec);
QueryPerformanceCounter(&qpc_start_time);
}
QueryPerformanceCounter(&now);
return (uint32_t)(((now.QuadPart - qpc_start_time.QuadPart) * 1000) / qpc_ticks_per_sec.QuadPart);
}
void OS_Sleep(int delay_ms) {
Sleep(delay_ms);
}
char* OS_GetFirstFileInDirectory(char* path) {
char with_extension[256];
WIN32_FIND_DATA find_data;
HANDLE hFind = NULL;
strcpy(with_extension, path);
strcat(with_extension, "\\*.???");
directory_handle = FindFirstFile(with_extension, &find_data);
if (directory_handle == INVALID_HANDLE_VALUE) {
return NULL;
}
strcpy(last_found_file, find_data.cFileName);
return last_found_file;
}
// Required: continue directory iteration. If no more files, return NULL
char* OS_GetNextFileInDirectory(void) {
WIN32_FIND_DATA find_data;
if (directory_handle == NULL) {
return NULL;
}
while (FindNextFile(directory_handle, &find_data)) {
strcpy(last_found_file, find_data.cFileName);
return last_found_file;
}
FindClose(directory_handle);
return NULL;
}
void OS_Basename(char* path, char* base) {
_splitpath(path, NULL, NULL, base, NULL);
}
int OS_IsDebuggerPresent() {
return IsDebuggerPresent();
}
int addr2line(char const* const program_name, void const* const addr) {
char addr2line_cmd[512] = { 0 };
sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
fprintf(stderr, "%d: ", stack_nbr++);
return system(addr2line_cmd);
}
void print_stacktrace(CONTEXT* context) {
SymInitialize(GetCurrentProcess(), 0, true);
STACKFRAME frame = { 0 };
/* setup initial stack frame */
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
while (StackWalk(IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
addr2line(_program_name, (void*)frame.AddrPC.Offset);
}
SymCleanup(GetCurrentProcess());
}
LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
fputs("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
break;
case EXCEPTION_BREAKPOINT:
fputs("Error: EXCEPTION_BREAKPOINT\n", stderr);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
fputs("Error: EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
fputs("Error: EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
fputs("Error: EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
fputs("Error: EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
break;
case EXCEPTION_FLT_OVERFLOW:
fputs("Error: EXCEPTION_FLT_OVERFLOW\n", stderr);
break;
case EXCEPTION_FLT_STACK_CHECK:
fputs("Error: EXCEPTION_FLT_STACK_CHECK\n", stderr);
break;
case EXCEPTION_FLT_UNDERFLOW:
fputs("Error: EXCEPTION_FLT_UNDERFLOW\n", stderr);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
fputs("Error: EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
break;
case EXCEPTION_IN_PAGE_ERROR:
fputs("Error: EXCEPTION_IN_PAGE_ERROR\n", stderr);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_INT_OVERFLOW:
fputs("Error: EXCEPTION_INT_OVERFLOW\n", stderr);
break;
case EXCEPTION_INVALID_DISPOSITION:
fputs("Error: EXCEPTION_INVALID_DISPOSITION\n", stderr);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
fputs("Error: EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
break;
case EXCEPTION_PRIV_INSTRUCTION:
fputs("Error: EXCEPTION_PRIV_INSTRUCTION\n", stderr);
break;
case EXCEPTION_SINGLE_STEP:
fputs("Error: EXCEPTION_SINGLE_STEP\n", stderr);
break;
case EXCEPTION_STACK_OVERFLOW:
fputs("Error: EXCEPTION_STACK_OVERFLOW\n", stderr);
break;
default:
fputs("Error: Unrecognized Exception\n", stderr);
break;
}
fflush(stderr);
/* If this is a stack overflow then we can't walk the stack, so just show
where the error happened */
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
print_stacktrace(ExceptionInfo->ContextRecord);
} else {
addr2line(_program_name, (void*)ExceptionInfo->ContextRecord->Eip);
}
return EXCEPTION_EXECUTE_HANDLER;
}
void OS_InstallSignalHandler(char* program_name) {
strcpy(_program_name, program_name);
SetUnhandledExceptionFilter(windows_exception_handler);
}

View File

@ -1,6 +0,0 @@
#ifndef HARNESS_PLATFORM_H
#define HARNESS_PLATFORM_H
int PlatformIsDebuggerPresent(void);
#endif

View File

@ -1,45 +0,0 @@
#include "platforms/platform.h"
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#define TRACER_PID_STRING "TracerPid:"
int PlatformIsDebuggerPresent() {
char buf[4096];
int status_fd;
ssize_t num_read;
char* tracer_pid_ptr;
char* char_ptr;
status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1) {
return 0;
}
num_read = read(status_fd, buf, sizeof(buf) - 1);
close(status_fd);
if (num_read <= 0) {
return 0;
}
buf[num_read] = '\0';
tracer_pid_ptr = strstr(buf, TRACER_PID_STRING);
if (tracer_pid_ptr == NULL) {
return 0;
}
for (char_ptr = tracer_pid_ptr + sizeof(TRACER_PID_STRING) - 1; char_ptr <= buf + num_read; ++char_ptr)
{
if (isspace(*char_ptr)) {
continue;
} else {
return isdigit(*char_ptr) != 0 && *char_ptr != '0';
}
}
return 0;
}

View File

@ -1,44 +0,0 @@
#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
// FIXME:
// Important: Because the definition of the kinfo_proc structure (in <sys/sysctl.h>) is conditionalized by __APPLE_API_UNSTABLE,
// you should restrict use of the above code to the debug build of your program.
int PlatformIsDebuggerPresent()
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
{
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}

View File

@ -1,5 +0,0 @@
#include <windows.h>
int PlatformIsDebuggerPresent() {
return IsDebuggerPresent();
}

View File

@ -1,25 +0,0 @@
#ifndef SDL_GL_PLATFORM_H
#define SDL_GL_PLATFORM_H
#include "harness.h"
#include "sdl/common.h"
#include "sdl/gl_renderer.h"
tPlatform sdl_gl_platform = {
SDLPlatform_Init,
GLRenderer_CreateWindow,
SDLPlatform_PollEvents,
SDLPlatform_GetKeyMap,
SDLPlatform_IsKeyDown,
GLRenderer_BeginScene,
GLRenderer_EndScene,
GLRenderer_SetPalette,
GLRenderer_RenderFullScreenQuad,
GLRenderer_RenderModel,
GLRenderer_Swap,
GLRenderer_BufferTexture,
GLRenderer_BufferMaterial,
GLRenderer_FlushBuffers
};
#endif

View File

@ -1,20 +1,15 @@
#include "gl_renderer.h"
#include "brender/brender.h"
#include "cameras/debug_camera.h"
#include "gl_brender_stored_context.h"
#include "gl_renderer_shaders.h"
#include "harness.h"
#include "harness/trace.h"
#include "platforms/platform.h"
#include "shaders.h"
#include "stored_context.h"
#include <cglm/cglm.h>
#include <glad/glad.h>
#include <string.h>
// this needs to be included after glad.h
#include <SDL_opengl.h>
SDL_Window* window;
SDL_GLContext context;
GLuint screen_buffer_vao, screen_buffer_ebo;
GLuint screen_texture, palette_texture, depth_texture;
@ -178,18 +173,12 @@ void SetupFullScreenRectGeometry() {
glBindVertexArray(0);
}
void InitializeOpenGLContext() {
void GLRenderer_Init(int width, int height, int pRender_width, int pRender_height) {
context = SDL_GL_CreateContext(window);
if (!context) {
LOG_PANIC("Failed to call SDL_GL_CreateContext. %s", SDL_GetError());
}
// Load GL extensions using glad
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
LOG_PANIC("Failed to initialize the OpenGL context with GLAD.");
exit(1);
}
window_width = width;
window_height = height;
render_width = pRender_width;
render_height = pRender_height;
int maxTextureImageUnits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
@ -235,48 +224,10 @@ void InitializeOpenGLContext() {
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
CHECK_GL_ERROR("initializeOpenGLContext");
}
void GLRenderer_CreateWindow(char* title, int width, int height, int pRender_width, int pRender_height) {
window_width = width;
window_height = height;
render_width = pRender_width;
render_height = pRender_height;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError());
}
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) {
LOG_PANIC("Failed to set SDL_GL_CONTEXT_PROFILE_MASK attribute. %s", SDL_GetError());
};
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetSwapInterval(1);
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
width, height,
SDL_WINDOW_OPENGL);
if (!window) {
LOG_PANIC("Failed to create window");
}
// Don't grab the mouse when a debugger is present
if (!PlatformIsDebuggerPresent()) {
SDL_SetRelativeMouseMode(SDL_TRUE);
}
// SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
InitializeOpenGLContext();
screen_buffer_flip_pixels = malloc(sizeof(uint8_t) * render_width * render_height);
depth_buffer_flip_pixels = malloc(sizeof(uint16_t) * render_width * render_height);
CHECK_GL_ERROR("initializeOpenGLContext");
}
void GLRenderer_SetPalette(uint8_t* rgba_colors) {
@ -374,7 +325,7 @@ void GLRenderer_EndScene() {
CHECK_GL_ERROR("GLRenderer_RenderFullScreenQuad");
}
void GLRenderer_RenderFullScreenQuad(uint8_t* screen_buffer, int width, int height) {
void GLRenderer_FullScreenQuad(uint8_t* screen_buffer, int width, int height) {
glViewport(0, 0, window_width, window_height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST);
@ -392,8 +343,7 @@ void GLRenderer_RenderFullScreenQuad(uint8_t* screen_buffer, int width, int heig
CHECK_GL_ERROR("GLRenderer_RenderFullScreenQuad");
}
void GLRenderer_Swap() {
SDL_GL_SwapWindow(window);
void GLRenderer_ClearBuffers() {
// clear our virtual framebuffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
@ -407,6 +357,8 @@ void build_model(br_model* model) {
tStored_model_context* ctx;
v11model* v11;
LOG_DEBUG("called %s", model->identifier);
v11 = model->prepared;
ctx = NewStoredModelContext();
@ -469,13 +421,12 @@ void build_model(br_model* model) {
}
glGenVertexArrays(1, &ctx->vao_id);
GLuint vbo_id;
glGenBuffers(1, &vbo_id);
glGenBuffers(1, &ctx->vbo_id);
glGenBuffers(1, &ctx->ebo_id);
// Vertices }
// Vertices
glBindVertexArray(ctx->vao_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, ctx->vbo_id);
glBufferData(GL_ARRAY_BUFFER, sizeof(fmt_vertex) * total_verts, verts, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(fmt_vertex), (void*)offsetof(fmt_vertex, p));
glEnableVertexAttribArray(0);
@ -491,6 +442,7 @@ void build_model(br_model* model) {
free(verts);
free(indices);
model->stored = ctx;
CHECK_GL_ERROR("after build model");
@ -519,7 +471,7 @@ void setActiveMaterial(tStored_material* material) {
}
}
void GLRenderer_RenderModel(br_actor* actor, br_model* model, br_matrix34 model_matrix) {
void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix) {
tStored_model_context* ctx;
ctx = model->stored;
v11model* v11 = model->prepared;
@ -533,6 +485,7 @@ void GLRenderer_RenderModel(br_actor* actor, br_model* model, br_matrix34 model_
if (ctx == NULL) {
build_model(model);
ctx = model->stored;
// DebugCamera_SetPosition(model_matrix.m[3][0], model_matrix.m[3][1], model_matrix.m[3][2]);
}
CHECK_GL_ERROR("rm1");

View File

@ -2,7 +2,6 @@
#define HARNESS_GL_RENDERER
#include "harness.h"
#include <SDL.h>
#define CHECK_GL_ERROR(msg) \
{ \
@ -12,15 +11,15 @@
} \
}
void GLRenderer_CreateWindow(char* title, int width, int height, int render_width, int render_height);
void GLRenderer_Init(int width, int height, int render_width, int render_height);
void GLRenderer_SetPalette(uint8_t* rgba_colors);
void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer);
void GLRenderer_EndScene();
void GLRenderer_RenderFullScreenQuad(uint8_t* screen_buffer, int width, int height);
void GLRenderer_Swap();
void GLRenderer_RenderModel(br_actor* actor, br_model* model, br_matrix34 model_matrix);
void GLRenderer_FullScreenQuad(uint8_t* screen_buffer, int width, int height);
void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix);
void GLRenderer_BufferTexture(br_pixelmap* pm);
void GLRenderer_BufferMaterial(br_material* mat);
void GLRenderer_ClearBuffers();
void GLRenderer_FlushBuffers(br_pixelmap* color_buffer, br_pixelmap* depth_buffer);
#endif

View File

@ -1,10 +1,11 @@
#include "gl_brender_stored_context.h"
#include "stored_context.h"
#include "../include/harness/trace.h"
#include <stdlib.h>
void _free(br_object* o) {
tStored_model_context* ctx = (tStored_model_context*)o;
glDeleteVertexArrays(1, &ctx->vao_id);
glDeleteBuffers(1, &ctx->vbo_id);
glDeleteBuffers(1, &ctx->ebo_id);
free(o);
}

View File

@ -6,7 +6,7 @@
typedef struct tStored_model_context {
br_object_dispatch* dispatch;
GLuint vao_id, ebo_id;
GLuint vao_id, vbo_id, ebo_id;
} tStored_model_context;
typedef struct tStored_pixelmap {

View File

@ -1,33 +1,25 @@
#include "harness.h"
#include "renderer.h"
void Null_Init() {}
void Null_CreateWindow(char* title, int width, int height, int render_width, int render_height) {}
void Null_PollEvents() {}
int* Null_GetKeyMap() { return NULL; }
int Null_IsKeyDown(unsigned char pScan_code) { return 0; }
void Null_BeginFrame(br_actor* camera, br_pixelmap* colour_buffer) {}
void Null_EndFrame() {}
void Null_SetPalette(uint8_t* palette) {}
void Null_RenderFullScreenQuad(uint8_t* src, int width, int height) {}
void Null_RenderModel(br_actor* actor, br_model* model, br_matrix34 model_matrix) {}
void Null_RenderFrameBuffer() {}
void Null_Swap() {}
void Null_ClearBuffers() {}
void Null_BufferTexture(br_pixelmap* pm) {}
void Null_BufferMaterial(br_material* mat) {}
void Null_FlushBuffers(br_pixelmap* color_buffer, br_pixelmap* depth_buffer) {}
tPlatform null_platform = {
tRenderer null_renderer = {
Null_Init,
Null_CreateWindow,
Null_PollEvents,
Null_GetKeyMap,
Null_IsKeyDown,
Null_BeginFrame,
Null_EndFrame,
Null_SetPalette,
Null_RenderFullScreenQuad,
Null_RenderModel,
Null_Swap,
Null_ClearBuffers,
Null_BufferTexture,
Null_BufferMaterial,
Null_FlushBuffers

View File

@ -0,0 +1,20 @@
#ifndef HARNESS_RENDERER_H
#define HARNESS_RENDERER_H
#include "brender/br_types.h"
typedef struct tRenderer {
void (*Init)(int width, int height, int pRender_width, int pRender_height);
void (*BeginScene)(br_actor* camera, br_pixelmap* colour_buffer);
void (*EndScene)();
void (*SetPalette)(uint8_t* palette);
void (*FullScreenQuad)(uint8_t* src, int width, int height);
void (*Model)(br_actor* actor, br_model* model, br_matrix34 model_matrix);
void (*ClearBuffers)();
void (*BufferTexture)(br_pixelmap* pm);
void (*BufferMaterial)(br_material* mat);
void (*FlushBuffers)(br_pixelmap* color_buffer, br_pixelmap* depth_buffer);
} tRenderer;
#endif

View File

@ -1,11 +0,0 @@
#ifndef SDL_PLATFORM_H
#define SDL_PLATFORM_H
#include "harness.h"
void SDLPlatform_Init();
void SDLPlatform_PollEvents();
int* SDLPlatform_GetKeyMap();
int SDLPlatform_IsKeyDown(unsigned char scan_code);
#endif

View File

@ -1,6 +1,8 @@
add_executable(dethrace_test)
add_test(NAME test_dethrace COMMAND dethrace_test)
set(IO_PLATFORM "SDL_OpenGL")
target_link_libraries(dethrace_test PRIVATE glad dethrace_obj)
target_include_directories(dethrace_test PRIVATE

View File

@ -1,7 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tests.h"
@ -24,7 +23,7 @@ void test_controls_CheckKevKeys() {
gKeys_pressed = 0;
result = KevKeyService();
}
sleep_s(2);
OS_Sleep(2000);
gKeys_pressed = 0;
CheckKevKeys();

View File

@ -1,7 +1,6 @@
#include "tests.h"
#include <string.h>
#include <unistd.h>
#include "common/globvars.h"
#include "pd/sys.h"

View File

@ -5,7 +5,7 @@
#include <string.h>
void test_errors_FatalError() {
FatalError(0x6c, "test_errors", "FATAL");
FatalError(107, "test_errors", "FATAL");
TEST_ASSERT_EQUAL_STRING("Can't open 'test_errors'", _unittest_last_fatal_error);
}

View File

@ -3,7 +3,6 @@
#include "common/globvars.h"
#include "common/input.h"
#include <string.h>
#include <unistd.h>
void test_input_KevKeyService() {
int i;
@ -15,7 +14,7 @@ void test_input_KevKeyService() {
gKeys_pressed = 0;
result = KevKeyService();
}
sleep_s(2);
OS_Sleep(2000);
gKeys_pressed = 0;
result = KevKeyService();

View File

@ -1,7 +1,6 @@
#include "tests.h"
#include <string.h>
#include <unistd.h>
#include "CORE/PIXELMAP/pixelmap.h"
#include "common/globvars.h"

View File

@ -1,7 +1,6 @@
#include "tests.h"
#include <string.h>
#include <unistd.h>
#include "common/globvars.h"
#include "common/powerup.h"

View File

@ -1,4 +1,5 @@
#include "harness/hooks.h"
#include "harness/os.h"
#include "tests.h"
#include <assert.h>
#include <errno.h>
@ -6,7 +7,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define UNITY_USE_COMMAND_LINE_ARGS 1
@ -15,6 +15,7 @@
#include "CORE/PIXELMAP/pixelmap.h"
#include "CORE/V1DB/actsupt.h"
#include "CORE/V1DB/dbsetup.h"
#include "common/errors.h"
#include "common/newgame.h"
#include "common/utility.h"
@ -23,6 +24,7 @@
#include "common/grafdata.h"
#include "harness.h"
#include "harness/config.h"
#include "harness/os.h"
#define debug(format_, ...) fprintf(stderr, format_, __VA_ARGS__)
@ -167,6 +169,8 @@ void setup_global_vars(int argc, char* argv[]) {
strcpy(gBasic_car_names[0], "BLKEAGLE.TXT");
OpenDiagnostics();
setup_temp_folder();
printf("INFO: temp folder is \"%s\"\n", temp_folder);
@ -182,20 +186,14 @@ void setup_global_vars(int argc, char* argv[]) {
fake_argv[fake_argc++] = "--no-signal-handler";
}
Harness_Init(&fake_argc, fake_argv);
Harness_ForceNullRenderer();
}
int has_data_directory() {
return root_dir != NULL;
}
void sleep_s(int sec) {
#ifdef _WIN32
Sleep(1000 * sec);
#else
sleep(sec);
#endif
}
void create_temp_file(char buffer[PATH_MAX + 1], const char* prefix) {
#ifdef _WIN32
DWORD attributes;

View File

@ -2,6 +2,7 @@
#define TESTS_H
#include "framework/unity.h"
#include "harness/os.h"
#include "harness/trace.h"
#ifndef PATH_MAX
@ -14,23 +15,23 @@
#define HOST_NL "\n"
#endif
void TEST_ASSERT_EQUAL_FILE_CONTENTS_BINARY(const uint8_t *expected, char *filename, int len);
void TEST_ASSERT_EQUAL_FILE_TEXT(const char *expected, char *filename);
void TEST_ASSERT_EQUAL_FILE_CONTENTS_BINARY(const uint8_t* expected, char* filename, int len);
void TEST_ASSERT_EQUAL_FILE_TEXT(const char* expected, char* filename);
extern int has_data_directory();
extern void sleep_s(int sec);
void create_temp_file(char buffer[PATH_MAX+1], const char *prefix);
void create_temp_file(char buffer[PATH_MAX + 1], const char* prefix);
#define REQUIRES_DATA_DIRECTORY() \
if (!has_data_directory()) \
TEST_IGNORE();
#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements) do { \
float *priv_expected = (float*)(expected); \
float *priv_actual = (float*)(actual); \
for(int it = (num_elements); it != 0; --it, ++priv_expected, ++priv_actual) { \
TEST_ASSERT_FLOAT_WITHIN((delta), *priv_expected, *priv_actual); \
} \
} while (0)
#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements) \
do { \
float* priv_expected = (float*)(expected); \
float* priv_actual = (float*)(actual); \
for (int it = (num_elements); it != 0; --it, ++priv_expected, ++priv_actual) { \
TEST_ASSERT_FLOAT_WITHIN((delta), *priv_expected, *priv_actual); \
} \
} while (0)
#endif