mirror of https://github.com/ClassiCube/ClassiCube
Merge pull request #1471 from Extrems/master
Migrate GameCube port to libogc2
This commit is contained in:
commit
1cdc87082f
|
|
@ -0,0 +1,52 @@
|
|||
name: Build latest (GameCube)
|
||||
# trigger via either push to selected branches or on manual run
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-gc
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/extremscorner/libogc2:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile GameCube build
|
||||
id: compile
|
||||
run: |
|
||||
make gamecube
|
||||
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
SOURCE_FILE: 'ClassiCube-gc.dol'
|
||||
DEST_NAME: 'ClassiCube-gc.dol'
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
SOURCE_FILE: 'ClassiCube-gc.elf'
|
||||
DEST_NAME: 'ClassiCube-gc.elf'
|
||||
|
||||
|
||||
- uses: ./.github/actions/notify_success
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
DESTINATION_URL: '${{ secrets.NOTIFY_URL }}'
|
||||
WORKFLOW_NAME: 'gc'
|
||||
|
||||
|
||||
- uses: ./.github/actions/notify_failure
|
||||
if: failure()
|
||||
with:
|
||||
NOTIFY_MESSAGE: 'Failed to produce GameCube build'
|
||||
WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}'
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
name: Build latest (Wii/GameCube)
|
||||
name: Build latest (Wii)
|
||||
# trigger via either push to selected branches or on manual run
|
||||
on:
|
||||
push:
|
||||
|
|
@ -8,7 +8,7 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-wiigc
|
||||
group: ${{ github.ref }}-wii
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
|
@ -19,11 +19,10 @@ jobs:
|
|||
image: devkitpro/devkitppc:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile Wii and GameCube build
|
||||
- name: Compile Wii build
|
||||
id: compile
|
||||
run: |
|
||||
make wii
|
||||
make gamecube
|
||||
|
||||
- name: Create Wii homebrew
|
||||
run: |
|
||||
|
|
@ -39,24 +38,12 @@ jobs:
|
|||
SOURCE_FILE: 'ClassiCube-wii.dol'
|
||||
DEST_NAME: 'ClassiCube-wii.dol'
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
SOURCE_FILE: 'ClassiCube-gc.dol'
|
||||
DEST_NAME: 'ClassiCube-gc.dol'
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
SOURCE_FILE: 'ClassiCube-wii.elf'
|
||||
DEST_NAME: 'ClassiCube-wii.elf'
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
SOURCE_FILE: 'ClassiCube-gc.elf'
|
||||
DEST_NAME: 'ClassiCube-gc.elf'
|
||||
|
||||
- uses: ./.github/actions/upload_build
|
||||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
|
|
@ -68,11 +55,11 @@ jobs:
|
|||
if: ${{ always() && steps.compile.outcome == 'success' }}
|
||||
with:
|
||||
DESTINATION_URL: '${{ secrets.NOTIFY_URL }}'
|
||||
WORKFLOW_NAME: 'wiigc'
|
||||
WORKFLOW_NAME: 'wii'
|
||||
|
||||
|
||||
- uses: ./.github/actions/notify_failure
|
||||
if: failure()
|
||||
with:
|
||||
NOTIFY_MESSAGE: 'Failed to produce Wii/Gamecube build'
|
||||
NOTIFY_MESSAGE: 'Failed to produce Wii build'
|
||||
WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}'
|
||||
|
|
@ -32,10 +32,10 @@ DEPFILES := $(OBJS:%.o=%.d)
|
|||
# Code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
MACHDEP = -DGEKKO -mogc -mcpu=750 -meabi -mhard-float
|
||||
CFLAGS = -g -O2 -Wall $(MACHDEP) -I$(DEVKITPRO)/libogc/include -DPLAT_GAMECUBE
|
||||
MACHDEP = -DGEKKO -mogc -mcpu=750 -meabi -msdata=sysv -mhard-float
|
||||
CFLAGS = -g -O2 -Wall $(MACHDEP) -I$(DEVKITPRO)/libogc2/gamecube/include -DPLAT_GAMECUBE
|
||||
|
||||
LDFLAGS = -g $(MACHDEP) -L$(DEVKITPRO)/libogc/lib/cube
|
||||
LDFLAGS = -g $(MACHDEP) -L$(DEVKITPRO)/libogc2/gamecube/lib
|
||||
# Additional libraries to link against
|
||||
LIBS = -lasnd -lbba -lfat -logc -lm
|
||||
|
||||
|
|
|
|||
|
|
@ -247,9 +247,9 @@ Run `make wii`. You'll need [libogc](https://github.com/devkitPro/libogc)
|
|||
|
||||
#### GameCube
|
||||
|
||||
Run `make gamecube`. You'll need [libogc](https://github.com/devkitPro/libogc)
|
||||
Run `make gamecube`. You'll need [libogc2](https://github.com/extremscorner/libogc2)
|
||||
|
||||
**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `gamecube-dev` group)**
|
||||
**NOTE: It is highly recommended that you install the precompiled libogc2 packages from [here](https://github.com/extremscorner/pacman-packages#readme) - you need the `gamecube-dev` group)**
|
||||
|
||||
#### Nintendo DS/DSi
|
||||
|
||||
|
|
@ -454,8 +454,9 @@ Further information (e.g. style) for ClassiCube's source code can be found in th
|
|||
* [libctru](https://github.com/devkitPro/libctru) - Backend for 3DS
|
||||
* [citro3D](https://github.com/devkitPro/citro3d) - Rendering backend for 3DS
|
||||
* [Citra](https://github.com/citra-emu/citra) - Emulator used to test 3DS port
|
||||
* [libogc](https://github.com/devkitPro/libogc) - Backend for Wii and GameCube
|
||||
* [libfat](https://github.com/devkitPro/libfat) - Filesystem backend for Wii/GC
|
||||
* [libogc](https://github.com/devkitPro/libogc) - Backend for Wii
|
||||
* [libogc2](https://github.com/extremscorner/libogc2) - Backend for GameCube
|
||||
* [libdvm](https://github.com/devkitPro/libdvm) - Filesystem backend for Wii/GC
|
||||
* [Dolphin](https://github.com/dolphin-emu/dolphin) - Emulator used to test Wii/GC port
|
||||
* [libdragon](https://github.com/DragonMinded/libdragon) - Backend for Nintendo 64
|
||||
* [ares](https://github.com/ares-emulator/ares) - Emulator used to test Nintendo 64 port
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ void MusicCallback(s32 voice) {
|
|||
cc_result Audio_Init(struct AudioContext* ctx, int buffers) {
|
||||
ctx->chanID = -1;
|
||||
ctx->count = buffers;
|
||||
ctx->volume = 255;
|
||||
ctx->volume = MAX_VOLUME;
|
||||
ctx->bufHead = 0;
|
||||
ctx->makeAvailable = false;
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate
|
|||
}
|
||||
|
||||
void Audio_SetVolume(struct AudioContext* ctx, int volume) {
|
||||
ctx->volume = (volume / 100.0f) * 255;
|
||||
ctx->volume = (volume / 100.0f) * MAX_VOLUME;
|
||||
}
|
||||
|
||||
cc_result Audio_QueueChunk(struct AudioContext* ctx, struct AudioChunk* chunk) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*########################################################################################################################*
|
||||
*------------------------------------------------------Logging/Time-------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
#ifdef HW_RVL
|
||||
static void LogOverEXI(char* msg, int len) {
|
||||
u32 cmd = 0x80000000 | (0x800400 << 6); // write flag, UART base address
|
||||
|
||||
|
|
@ -27,13 +28,16 @@ void Platform_Log(const char* msg, int len) {
|
|||
|
||||
LogOverEXI(tmp, len + 1);
|
||||
}
|
||||
|
||||
#define GCWII_EPOCH_ADJUST 946684800ULL // GameCube/Wii time epoch is year 2000, not 1970
|
||||
#else
|
||||
void Platform_Log(const char* msg, int len) {
|
||||
SYS_Report("%.*s\n", len, msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
TimeMS DateTime_CurrentUTC(void) {
|
||||
u64 raw = gettime();
|
||||
u64 secs = ticks_to_secs(raw);
|
||||
return secs + UNIX_EPOCH_SECONDS + GCWII_EPOCH_ADJUST;
|
||||
struct timeval cur;
|
||||
gettimeofday(&cur, NULL);
|
||||
return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS;
|
||||
}
|
||||
|
||||
void DateTime_CurrentLocal(struct cc_datetime* t) {
|
||||
|
|
@ -51,7 +55,11 @@ void DateTime_CurrentLocal(struct cc_datetime* t) {
|
|||
}
|
||||
|
||||
cc_uint64 Stopwatch_Measure(void) {
|
||||
return gettime();
|
||||
#ifdef HW_RVL
|
||||
return SYS_Time();
|
||||
#else
|
||||
return __SYS_GetSystemTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) {
|
||||
|
|
@ -123,12 +131,15 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall
|
|||
DIR* dirPtr = opendir(str.buffer);
|
||||
if (!dirPtr) return errno;
|
||||
|
||||
// POSIX docs: "When the end of the directory is encountered, a null pointer is returned and errno is not changed."
|
||||
// errno is sometimes leftover from previous calls, so always reset it before readdir gets called
|
||||
errno = 0;
|
||||
String_InitArray(path, pathBuffer);
|
||||
|
||||
while ((entry = readdir(dirPtr))) {
|
||||
do {
|
||||
// POSIX docs: "When the end of the directory is encountered, a null pointer is returned and errno is not changed."
|
||||
// errno is sometimes leftover from previous calls, so always reset it before readdir gets called
|
||||
errno = 0;
|
||||
entry = readdir(dirPtr);
|
||||
if (!entry) continue;
|
||||
|
||||
path.length = 0;
|
||||
String_Format1(&path, "%s/", dirPath);
|
||||
|
||||
|
|
@ -143,8 +154,7 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall
|
|||
// TODO: fallback to stat when this fails
|
||||
|
||||
callback(&path, obj, is_dir);
|
||||
errno = 0;
|
||||
}
|
||||
} while (entry || errno == EOVERFLOW);
|
||||
|
||||
res = errno; // return code from readdir
|
||||
closedir(dirPtr);
|
||||
|
|
@ -216,13 +226,16 @@ void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char*
|
|||
lwp_t* thread = (lwp_t*)Mem_Alloc(1, sizeof(lwp_t), "thread");
|
||||
*handle = thread;
|
||||
|
||||
int res = LWP_CreateThread(thread, ExecThread, (void*)func, NULL, stackSize, 80);
|
||||
int res = LWP_CreateThread(thread, ExecThread, (void*)func, NULL, stackSize, 64);
|
||||
if (res) Process_Abort2(res, "Creating thread");
|
||||
}
|
||||
|
||||
void Thread_Detach(void* handle) {
|
||||
// TODO: Leaks return value of thread ???
|
||||
lwp_t* ptr = (lwp_t*)handle;
|
||||
#ifndef HW_RVL
|
||||
LWP_DetachThread(*ptr);
|
||||
#endif
|
||||
Mem_Free(ptr);
|
||||
}
|
||||
|
||||
|
|
@ -259,6 +272,7 @@ void Mutex_Unlock(void* handle) {
|
|||
if (res) Process_Abort2(res, "Unlocking mutex");
|
||||
}
|
||||
|
||||
#ifdef HW_RVL
|
||||
// should really use a semaphore with max 1.. too bad no 'TimedWait' though
|
||||
struct WaitData {
|
||||
cond_t cond;
|
||||
|
|
@ -320,8 +334,8 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
|
|||
struct timespec ts;
|
||||
int res;
|
||||
|
||||
ts.tv_sec = milliseconds / 1000;
|
||||
ts.tv_nsec = milliseconds % 1000;
|
||||
ts.tv_sec = milliseconds / TB_MSPERSEC;
|
||||
ts.tv_nsec = (milliseconds % TB_MSPERSEC) * TB_NSPERMS;
|
||||
|
||||
Mutex_Lock(&ptr->mutex);
|
||||
if (!ptr->signalled) {
|
||||
|
|
@ -331,6 +345,45 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
|
|||
ptr->signalled = false;
|
||||
Mutex_Unlock(&ptr->mutex);
|
||||
}
|
||||
#else
|
||||
void* Waitable_Create(const char* name) {
|
||||
sem_t* ptr = (sem_t*)Mem_Alloc(1, sizeof(sem_t), "waitable");
|
||||
int res = LWP_SemInit(ptr, 0, 1);
|
||||
if (res) Process_Abort2(res, "Creating waitable");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Waitable_Free(void* handle) {
|
||||
sem_t* ptr = (sem_t*)handle;
|
||||
int res = LWP_SemDestroy(*ptr);
|
||||
if (res) Process_Abort2(res, "Destroying waitable");
|
||||
Mem_Free(handle);
|
||||
}
|
||||
|
||||
void Waitable_Signal(void* handle) {
|
||||
sem_t* ptr = (sem_t*)handle;
|
||||
int res = LWP_SemPost(*ptr);
|
||||
if (res && res != EOVERFLOW) Process_Abort2(res, "Signalling event");
|
||||
}
|
||||
|
||||
void Waitable_Wait(void* handle) {
|
||||
sem_t* ptr = (sem_t*)handle;
|
||||
int res = LWP_SemWait(*ptr);
|
||||
if (res) Process_Abort2(res, "Event wait");
|
||||
}
|
||||
|
||||
void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
|
||||
sem_t* ptr = (sem_t*)handle;
|
||||
struct timespec ts;
|
||||
int res;
|
||||
|
||||
ts.tv_sec = milliseconds / TB_MSPERSEC;
|
||||
ts.tv_nsec = (milliseconds % TB_MSPERSEC) * TB_NSPERMS;
|
||||
|
||||
res = LWP_SemTimedWait(*ptr, &ts);
|
||||
if (res && res != ETIMEDOUT) Process_Abort2(res, "Event timed wait");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
|
|
@ -359,11 +412,11 @@ cc_result Socket_Create(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) {
|
|||
struct sockaddr* raw = (struct sockaddr*)addr->data;
|
||||
if (!net_supported) { *s = -1; return ERR_NO_NETWORKING; }
|
||||
|
||||
*s = net_socket(raw->sa_family, SOCK_STREAM, 0);
|
||||
*s = net_socket(raw->sa_family, SOCK_STREAM, IPPROTO_IP);
|
||||
if (*s < 0) return *s;
|
||||
|
||||
if (nonblocking) {
|
||||
int blocking_raw = -1; /* non-blocking mode */
|
||||
int blocking_raw = 1; /* non-blocking mode */
|
||||
net_ioctl(*s, FIONBIO, &blocking_raw);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@
|
|||
#include <network.h>
|
||||
#include <ogc/lwp.h>
|
||||
#include <ogc/mutex.h>
|
||||
#include <ogc/cond.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include <ogc/semaphore.h>
|
||||
#include <ogc/system.h>
|
||||
#include <ogc/timesupp.h>
|
||||
#include <fat.h>
|
||||
#include <ogc/exi.h>
|
||||
|
||||
const cc_result ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */
|
||||
const cc_result ReturnCode_FileNotFound = ENOENT;
|
||||
|
|
@ -65,10 +65,13 @@ int main(int argc, char** argv) {
|
|||
static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) {
|
||||
// DNS resolution not implemented in gamecube libbba
|
||||
static struct fixed_dns_map {
|
||||
const cc_string host, ip;
|
||||
const cc_string host, ip[2];
|
||||
} mappings[] = {
|
||||
{ String_FromConst("cdn.classicube.net"), String_FromConst("104.20.90.158") },
|
||||
{ String_FromConst("www.classicube.net"), String_FromConst("104.20.90.158") }
|
||||
{ String_FromConst("cdn.classicube.net"), { String_FromConst("172.66.134.165"), String_FromConst("172.66.138.91") }},
|
||||
{ String_FromConst("static.classicube.net"), { String_FromConst("172.66.134.165"), String_FromConst("172.66.138.91") }},
|
||||
{ String_FromConst("www.classicube.net"), { String_FromConst("172.66.134.165"), String_FromConst("172.66.138.91") }},
|
||||
{ String_FromConst("resources.download.minecraft.net"), { String_FromConst("13.107.213.36"), String_FromConst("13.107.246.36") }},
|
||||
{ String_FromConst("launcher.mojang.com"), { String_FromConst("13.107.213.36"), String_FromConst("13.107.246.36") }}
|
||||
};
|
||||
if (!net_supported) return ERR_NO_NETWORKING;
|
||||
|
||||
|
|
@ -76,8 +79,9 @@ static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int*
|
|||
{
|
||||
if (!String_CaselessEqualsConst(&mappings[i].host, host)) continue;
|
||||
|
||||
ParseIPv4(&mappings[i].ip, port, &addrs[0]);
|
||||
*numValidAddrs = 1;
|
||||
ParseIPv4(&mappings[i].ip[0], port, &addrs[0]);
|
||||
ParseIPv4(&mappings[i].ip[1], port, &addrs[1]);
|
||||
*numValidAddrs = 2;
|
||||
return 0;
|
||||
}
|
||||
return ERR_NOT_SUPPORTED;
|
||||
|
|
@ -104,7 +108,7 @@ cc_result Socket_GetLastError(cc_socket s) {
|
|||
u32 errSize = sizeof(error);
|
||||
|
||||
/* https://stackoverflow.com/questions/29479953/so-error-value-after-successful-socket-operation */
|
||||
net_getsockopt(s, SOL_SOCKET, SO_ERROR, &error, errSize);
|
||||
net_getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errSize);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ static void InitSockets(void) {
|
|||
char netmask[16] = {0};
|
||||
char gateway[16] = {0};
|
||||
|
||||
int ret = if_config(localip, netmask, gateway, TRUE, 20);
|
||||
int ret = if_config(localip, netmask, gateway, true);
|
||||
if (ret >= 0) {
|
||||
cc_string str; char buffer[256];
|
||||
String_InitArray_NT(str, buffer);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <ogc/lwp_watchdog.h>
|
||||
#include <fat.h>
|
||||
#include <ogc/exi.h>
|
||||
#include <ogc/system.h>
|
||||
#include <ogc/wiilaunch.h>
|
||||
|
||||
const cc_result ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */
|
||||
|
|
|
|||
|
|
@ -158,11 +158,12 @@ static void ProcessPAD_Buttons(int port, int mods) {
|
|||
static void ProcessPADInput(PADStatus* pad, int i, float delta) {
|
||||
int error = pad->err;
|
||||
|
||||
if (error == 0) {
|
||||
if (error == PAD_ERR_NONE) {
|
||||
gc_pads[i] = *pad; // new state arrived
|
||||
} else if (error == PAD_ERR_TRANSFER) {
|
||||
// usually means still busy transferring state - use last state
|
||||
} else {
|
||||
if (error == PAD_ERR_NO_CONTROLLER) PAD_Reset(PAD_CHAN0_BIT >> i);
|
||||
return; // not connected, still busy, etc
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue