Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2020-07-30 19:09:52 -07:00
commit 5135756ec4
28 changed files with 751 additions and 187 deletions

View File

@ -88,6 +88,8 @@ Other fixes:
- SM83: Simplify register pair access on big endian - SM83: Simplify register pair access on big endian
- Wii: Fix pixelated filtering on interframe blending (fixes mgba.io/i/1830) - Wii: Fix pixelated filtering on interframe blending (fixes mgba.io/i/1830)
Misc: Misc:
- GB: Allow pausing event loop while CPU is blocked
- GBA: Allow pausing event loop while CPU is blocked
- Debugger: Keep track of global cycle count - Debugger: Keep track of global cycle count
- FFmpeg: Add looping option for GIF/APNG - FFmpeg: Add looping option for GIF/APNG
- FFmpeg: Use range coder for FFV1 to reduce output size - FFmpeg: Use range coder for FFV1 to reduce output size

View File

@ -13,7 +13,7 @@ set(CMAKE_C_STANDARD 99)
if(NOT MSVC) if(NOT MSVC)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF)
if(SWITCH) if(SWITCH OR 3DS)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_EXTENSIONS ON)
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.3") elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.3")
@ -297,6 +297,7 @@ include(CheckIncludeFiles)
check_function_exists(strdup HAVE_STRDUP) check_function_exists(strdup HAVE_STRDUP)
check_function_exists(strndup HAVE_STRNDUP) check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(strlcpy HAVE_STRLCPY) check_function_exists(strlcpy HAVE_STRLCPY)
check_function_exists(vasprintf HAVE_VASPRINTF)
if(NOT DEFINED PSP2) if(NOT DEFINED PSP2)
check_function_exists(localtime_r HAVE_LOCALTIME_R) check_function_exists(localtime_r HAVE_LOCALTIME_R)
endif() endif()
@ -379,6 +380,10 @@ if(HAVE_STRLCPY)
list(APPEND FUNCTION_DEFINES HAVE_STRLCPY) list(APPEND FUNCTION_DEFINES HAVE_STRLCPY)
endif() endif()
if(HAVE_VASPRINTF)
list(APPEND FUNCTION_DEFINES HAVE_VASPRINTF)
endif()
if(HAVE_LOCALTIME_R) if(HAVE_LOCALTIME_R)
list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R) list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R)
endif() endif()
@ -988,6 +993,9 @@ endif()
if(NOT USE_CMOCKA) if(NOT USE_CMOCKA)
set(BUILD_SUITE OFF) set(BUILD_SUITE OFF)
endif() endif()
if(BUILD_TEST OR BUILD_SUITE OR BUILD_CINEMA)
enable_testing()
endif()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test ${CMAKE_CURRENT_BINARY_DIR}/test) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test ${CMAKE_CURRENT_BINARY_DIR}/test)
if(BUILD_EXAMPLE) if(BUILD_EXAMPLE)

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

View File

@ -24,6 +24,7 @@ typedef THREAD_ENTRY (*ThreadEntry)(void*);
typedef pthread_t Thread; typedef pthread_t Thread;
typedef pthread_mutex_t Mutex; typedef pthread_mutex_t Mutex;
typedef pthread_cond_t Condition; typedef pthread_cond_t Condition;
typedef pthread_key_t ThreadLocal;
static inline int MutexInit(Mutex* mutex) { static inline int MutexInit(Mutex* mutex) {
return pthread_mutex_init(mutex, 0); return pthread_mutex_init(mutex, 0);
@ -101,6 +102,18 @@ static inline int ThreadSetName(const char* name) {
#endif #endif
} }
static inline void ThreadLocalInitKey(ThreadLocal* key) {
pthread_key_create(key, 0);
}
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
pthread_setspecific(key, value);
}
static inline void* ThreadLocalGetValue(ThreadLocal key) {
return pthread_getspecific(key);
}
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -17,6 +17,7 @@ typedef struct {
} Condition; } Condition;
#define THREAD_ENTRY int #define THREAD_ENTRY int
typedef THREAD_ENTRY (*ThreadEntry)(void*); typedef THREAD_ENTRY (*ThreadEntry)(void*);
typedef int ThreadLocal;
static inline int MutexInit(Mutex* mutex) { static inline int MutexInit(Mutex* mutex) {
Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0); Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0);
@ -143,4 +144,19 @@ static inline int ThreadSetName(const char* name) {
UNUSED(name); UNUSED(name);
return -1; return -1;
} }
static inline void ThreadLocalInitKey(ThreadLocal* key) {
static int base = 0x90;
*key = __atomic_fetch_add(&base, 1, __ATOMIC_SEQ_CST);
}
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
void** tls = sceKernelGetTLSAddr(key);
*tls = value;
}
static inline void* ThreadLocalGetValue(ThreadLocal key) {
void** tls = sceKernelGetTLSAddr(key);
return *tls;
}
#endif #endif

View File

@ -16,6 +16,7 @@ typedef THREAD_ENTRY ThreadEntry(LPVOID);
typedef HANDLE Thread; typedef HANDLE Thread;
typedef CRITICAL_SECTION Mutex; typedef CRITICAL_SECTION Mutex;
typedef CONDITION_VARIABLE Condition; typedef CONDITION_VARIABLE Condition;
typedef DWORD ThreadLocal;
static inline int MutexInit(Mutex* mutex) { static inline int MutexInit(Mutex* mutex) {
InitializeCriticalSection(mutex); InitializeCriticalSection(mutex);
@ -88,4 +89,16 @@ static inline int ThreadSetName(const char* name) {
return -1; return -1;
} }
static inline void ThreadLocalInitKey(ThreadLocal* key) {
*key = TlsAlloc();
}
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
TlsSetValue(key, value);
}
static inline void* ThreadLocalGetValue(ThreadLocal key) {
return TlsGetValue(key);
}
#endif #endif

View File

@ -45,6 +45,7 @@ const char* hex4(const char* line, uint8_t* out);
void rtrim(char* string); void rtrim(char* string);
ssize_t parseQuotedString(const char* unparsed, ssize_t unparsedLen, char* parsed, ssize_t parsedLen); ssize_t parseQuotedString(const char* unparsed, ssize_t unparsedLen, char* parsed, ssize_t parsedLen);
bool wildcard(const char* search, const char* string);
CXX_GUARD_END CXX_GUARD_END

View File

@ -11,6 +11,12 @@
CXX_GUARD_START CXX_GUARD_START
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING
#if __STDC_VERSION__ >= 201112L
#define ThreadLocal _Thread_local void*
#define ThreadLocalInitKey(X)
#define ThreadLocalSetKey(K, V) K = V
#define ThreadLocalGetValue(K) K
#endif
#ifdef USE_PTHREADS #ifdef USE_PTHREADS
#include <mgba-util/platform/posix/threading.h> #include <mgba-util/platform/posix/threading.h>
#elif defined(_WIN32) #elif defined(_WIN32)
@ -40,6 +46,7 @@ typedef void* Thread;
typedef void* Mutex; typedef void* Mutex;
#endif #endif
typedef void* Condition; typedef void* Condition;
typedef int ThreadLocal;
static inline int MutexInit(Mutex* mutex) { static inline int MutexInit(Mutex* mutex) {
UNUSED(mutex); UNUSED(mutex);
@ -93,6 +100,20 @@ static inline int ConditionWake(Condition* cond) {
UNUSED(cond); UNUSED(cond);
return 0; return 0;
} }
static inline void ThreadLocalInitKey(ThreadLocal* key) {
UNUSED(key);
}
static inline void ThreadLocalSetKey(ThreadLocal key, void* value) {
UNUSED(key);
UNUSED(value);
}
static inline void* ThreadLocalGetValue(ThreadLocal key) {
UNUSED(key);
return NULL;
}
#endif #endif
CXX_GUARD_END CXX_GUARD_END

View File

@ -235,6 +235,8 @@ DECL_BIT(GBSerializedCpuFlags, Condition, 0);
DECL_BIT(GBSerializedCpuFlags, IrqPending, 1); DECL_BIT(GBSerializedCpuFlags, IrqPending, 1);
DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2); DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2);
DECL_BIT(GBSerializedCpuFlags, EiPending, 3); DECL_BIT(GBSerializedCpuFlags, EiPending, 3);
DECL_BIT(GBSerializedCpuFlags, Halted, 4);
DECL_BIT(GBSerializedCpuFlags, Blocked, 5);
DECL_BITFIELD(GBSerializedTimerFlags, uint8_t); DECL_BITFIELD(GBSerializedTimerFlags, uint8_t);
DECL_BIT(GBSerializedTimerFlags, IrqPending, 0); DECL_BIT(GBSerializedTimerFlags, IrqPending, 0);

View File

