mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-05-23 12:52:59 +00:00
* Added Linux version of Iggy * Expose audio functionality * Reimplemented IggyAudioOutParamExtendedInformation more carefully * Link to .o files directly * Allow required SWF files to be loaded on Linux * Some other misc ifdef WINDOWS64 fixes * Another ifdef windows64 fix
339 lines
8.8 KiB
C
339 lines
8.8 KiB
C
#define _GNU_SOURCE
|
|
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <sched.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <fenv.h>
|
|
|
|
#include "iggy_audio_shim.h"
|
|
|
|
_Static_assert(sizeof(pthread_t) == 8, "pthread_t must be 8 bytes");
|
|
_Static_assert(sizeof(int) == 4, "int must be 32 bit");
|
|
_Static_assert(sizeof(unsigned) == 4, "unsigned must be 32 bit");
|
|
|
|
|
|
/* ============================================================
|
|
* CRT & Math Shims
|
|
* ============================================================ */
|
|
|
|
double _Sin(double x) { return sin(x); }
|
|
double _Log(double x) { return log(x); }
|
|
|
|
// Map to the C fenv.h rounding modes
|
|
int _Fltrounds(void) {
|
|
switch (fegetround()) {
|
|
case FE_TOWARDZERO: return 0;
|
|
case FE_TONEAREST: return 1;
|
|
case FE_DOWNWARD: return 2;
|
|
case FE_UPWARD: return 3;
|
|
default: return 1;
|
|
}
|
|
}
|
|
|
|
// BSD specific function supposed to return pointer to thread-local errno
|
|
#if defined(__GLIBC__)
|
|
int *__error(void) { return __errno_location(); }
|
|
#else
|
|
int *__error(void) { return &errno; }
|
|
#endif
|
|
|
|
// Microsoft specific variant of gmtime
|
|
int gmtime_s(struct tm *tmDest, const time_t *sourceTime) {
|
|
if (!tmDest || !sourceTime) return EINVAL;
|
|
if (gmtime_r(sourceTime, tmDest) == NULL) return EINVAL; /* conservative */
|
|
return 0;
|
|
}
|
|
|
|
// Returns pointer to ctype classification table
|
|
const unsigned short *_Getpctype(void) {
|
|
#if defined(__GLIBC__)
|
|
return (const unsigned short *)(*__ctype_b_loc());
|
|
#else
|
|
// We might still be alright
|
|
errno = ENOSYS;
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/* ============================================================
|
|
* Helpers for heap-allocated opaque objects
|
|
* ============================================================ */
|
|
|
|
static int alloc_and_init(void **out, size_t size, int (*init_fn)(void *), void (*free_fn)(void *)) {
|
|
if (!out) return EINVAL;
|
|
*out = NULL;
|
|
|
|
void *p = malloc(size);
|
|
if (!p) return ENOMEM;
|
|
|
|
int rc = init_fn(p);
|
|
if (rc != 0) {
|
|
if (free_fn) free_fn(p);
|
|
else free(p);
|
|
return rc;
|
|
}
|
|
|
|
*out = p;
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================
|
|
* Pthread Shims (opaque handles allocated on heap)
|
|
* ============================================================ */
|
|
|
|
// Mutex Attributes
|
|
|
|
static int mutexattr_init_thunk(void *p) {
|
|
return pthread_mutexattr_init((pthread_mutexattr_t *)p);
|
|
}
|
|
|
|
int scePthreadMutexattrInit(void **attr) {
|
|
return alloc_and_init(attr, sizeof(pthread_mutexattr_t), mutexattr_init_thunk, free);
|
|
}
|
|
|
|
int scePthreadMutexattrSettype(void **attr, int type) {
|
|
if (!attr || !*attr) return EINVAL;
|
|
return pthread_mutexattr_settype((pthread_mutexattr_t *)*attr, type);
|
|
}
|
|
|
|
int scePthreadMutexattrDestroy(void **attr) {
|
|
if (!attr || !*attr) return EINVAL;
|
|
pthread_mutexattr_t *a = (pthread_mutexattr_t *)*attr;
|
|
|
|
int rc = pthread_mutexattr_destroy(a);
|
|
if (rc == 0) {
|
|
free(a);
|
|
*attr = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// Mutexes
|
|
|
|
int scePthreadMutexInit(void **mutex, void **attr, const char *name) {
|
|
(void)name;
|
|
|
|
if (!mutex) return EINVAL;
|
|
*mutex = NULL;
|
|
|
|
pthread_mutex_t *m = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
|
if (!m) return ENOMEM;
|
|
|
|
pthread_mutexattr_t *a = (attr && *attr) ? (pthread_mutexattr_t *)*attr : NULL;
|
|
|
|
int rc = pthread_mutex_init(m, a);
|
|
if (rc != 0) {
|
|
free(m);
|
|
return rc;
|
|
}
|
|
|
|
*mutex = m;
|
|
return 0;
|
|
}
|
|
|
|
int scePthreadMutexLock(void **mutex) {
|
|
if (!mutex || !*mutex) return EINVAL;
|
|
return pthread_mutex_lock((pthread_mutex_t *)*mutex);
|
|
}
|
|
|
|
int scePthreadMutexTrylock(void **mutex) {
|
|
if (!mutex || !*mutex) return EINVAL;
|
|
return pthread_mutex_trylock((pthread_mutex_t *)*mutex);
|
|
}
|
|
|
|
int scePthreadMutexUnlock(void **mutex) {
|
|
if (!mutex || !*mutex) return EINVAL;
|
|
return pthread_mutex_unlock((pthread_mutex_t *)*mutex);
|
|
}
|
|
|
|
int scePthreadMutexTimedlock(void **mutex, unsigned int usecs) {
|
|
if (!mutex || !*mutex) return EINVAL;
|
|
|
|
struct timespec ts;
|
|
|
|
// Get current wall clock time
|
|
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
|
return errno;
|
|
|
|
// Add the relative microseconds to the current time
|
|
ts.tv_sec += (usecs / 1000000);
|
|
ts.tv_nsec += (usecs % 1000000) * 1000;
|
|
|
|
// Handle nanosecond overflow
|
|
if (ts.tv_nsec >= 1000000000L) {
|
|
ts.tv_sec++;
|
|
ts.tv_nsec -= 1000000000L;
|
|
}
|
|
|
|
// Call the standard POSIX function with the absolute time
|
|
return pthread_mutex_timedlock(*mutex, &ts);
|
|
}
|
|
|
|
int scePthreadMutexDestroy(void **mutex) {
|
|
if (!mutex || !*mutex) return EINVAL;
|
|
|
|
pthread_mutex_t *m = (pthread_mutex_t *)*mutex;
|
|
int rc = pthread_mutex_destroy(m);
|
|
if (rc == 0) {
|
|
free(m);
|
|
*mutex = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// Thread Attributes
|
|
|
|
static int attr_init_thunk(void *p) {
|
|
return pthread_attr_init((pthread_attr_t *)p);
|
|
}
|
|
|
|
int scePthreadAttrInit(void **attr) {
|
|
return alloc_and_init(attr, sizeof(pthread_attr_t), attr_init_thunk, free);
|
|
}
|
|
|
|
int scePthreadAttrSetstacksize(void **attr, size_t stacksize) {
|
|
if (!attr || !*attr) return EINVAL;
|
|
return pthread_attr_setstacksize((pthread_attr_t *)*attr, stacksize);
|
|
}
|
|
|
|
int scePthreadAttrDestroy(void **attr) {
|
|
if (!attr || !*attr) return EINVAL;
|
|
|
|
pthread_attr_t *a = (pthread_attr_t *)*attr;
|
|
int rc = pthread_attr_destroy(a);
|
|
if (rc == 0) {
|
|
free(a);
|
|
*attr = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// Threads
|
|
|
|
int scePthreadCreate(pthread_t *thread_out, void **attr, void *(*start_routine)(void *), void *arg, const char *name) {
|
|
(void)name;
|
|
|
|
if (!thread_out || !start_routine) return EINVAL;
|
|
pthread_attr_t *a = (attr && *attr) ? (pthread_attr_t *)*attr : NULL;
|
|
return pthread_create(thread_out, a, start_routine, arg);
|
|
}
|
|
|
|
int scePthreadJoin(pthread_t thread, void **value_ptr) {
|
|
return pthread_join(thread, value_ptr);
|
|
}
|
|
|
|
int scePthreadDetach(pthread_t thread) {
|
|
return pthread_detach(thread);
|
|
}
|
|
|
|
pthread_t scePthreadSelf(void) {
|
|
return pthread_self();
|
|
}
|
|
|
|
void scePthreadYield(void) {
|
|
sched_yield();
|
|
}
|
|
|
|
int scePthreadSetschedparam(pthread_t thread, int policy, const struct sched_param *param) {
|
|
if (!param) return EINVAL;
|
|
return pthread_setschedparam(thread, policy, param);
|
|
}
|
|
|
|
int scePthreadGetschedparam(pthread_t thread, int *policy, struct sched_param *param) {
|
|
if (!policy || !param) return EINVAL;
|
|
return pthread_getschedparam(thread, policy, param);
|
|
}
|
|
|
|
// TLS Keys
|
|
|
|
int scePthreadKeyCreate(pthread_key_t *key, void (*destructor)(void *), const char *name) {
|
|
(void)name;
|
|
if (!key) return EINVAL;
|
|
return pthread_key_create(key, destructor);
|
|
}
|
|
|
|
int scePthreadSetspecific(pthread_key_t key, const void *value) {
|
|
return pthread_setspecific(key, value);
|
|
}
|
|
|
|
void *scePthreadGetspecific(pthread_key_t key) {
|
|
return pthread_getspecific(key);
|
|
}
|
|
|
|
// Affinity
|
|
|
|
int scePthreadSetaffinity(pthread_t thread, uint64_t cpumask) {
|
|
(void)thread;
|
|
(void)cpumask;
|
|
return 0; // Could return ENOSYS here, but this should be alright too
|
|
}
|
|
|
|
/* ============================================================
|
|
* Kernel & Hardware Shims
|
|
* ============================================================ */
|
|
|
|
int sceKernelGettimeofday(struct timeval *tp) {
|
|
if (!tp) return EINVAL;
|
|
return gettimeofday(tp, NULL);
|
|
}
|
|
|
|
int sceKernelUsleep(unsigned microseconds) {
|
|
return usleep(microseconds);
|
|
}
|
|
|
|
/* ============================================================
|
|
* Audio Shims
|
|
* ============================================================ */
|
|
|
|
static IggyAudioShimCallbacks g_audioCbs = {};
|
|
|
|
int iggyAudioShimRegisterCallbacks(const IggyAudioShimCallbacks* callbacks) {
|
|
if (callbacks) {
|
|
g_audioCbs = *callbacks;
|
|
} else {
|
|
IggyAudioShimCallbacks cbs = {};
|
|
g_audioCbs = cbs;
|
|
}
|
|
return IGGY_AUDIO_OK;
|
|
}
|
|
|
|
int sceAudioOutOpen(int user_id, enum IggyAudioOutPort port_type, int index, unsigned length, unsigned sample_rate, IggyAudioOutParamExtendedInformation param_type) {
|
|
if (g_audioCbs.iggyAudioOutOpen)
|
|
return g_audioCbs.iggyAudioOutOpen(user_id, port_type, index, length, sample_rate, param_type);
|
|
return IGGY_AUDIO_OK;
|
|
}
|
|
|
|
int sceAudioOutSetVolume(int handle, int flag, int* vol) {
|
|
if (g_audioCbs.iggyAudioOutSetVolume)
|
|
return g_audioCbs.iggyAudioOutSetVolume(handle, flag, vol);
|
|
return IGGY_AUDIO_OK;
|
|
}
|
|
|
|
int sceAudioOutClose(int handle) {
|
|
if (g_audioCbs.iggyAudioOutClose)
|
|
return g_audioCbs.iggyAudioOutClose(handle);
|
|
return IGGY_AUDIO_OK;
|
|
}
|
|
|
|
int sceAudioOutOutput(int handle, void* ptr) {
|
|
if (g_audioCbs.iggyAudioOutOutput)
|
|
return g_audioCbs.iggyAudioOutOutput(handle, ptr);
|
|
return IGGY_AUDIO_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* Misc
|
|
* ============================================================ */
|
|
|
|
void gdraw_ps4_wait(void) {
|
|
usleep(1000);
|
|
}
|