@ -236,6 +236,7 @@ DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
DECL_BIT(GBASerializedMiscFlags, Halted, 0); DECL_BIT(GBASerializedMiscFlags, Halted, 0);
DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1); DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1);
DECL_BIT(GBASerializedMiscFlags, IrqPending, 2); DECL_BIT(GBASerializedMiscFlags, IrqPending, 2);
DECL_BIT(GBASerializedMiscFlags, Blocked, 3);
struct GBASerializedState { struct GBASerializedState {
uint32_t versionMagic; uint32_t versionMagic;

View File

@ -16,23 +16,22 @@
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING
static const float _defaultFPSTarget = 60.f; static const float _defaultFPSTarget = 60.f;
static ThreadLocal _contextKey;
#ifdef USE_PTHREADS #ifdef USE_PTHREADS
static pthread_key_t _contextKey;
static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT; static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
static void _createTLS(void) { static void _createTLS(void) {
pthread_key_create(&_contextKey, 0); ThreadLocalInitKey(&_contextKey);
} }
#elif _WIN32 #elif _WIN32
static DWORD _contextKey;
static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT; static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
UNUSED(once); UNUSED(once);
UNUSED(param); UNUSED(param);
UNUSED(context); UNUSED(context);
_contextKey = TlsAlloc(); ThreadLocalInitKey(&_contextKey);
return TRUE; return TRUE;
} }
#endif #endif
@ -144,12 +143,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
struct mCoreThread* threadContext = context; struct mCoreThread* threadContext = context;
#ifdef USE_PTHREADS #ifdef USE_PTHREADS
pthread_once(&_contextOnce, _createTLS); pthread_once(&_contextOnce, _createTLS);
pthread_setspecific(_contextKey, threadContext);
#elif _WIN32 #elif _WIN32
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0); InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
TlsSetValue(_contextKey, threadContext);
#endif #endif
ThreadLocalSetKey(_contextKey, threadContext);
ThreadSetName("CPU Thread"); ThreadSetName("CPU Thread");
#if !defined(_WIN32) && defined(USE_PTHREADS) #if !defined(_WIN32) && defined(USE_PTHREADS)
@ -620,21 +618,14 @@ void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
MutexUnlock(&threadContext->impl->stateMutex); MutexUnlock(&threadContext->impl->stateMutex);
} }
struct mCoreThread* mCoreThreadGet(void) {
#ifdef USE_PTHREADS #ifdef USE_PTHREADS
struct mCoreThread* mCoreThreadGet(void) {
pthread_once(&_contextOnce, _createTLS); pthread_once(&_contextOnce, _createTLS);
return pthread_getspecific(_contextKey);
}
#elif _WIN32 #elif _WIN32
struct mCoreThread* mCoreThreadGet(void) {
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0); InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
return TlsGetValue(_contextKey);
}
#else
struct mCoreThread* mCoreThreadGet(void) {
return NULL;
}
#endif #endif
return ThreadLocalGetValue(_contextKey);
}
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) { static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
UNUSED(logger); UNUSED(logger);

View File

@ -681,7 +681,7 @@ void GBProcessEvents(struct SM83Core* cpu) {
nextEvent = cycles; nextEvent = cycles;
do { do {
nextEvent = mTimingTick(&gb->timing, nextEvent); nextEvent = mTimingTick(&gb->timing, nextEvent);
} while (gb->cpuBlocked); } while (gb->cpuBlocked && !gb->earlyExit);
cpu->nextEvent = nextEvent; cpu->nextEvent = nextEvent;
if (cpu->halted) { if (cpu->halted) {
@ -695,6 +695,9 @@ void GBProcessEvents(struct SM83Core* cpu) {
} }
} while (cpu->cycles >= cpu->nextEvent); } while (cpu->cycles >= cpu->nextEvent);
gb->earlyExit = false; gb->earlyExit = false;
if (gb->cpuBlocked) {
cpu->cycles = cpu->nextEvent;
}
} }
void GBSetInterrupts(struct SM83Core* cpu, bool enable) { void GBSetInterrupts(struct SM83Core* cpu, bool enable) {

View File

@ -56,6 +56,8 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending); flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);
flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed); flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed);
flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending)); flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending));
flags = GBSerializedCpuFlagsSetHalted(flags, gb->cpu->halted);
flags = GBSerializedCpuFlagsSetBlocked(flags, gb->cpuBlocked);
STORE_32LE(flags, 0, &state->cpu.flags); STORE_32LE(flags, 0, &state->cpu.flags);
STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending); STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending);
@ -173,6 +175,9 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags); gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags); gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags); gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags);
gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags);
gb->audio.timingFactor = gb->doubleSpeed + 1; gb->audio.timingFactor = gb->doubleSpeed + 1;
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);

View File

@ -379,6 +379,7 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
GBFrameEnded(video->p); GBFrameEnded(video->p);
mCoreSyncPostFrame(video->p->sync); mCoreSyncPostFrame(video->p->sync);
++video->frameCounter; ++video->frameCounter;
video->p->earlyExit = true;
GBFrameStarted(video->p); GBFrameStarted(video->p);
} }

View File

@ -286,7 +286,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
} }
#endif #endif
nextEvent = mTimingTick(&gba->timing, cycles < nextEvent ? nextEvent : cycles); nextEvent = mTimingTick(&gba->timing, cycles < nextEvent ? nextEvent : cycles);
} while (gba->cpuBlocked); } while (gba->cpuBlocked && !gba->earlyExit);
cpu->nextEvent = nextEvent; cpu->nextEvent = nextEvent;
if (cpu->halted) { if (cpu->halted) {
@ -305,11 +305,9 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
} }
} }
gba->earlyExit = false; gba->earlyExit = false;
#ifndef NDEBUG
if (gba->cpuBlocked) { if (gba->cpuBlocked) {
mLOG(GBA, FATAL, "CPU is blocked!"); cpu->cycles = cpu->nextEvent;
} }
#endif
} }
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS

View File

@ -67,6 +67,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
miscFlags = GBASerializedMiscFlagsFillIrqPending(miscFlags); miscFlags = GBASerializedMiscFlagsFillIrqPending(miscFlags);
STORE_32(gba->irqEvent.when - mTimingCurrentTime(&gba->timing), 0, &state->nextIrq); STORE_32(gba->irqEvent.when - mTimingCurrentTime(&gba->timing), 0, &state->nextIrq);
} }
miscFlags = GBASerializedMiscFlagsSetBlocked(miscFlags, gba->cpuBlocked);
STORE_32(miscFlags, 0, &state->miscFlags); STORE_32(miscFlags, 0, &state->miscFlags);
GBAMemorySerialize(&gba->memory, state); GBAMemorySerialize(&gba->memory, state);
@ -185,6 +186,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(when, 0, &state->nextIrq); LOAD_32(when, 0, &state->nextIrq);
mTimingSchedule(&gba->timing, &gba->irqEvent, when); mTimingSchedule(&gba->timing, &gba->irqEvent, when);
} }
gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags);
GBAVideoDeserialize(&gba->video, state); GBAVideoDeserialize(&gba->video, state);
GBAMemoryDeserialize(&gba->memory, state); GBAMemoryDeserialize(&gba->memory, state);

View File

@ -196,6 +196,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
video->frameskipCounter = video->frameskip; video->frameskipCounter = video->frameskip;
} }
++video->frameCounter; ++video->frameCounter;
video->p->earlyExit = true;
break; break;
case VIDEO_VERTICAL_TOTAL_PIXELS - 1: case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat); video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);

View File

@ -54,9 +54,8 @@ add_custom_target(${BINARY_NAME}-py-bdist
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${BINARY_NAME}-py) DEPENDS ${BINARY_NAME}-py)
file(GLOB BASE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test_*.py) file(GLOB TESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
file(GLOB SUBTESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py) foreach(TEST IN LISTS TESTS)
foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
if(APPLE) if(APPLE)
set(PATH DYLD_LIBRARY_PATH) set(PATH DYLD_LIBRARY_PATH)
elseif(WIN32) elseif(WIN32)
@ -64,7 +63,7 @@ foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
else() else()
set(PATH LD_LIBRARY_PATH) set(PATH LD_LIBRARY_PATH)
endif() endif()
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/(tests/.*/)?test_" "" TEST_NAME "${TEST}") string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/tests/(.*/)?test_" "" TEST_NAME "${TEST}")
string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}") string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}")
add_test(NAME python-${TEST_NAME} add_test(NAME python-${TEST_NAME}
COMMAND ${PYTHON_EXECUTABLE} setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST} COMMAND ${PYTHON_EXECUTABLE} setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST}

View File

@ -43,4 +43,5 @@ if(BUILD_CINEMA)
add_executable(${BINARY_NAME}-cinema ${CMAKE_CURRENT_SOURCE_DIR}/cinema-main.c) add_executable(${BINARY_NAME}-cinema ${CMAKE_CURRENT_SOURCE_DIR}/cinema-main.c)
target_link_libraries(${BINARY_NAME}-cinema ${BINARY_NAME} ${PLATFORM_LIBRARY}) target_link_libraries(${BINARY_NAME}-cinema ${BINARY_NAME} ${PLATFORM_LIBRARY})
set_target_properties(${BINARY_NAME}-cinema PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") set_target_properties(${BINARY_NAME}-cinema PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
add_test(cinema ${BINARY_NAME}-cinema -v)
endif() endif()

File diff suppressed because it is too large Load Diff

View File

@ -518,4 +518,34 @@ ssize_t parseQuotedString(const char* unparsed, ssize_t unparsedLen, char* parse
} }
} }
return -1; return -1;
}
bool wildcard(const char* search, const char* string) {
while (true) {
if (search[0] == '*') {
while (search[0] == '*') {
++search;
}
if (!search[0]) {
return true;
}
while (string[0]) {
if (string[0] == search[0] && wildcard(search, string)) {
return true;
}
++string;
}
return false;
} else if (!search[0]) {
return !string[0];
} else if (!string[0]) {
return false;
} else if (string[0] != search[0]) {
return false;
} else {
++search;
++string;
}
}
return false;
} }