diff --git a/.gitignore b/.gitignore
index e78cff7f4..fe5b37da8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,10 @@ ClassiCube*
screenshots
fontscache.txt
+# CMake files
+CMakeFiles/
+CMakeCache.txt
+
#GCC object files
*.o
diff --git a/Makefile b/Makefile
index 6211a11b9..497cd7e8d 100644
--- a/Makefile
+++ b/Makefile
@@ -138,6 +138,9 @@ vita:
$(MAKE) -f misc/vita/Makefile PLAT=vita
ps3:
$(MAKE) -f misc/ps3/Makefile PLAT=ps3
+ps1:
+ cmake --preset default misc/ps1
+ cmake --build misc/ps1/build
ps2:
$(MAKE) -f misc/ps2/Makefile PLAT=ps2
xbox:
diff --git a/misc/ps1/CMakeLists.txt b/misc/ps1/CMakeLists.txt
new file mode 100644
index 000000000..1f78b62a4
--- /dev/null
+++ b/misc/ps1/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.21)
+
+project(
+ ClassiCube
+ LANGUAGES C ASM
+ VERSION 1.0.0
+ DESCRIPTION "ClassiCube PS1 port"
+ HOMEPAGE_URL "https://classicube.net"
+)
+
+add_definitions(-DPLAT_PS1)
+file(GLOB _sources ../../src/*.c)
+
+psn00bsdk_add_executable(template GPREL ${_sources})
+
+psn00bsdk_add_cd_image(
+ iso # Target name
+ template # Output file name (= template.bin + template.cue)
+ iso.xml # Path to config file
+ DEPENDS template system.cnf
+)
diff --git a/misc/ps1/CMakePresets.json b/misc/ps1/CMakePresets.json
new file mode 100644
index 000000000..97d842820
--- /dev/null
+++ b/misc/ps1/CMakePresets.json
@@ -0,0 +1,26 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 21,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "default",
+ "displayName": "Default configuration",
+ "description": "Use this preset to build the project using PSn00bSDK.",
+ "generator": "Ninja",
+ "toolchainFile": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake",
+ "binaryDir": "${sourceDir}/build",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "PSN00BSDK_TC": "",
+ "PSN00BSDK_TARGET": "mipsel-none-elf"
+ },
+ "warnings": {
+ "dev": false
+ }
+ }
+ ]
+}
diff --git a/misc/ps1/iso.xml b/misc/ps1/iso.xml
new file mode 100644
index 000000000..96ea23a8c
--- /dev/null
+++ b/misc/ps1/iso.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
diff --git a/misc/ps1/system.cnf b/misc/ps1/system.cnf
new file mode 100644
index 000000000..e22172615
--- /dev/null
+++ b/misc/ps1/system.cnf
@@ -0,0 +1,4 @@
+BOOT=cdrom:\template.exe;1
+TCB=4
+EVENT=10
+STACK=801FFFF0
diff --git a/src/Core.h b/src/Core.h
index 2474e51f5..2ac752cac 100644
--- a/src/Core.h
+++ b/src/Core.h
@@ -118,6 +118,7 @@ typedef cc_uint8 cc_bool;
#endif
+#define CC_BUILD_NETWORKING
#define CC_BUILD_FREETYPE
#ifndef CC_BUILD_FLATPAK
#define CC_BUILD_RESOURCES
@@ -329,6 +330,7 @@ typedef cc_uint8 cc_bool;
#define CC_BUILD_CONSOLE
#undef CC_BUILD_FREETYPE
#undef CC_BUILD_RESOURCES
+ #undef CC_BUILD_NETWORKING
#elif defined PLAT_PS2
#define CC_BUILD_PS2
#define CC_BUILD_OPENAL
@@ -368,6 +370,17 @@ typedef cc_uint8 cc_bool;
#define CC_BUILD_GLES
#define CC_BUILD_EGL
#undef CC_BUILD_FREETYPE
+#elif defined PLAT_PS1
+ #define CC_BUILD_PS1
+ #define CC_BUILD_HTTPCLIENT
+ #define CC_BUILD_COOPTHREADED
+ #define CC_BUILD_LOWMEM
+ #define CC_BUILD_CONSOLE
+ #define CC_BUILD_NOMUSIC
+ #define CC_BUILD_NOSOUNDS
+ #undef CC_BUILD_FREETYPE
+ #undef CC_BUILD_RESOURCES
+ #undef CC_BUILD_NETWORKING
#endif
#endif
diff --git a/src/ExtMath.c b/src/ExtMath.c
index 5081d706f..6196c6785 100644
--- a/src/ExtMath.c
+++ b/src/ExtMath.c
@@ -4,8 +4,20 @@
/* For abs(x) function */
#include
-#ifndef __GNUC__
+#if defined PLAT_PS1
+#include
+float Math_AbsF(float x) { return __builtin_fabsf(x); }
+
+float Math_SqrtF(float x) {
+ int fp_x = (int)(x * (1 << 12));
+ fp_x = SquareRoot12(fp_x);
+ return (float)fp_x / (1 << 12);
+}
+#elif defined __GNUC__
+/* Defined in .h using builtins */
+#else
#include
+
float Math_AbsF(float x) { return fabsf(x); /* MSVC intrinsic */ }
float Math_SqrtF(float x) { return sqrtf(x); /* MSVC intrinsic */ }
#endif
diff --git a/src/ExtMath.h b/src/ExtMath.h
index a176d5551..4907a416f 100644
--- a/src/ExtMath.h
+++ b/src/ExtMath.h
@@ -14,7 +14,7 @@
#define Math_Deg2Packed(x) ((cc_uint8)((x) * 256.0f / 360.0f))
#define Math_Packed2Deg(x) ((x) * 360.0f / 256.0f)
-#ifdef __GNUC__
+#if defined __GNUC__ && !defined CC_PLAT_PS1
/* fabsf/sqrtf are single intrinsic instructions in gcc/clang */
/* (sqrtf is only when -fno-math-errno though) */
#define Math_AbsF(x) __builtin_fabsf(x)
diff --git a/src/Graphics_PS1.c b/src/Graphics_PS1.c
new file mode 100644
index 000000000..8b427d81f
--- /dev/null
+++ b/src/Graphics_PS1.c
@@ -0,0 +1,558 @@
+#include "Core.h"
+#if defined CC_BUILD_PS1
+#include "_GraphicsBase.h"
+#include "Errors.h"
+#include "Window.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+// Based off https://github.com/Lameguy64/PSn00bSDK/blob/master/examples/beginner/hello/main.c
+
+
+// Length of the ordering table, i.e. the range Z coordinates can have, 0-15 in
+// this case. Larger values will allow for more granularity with depth (useful
+// when drawing a complex 3D scene) at the expense of RAM usage and performance.
+#define OT_LENGTH 1024
+
+// Size of the buffer GPU commands and primitives are written to. If the program
+// crashes due to too many primitives being drawn, increase this value.
+#define BUFFER_LENGTH 8192
+
+typedef struct {
+ DISPENV disp_env;
+ DRAWENV draw_env;
+
+ cc_uint32 ot[OT_LENGTH];
+ cc_uint8 buffer[BUFFER_LENGTH];
+} RenderBuffer;
+
+static RenderBuffer buffers[2];
+static cc_uint8* next_packet;
+static int active_buffer;
+static RenderBuffer* buffer;
+
+static void OnBufferUpdated(void) {
+ buffer = &buffers[active_buffer];
+ next_packet = buffer->buffer;
+ ClearOTagR(buffer->ot, OT_LENGTH);
+}
+
+static void SetupContexts(int w, int h, int r, int g, int b) {
+ SetDefDrawEnv(&buffers[0].draw_env, 0, 0, w, h);
+ SetDefDispEnv(&buffers[0].disp_env, 0, 0, w, h);
+ SetDefDrawEnv(&buffers[1].draw_env, 0, h, w, h);
+ SetDefDispEnv(&buffers[1].disp_env, 0, h, w, h);
+
+ setRGB0(&buffers[0].draw_env, r, g, b);
+ setRGB0(&buffers[1].draw_env, r, g, b);
+ buffers[0].draw_env.isbg = 1;
+ buffers[1].draw_env.isbg = 1;
+
+ active_buffer = 0;
+ OnBufferUpdated();
+}
+
+static void FlipBuffers(void) {
+ DrawSync(0);
+ VSync(0);
+
+ RenderBuffer* draw_buffer = &buffers[active_buffer];
+ RenderBuffer* disp_buffer = &buffers[active_buffer ^ 1];
+
+ PutDispEnv(&disp_buffer->disp_env);
+ DrawOTagEnv(&draw_buffer->ot[OT_LENGTH - 1], &draw_buffer->draw_env);
+
+ active_buffer ^= 1;
+ OnBufferUpdated();
+}
+
+static void* new_primitive(int size) {
+ RenderBuffer* buffer = &buffers[active_buffer];
+ uint8_t* prim = next_packet;
+
+ next_packet += size;
+
+ assert(next_packet <= &buffer->buffer[BUFFER_LENGTH]);
+ return (void*)prim;
+}
+
+void Gfx_RestoreState(void) {
+ InitDefaultResources();
+}
+
+void Gfx_FreeState(void) {
+ FreeDefaultResources();
+}
+
+void Gfx_Create(void) {
+ Gfx.MaxTexWidth = 128;
+ Gfx.MaxTexHeight = 128;
+ Gfx.Created = true;
+
+ Gfx_RestoreState();
+ ResetGraph(0);
+
+ SetupContexts(Window_Main.Width, Window_Main.Height, 63, 0, 127);
+ SetDispMask(1);
+
+ InitGeom();
+ //gte_SetGeomOffset(Window_Main.Width / 2, Window_Main.Height / 2);
+ // Set screen depth (basically FOV control, W/2 works best)
+ //gte_SetGeomScreen(Window_Main.Width / 2);
+}
+
+void Gfx_Free(void) {
+ Gfx_FreeState();
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Textures--------------------------------------------------------*
+*#########################################################################################################################*/
+static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) {
+ return NULL;
+}
+
+void Gfx_BindTexture(GfxResourceID texId) {
+}
+
+void Gfx_DeleteTexture(GfxResourceID* texId) {
+ GfxResourceID data = *texId;
+ if (data) Mem_Free(data);
+ *texId = NULL;
+}
+
+void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
+ // TODO
+}
+
+void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) {
+ // TODO
+}
+
+void Gfx_EnableMipmaps(void) { }
+void Gfx_DisableMipmaps(void) { }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------State management---------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetFog(cc_bool enabled) { }
+void Gfx_SetFogCol(PackedCol col) { }
+void Gfx_SetFogDensity(float value) { }
+void Gfx_SetFogEnd(float value) { }
+void Gfx_SetFogMode(FogFunc func) { }
+
+void Gfx_SetFaceCulling(cc_bool enabled) {
+ // TODO
+}
+
+void Gfx_SetAlphaTest(cc_bool enabled) {
+}
+
+void Gfx_SetAlphaBlending(cc_bool enabled) {
+}
+
+void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
+
+void Gfx_ClearBuffers(GfxBuffers buffers) {
+}
+
+void Gfx_ClearColor(PackedCol color) {
+ int r = PackedCol_R(color);
+ int g = PackedCol_G(color);
+ int b = PackedCol_B(color);
+
+ setRGB0(&buffers[0].draw_env, r, g, b);
+ setRGB0(&buffers[1].draw_env, r, g, b);
+}
+
+void Gfx_SetDepthTest(cc_bool enabled) {
+}
+
+void Gfx_SetDepthWrite(cc_bool enabled) {
+ // TODO
+}
+
+static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
+ // TODO
+}
+
+void Gfx_DepthOnlyRendering(cc_bool depthOnly) {
+ cc_bool enabled = !depthOnly;
+ SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1],
+ enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]);
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Index buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) {
+ return (void*)1;
+}
+
+void Gfx_BindIb(GfxResourceID ib) { }
+void Gfx_DeleteIb(GfxResourceID* ib) { }
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Vertex buffers----------------------------------------------------*
+*#########################################################################################################################*/
+static void* gfx_vertices;
+
+static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) {
+ return Mem_TryAlloc(count, strideSizes[fmt]);
+}
+
+void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; }
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+ GfxResourceID data = *vb;
+ if (data) Mem_Free(data);
+ *vb = 0;
+}
+
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return vb;
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) {
+ gfx_vertices = vb;
+}
+
+
+static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) {
+ return Mem_TryAlloc(maxVertices, strideSizes[fmt]);
+}
+
+void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); }
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return vb;
+}
+
+void Gfx_UnlockDynamicVb(GfxResourceID vb) {
+ gfx_vertices = vb;
+}
+
+void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); }
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+static struct Matrix _view, _proj, mvp;
+#define ToFixed(v) (int)(v * (1 << 12))
+
+static void LoadTransformMatrix(struct Matrix* src) {
+ // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati
+ MATRIX mtx;
+
+ mtx.t[0] = 0;
+ mtx.t[1] = 0;
+ mtx.t[2] = 0;
+
+ //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row1.x, &src->row1.y, &src->row1.z);
+ //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row2.x, &src->row2.y, &src->row2.z);
+ //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row3.x, &src->row3.y, &src->row3.z);
+ //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row4.x, &src->row4.y, &src->row4.z);
+ //Platform_LogConst("====");
+
+ float len1 = Math_SqrtF(src->row1.x + src->row1.y + src->row1.z);
+ float len2 = Math_SqrtF(src->row2.x + src->row2.y + src->row2.z);
+ float len3 = Math_SqrtF(src->row3.x + src->row3.y + src->row3.z);
+
+ mtx.m[0][0] = ToFixed(1);
+ mtx.m[0][1] = 0;
+ mtx.m[0][2] = 0;
+
+ mtx.m[1][0] = 0;
+ mtx.m[1][1] = ToFixed(1);
+ mtx.m[1][2] = 0;
+
+ mtx.m[2][0] = 0;
+ mtx.m[2][1] = ToFixed(1);
+ mtx.m[2][2] = 1;
+
+ gte_SetRotMatrix(&mtx);
+ gte_SetTransMatrix(&mtx);
+}
+
+/*static void LoadTransformMatrix(struct Matrix* src) {
+ // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati
+ MATRIX mtx;
+
+ mtx.t[0] = ToFixed(src->row4.x);
+ mtx.t[1] = ToFixed(src->row4.y);
+ mtx.t[2] = ToFixed(src->row4.z);
+
+ Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row1.x, &src->row1.y, &src->row1.z);
+ Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row2.x, &src->row2.y, &src->row2.z);
+ Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row3.x, &src->row3.y, &src->row3.z);
+ Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row4.x, &src->row4.y, &src->row4.z);
+ Platform_LogConst("====");
+
+ float len1 = Math_SqrtF(src->row1.x + src->row1.y + src->row1.z);
+ float len2 = Math_SqrtF(src->row2.x + src->row2.y + src->row2.z);
+ float len3 = Math_SqrtF(src->row3.x + src->row3.y + src->row3.z);
+
+ mtx.m[0][0] = ToFixed(src->row1.x / len1);
+ mtx.m[0][1] = ToFixed(src->row1.y / len1);
+ mtx.m[0][2] = ToFixed(src->row1.z / len1);
+
+ mtx.m[1][0] = ToFixed(src->row2.x / len2);
+ mtx.m[1][1] = ToFixed(src->row2.y / len2);
+ mtx.m[1][2] = ToFixed(src->row2.z / len2);
+
+ mtx.m[2][0] = ToFixed(src->row3.x / len3);
+ mtx.m[2][1] = ToFixed(src->row3.y / len3);
+ mtx.m[2][2] = ToFixed(src->row3.z / len3);
+
+ gte_SetRotMatrix(&mtx);
+ gte_SetTransMatrix(&mtx);
+}*/
+
+void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
+ if (type == MATRIX_VIEW) _view = *matrix;
+ if (type == MATRIX_PROJECTION) _proj = *matrix;
+
+ Matrix_Mul(&mvp, &_view, &_proj);
+ LoadTransformMatrix(&mvp);
+}
+
+void Gfx_LoadIdentityMatrix(MatrixType type) {
+ Gfx_LoadMatrix(type, &Matrix_Identity);
+}
+
+void Gfx_EnableTextureOffset(float x, float y) {
+ // TODO
+}
+
+void Gfx_DisableTextureOffset(void) {
+ // TODO
+}
+
+void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
+ /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */
+ /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */
+ /* NOTE: This calculation is shared with Direct3D 11 backend */
+ *matrix = Matrix_Identity;
+
+ matrix->row1.x = 2.0f / width;
+ matrix->row2.y = -2.0f / height;
+ matrix->row3.z = 1.0f / (zNear - zFar);
+
+ matrix->row4.x = -1.0f;
+ matrix->row4.y = 1.0f;
+ matrix->row4.z = zNear / (zNear - zFar);
+}
+
+static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
+void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
+ float zNear = 0.01f;
+ /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */
+ float c = (float)Cotangent(0.5f * fov);
+ *matrix = Matrix_Identity;
+
+ matrix->row1.x = c / aspect;
+ matrix->row2.y = c;
+ matrix->row3.z = zFar / (zNear - zFar);
+ matrix->row3.w = -1.0f;
+ matrix->row4.z = (zNear * zFar) / (zNear - zFar);
+ matrix->row4.w = 0.0f;
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Rendering-------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetVertexFormat(VertexFormat fmt) {
+ gfx_format = fmt;
+ gfx_stride = strideSizes[fmt];
+}
+
+void Gfx_DrawVb_Lines(int verticesCount) {
+
+}
+
+static void Transform(Vec3* result, struct VertexTextured* a, const struct Matrix* mat) {
+ /* a could be pointing to result - therefore can't directly assign X/Y/Z */
+ float x = a->x * mat->row1.x + a->y * mat->row2.x + a->z * mat->row3.x + mat->row4.x;
+ float y = a->x * mat->row1.y + a->y * mat->row2.y + a->z * mat->row3.y + mat->row4.y;
+ float z = a->x * mat->row1.z + a->y * mat->row2.z + a->z * mat->row3.z + mat->row4.z;
+
+ result->x = x * (320/2) + (320/2);
+ result->y = y * -(240/2) + (240/2);
+ result->z = z * OT_LENGTH;
+}
+
+cc_bool VERTEX_LOGGING;
+static void DrawQuads(int verticesCount, int startVertex) {
+ for (int i = 0; i < verticesCount; i += 4)
+ {
+ struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i;
+
+ POLY_F4* poly = new_primitive(sizeof(POLY_F4));
+ setPolyF4(poly);
+
+ Vec3 coords[4];
+ Transform(&coords[0], &v[0], &mvp);
+ Transform(&coords[1], &v[1], &mvp);
+ Transform(&coords[2], &v[2], &mvp);
+ Transform(&coords[3], &v[3], &mvp);
+
+ poly->x0 = coords[1].x; poly->y0 = coords[1].y;
+ poly->x1 = coords[0].x; poly->y1 = coords[0].y;
+ poly->x2 = coords[2].x; poly->y2 = coords[2].y;
+ poly->x3 = coords[3].x; poly->y3 = coords[3].y;
+
+ int X = v[0].x, Y = v[0].y, Z = v[0].z;
+ if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z);
+ X = poly->x1; Y = poly->y1, Z = coords[0].z;
+
+ poly->r0 = PackedCol_R(v->Col);
+ poly->g0 = PackedCol_G(v->Col);
+ poly->b0 = PackedCol_B(v->Col);
+
+ int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4;
+ if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p);
+
+ if (p < 0 || p >= OT_LENGTH) continue;
+ addPrim(&buffer->ot[p >> 2], poly);
+ }
+}
+
+/*static void DrawQuads(int verticesCount, int startVertex) {
+ for (int i = 0; i < verticesCount; i += 4)
+ {
+ struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i;
+
+ POLY_F4* poly = new_primitive(sizeof(POLY_F4));
+ setPolyF4(poly);
+
+ SVECTOR coords[4];
+ coords[0].vx = v[0].x; coords[0].vy = v[0].y; coords[0].vz = v[0].z;
+ coords[1].vx = v[1].x; coords[1].vy = v[1].y; coords[1].vz = v[1].z;
+ coords[2].vx = v[2].x; coords[2].vy = v[2].y; coords[2].vz = v[1].z;
+ coords[3].vx = v[3].x; coords[3].vy = v[3].y; coords[3].vz = v[3].z;
+
+ int X = coords[0].vx, Y = coords[0].vy, Z = coords[0].vz;
+ //Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z);
+ gte_ldv3(&coords[0], &coords[1], &coords[2]);
+ gte_rtpt();
+ gte_stsxy0(&poly->x0);
+
+ int p;
+ gte_avsz3();
+ gte_stotz( &p );
+
+ X = poly->x0; Y = poly->y0, Z = p;
+ //Platform_Log3("OUT: %i, %i, %i", &X, &Y, &Z);
+ if (((p >> 2) >= OT_LENGTH) || ((p >> 2) < 0))
+ continue;
+
+ gte_ldv0(&coords[3]);
+ gte_rtps();
+ gte_stsxy3(&poly->x1, &poly->x2, &poly->x3);
+
+ //poly->x0 = v[1].x; poly->y0 = v[1].y;
+ //poly->x1 = v[0].x; poly->y1 = v[0].y;
+ //poly->x2 = v[2].x; poly->y2 = v[2].y;
+ //poly->x3 = v[3].x; poly->y3 = v[3].y;
+
+ poly->r0 = PackedCol_R(v->Col);
+ poly->g0 = PackedCol_G(v->Col);
+ poly->b0 = PackedCol_B(v->Col);
+
+ addPrim(&buffer->ot[p >> 2], poly);
+ }
+}*/
+
+/*static void DrawQuads(int verticesCount, int startVertex) {
+ for (int i = 0; i < verticesCount; i += 4)
+ {
+ struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i;
+
+ POLY_F4* poly = new_primitive(sizeof(POLY_F4));
+ setPolyF4(poly);
+
+ poly->x0 = v[1].x; poly->y0 = v[1].y;
+ poly->x1 = v[0].x; poly->y1 = v[0].y;
+ poly->x2 = v[2].x; poly->y2 = v[2].y;
+ poly->x3 = v[3].x; poly->y3 = v[3].y;
+
+ poly->r0 = PackedCol_R(v->Col);
+ poly->g0 = PackedCol_G(v->Col);
+ poly->b0 = PackedCol_B(v->Col);
+
+ int p = 0;
+ addPrim(&buffer->ot[p >> 2], poly);
+ }
+}*/
+
+void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
+ if (gfx_format == VERTEX_FORMAT_COLOURED) return;
+ DrawQuads(verticesCount, startVertex);
+}
+
+void Gfx_DrawVb_IndexedTris(int verticesCount) {
+ if (gfx_format == VERTEX_FORMAT_COLOURED) return;
+ DrawQuads(verticesCount, 0);
+}
+
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
+ DrawQuads(verticesCount, startVertex);
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Other/Misc------------------------------------------------------*
+*#########################################################################################################################*/
+cc_result Gfx_TakeScreenshot(struct Stream* output) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_bool Gfx_WarnIfNecessary(void) {
+ return false;
+}
+
+void Gfx_BeginFrame(void) {
+ // Draw the square by allocating a TILE (i.e. untextured solid color
+ // rectangle) primitive at Z = 1.
+ TILE *tile = (TILE *)new_primitive(sizeof(TILE));
+
+ setTile(tile);
+ setXY0 (tile, 40, 40);
+ setWH (tile, 64, 64);
+ setRGB0(tile, 255, 255, 0);
+ addPrim(&buffer->ot[1 >> 2], tile);
+}
+
+void Gfx_EndFrame(void) {
+ FlipBuffers();
+ if (gfx_minFrameMs) LimitFPS();
+}
+
+void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+ gfx_minFrameMs = minFrameMs;
+ gfx_vsync = vsync;
+}
+
+void Gfx_OnWindowResize(void) {
+ // TODO
+}
+
+void Gfx_GetApiInfo(cc_string* info) {
+ String_AppendConst(info, "-- Using PS1 --\n");
+ PrintMaxTextureInfo(info);
+}
+
+cc_bool Gfx_TryRestoreContext(void) { return true; }
+#endif
diff --git a/src/Platform_PS1.c b/src/Platform_PS1.c
new file mode 100644
index 000000000..e654127c9
--- /dev/null
+++ b/src/Platform_PS1.c
@@ -0,0 +1,228 @@
+#include "Core.h"
+#if defined PLAT_PS1
+
+#include "_PlatformBase.h"
+#include "Stream.h"
+#include "ExtMath.h"
+#include "Funcs.h"
+#include "Window.h"
+#include "Utils.h"
+#include "Errors.h"
+#include "PackedCol.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+void exit(int code) { } // TODO how to fix
+#include "_PlatformConsole.h"
+
+const cc_result ReturnCode_FileShareViolation = 1000000000; // not used
+const cc_result ReturnCode_FileNotFound = 99999;
+const cc_result ReturnCode_DirectoryExists = 99999;
+
+const cc_result ReturnCode_SocketInProgess = -1;
+const cc_result ReturnCode_SocketWouldBlock = -1;
+const char* Platform_AppNameSuffix = " PS1";
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Logging/Time-------------------------------------------------------*
+*#########################################################################################################################*/
+void Platform_Log(const char* msg, int len) {
+ char tmp[2048 + 1];
+ len = min(len, 2048);
+ Mem_Copy(tmp, msg, len); tmp[len] = '\0';
+
+ printf("%s\n", tmp);
+}
+
+#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000))
+TimeMS DateTime_CurrentUTC_MS(void) {
+ return 0;
+}
+
+void DateTime_CurrentLocal(struct DateTime* t) {
+ Mem_Set(t, 0, sizeof(struct DateTime));
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------------Stopwatch--------------------------------------------------------*
+*#########################################################################################################################*/
+static volatile cc_uint32 irq_count;
+
+cc_uint64 Stopwatch_Measure(void) {
+ return irq_count;
+}
+
+cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) {
+ if (end < beg) return 0;
+ return (end - beg) * 1000;
+}
+
+static void timer2_handler(void) { irq_count++; }
+
+static void Stopwatch_Init(void) {
+ TIMER_CTRL(2) = 0x0258; // CLK/8 input, IRQ on reload
+ TIMER_RELOAD(2) = (F_CPU / 8) / 1000; // 1000 Hz
+
+ EnterCriticalSection();
+ InterruptCallback(IRQ_TIMER2, &timer2_handler);
+ ExitCriticalSection();
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------Directory/File------------------------------------------------------*
+*#########################################################################################################################*/
+cc_result Directory_Create(const cc_string* path) {
+ return ERR_NOT_SUPPORTED;
+}
+
+int File_Exists(const cc_string* path) {
+ return 0;
+}
+
+cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Open(cc_file* file, const cc_string* path) {
+ return ERR_NOT_SUPPORTED;
+}
+cc_result File_Create(cc_file* file, const cc_string* path) {
+ return ERR_NOT_SUPPORTED;
+}
+cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Close(cc_file file) {
+ return 0;
+}
+
+cc_result File_Seek(cc_file file, int offset, int seekType) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Position(cc_file file, cc_uint32* pos) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result File_Length(cc_file file, cc_uint32* len) {
+ return ERR_NOT_SUPPORTED;
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------------Threading--------------------------------------------------------*
+*#########################################################################################################################*/
+void Thread_Sleep(cc_uint32 milliseconds) {
+ // TODO sleep a bit
+ VSync(0);
+}
+
+void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) {
+ *handle = NULL;
+}
+
+void Thread_Detach(void* handle) {
+}
+
+void Thread_Join(void* handle) {
+}
+
+void* Mutex_Create(void) {
+ return NULL;
+}
+
+void Mutex_Free(void* handle) {
+}
+
+void Mutex_Lock(void* handle) {
+}
+
+void Mutex_Unlock(void* handle) {
+}
+
+void* Waitable_Create(void) {
+ return NULL;
+}
+
+void Waitable_Free(void* handle) {
+}
+
+void Waitable_Signal(void* handle) {
+}
+
+void Waitable_Wait(void* handle) {
+}
+
+void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Socket----------------------------------------------------------*
+*#########################################################################################################################*/
+cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
+ return ERR_NOT_SUPPORTED;
+}
+
+void Socket_Close(cc_socket s) {
+}
+
+cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) {
+ return ERR_NOT_SUPPORTED;
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------------Platform---------------------------------------------------------*
+*#########################################################################################################################*/
+void Platform_Init(void) {
+ ResetGraph( 0 );
+ Stopwatch_Init();
+}
+
+void Platform_Free(void) { }
+
+cc_bool Platform_DescribeError(cc_result res, cc_string* dst) {
+ return false;
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Encryption--------------------------------------------------------*
+*#########################################################################################################################*/
+static cc_result GetMachineID(cc_uint32* key) {
+ return ERR_NOT_SUPPORTED;
+}
+#endif
diff --git a/src/Program.c b/src/Program.c
index fef95a37f..104c9bfda 100644
--- a/src/Program.c
+++ b/src/Program.c
@@ -168,4 +168,4 @@ int main(int argc, char** argv) {
Process_Exit(res);
return res;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/Protocol.c b/src/Protocol.c
index e4d45ba39..0459a32c7 100644
--- a/src/Protocol.c
+++ b/src/Protocol.c
@@ -1,9 +1,10 @@
#include "Protocol.h"
+#include "Game.h"
+#ifdef CC_BUILD_NETWORKING
#include "String.h"
#include "Deflate.h"
#include "Server.h"
#include "Stream.h"
-#include "Game.h"
#include "Entity.h"
#include "Platform.h"
#include "Screens.h"
@@ -1872,6 +1873,13 @@ static void OnReset(void) {
Protocol_Reset();
FreeMapStates();
}
+#else
+void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct RayTracer* t) { }
+
+static void OnInit(void) { }
+
+static void OnReset(void) { }
+#endif
struct IGameComponent Protocol_Component = {
OnInit, /* Init */
diff --git a/src/Server.c b/src/Server.c
index cd13c2cbd..3d1b8dad3 100644
--- a/src/Server.c
+++ b/src/Server.c
@@ -128,7 +128,7 @@ static void SPConnection_BeginConnect(void) {
Random_SeedFromCurrentTime(&rnd);
World_NewMap();
-#if defined CC_BUILD_NDS
+#if defined CC_BUILD_NDS || defined CC_BUILD_PS1
World_SetDimensions(16, 16, 16);
#elif defined CC_BUILD_LOWMEM
World_SetDimensions(64, 64, 64);
@@ -136,7 +136,7 @@ static void SPConnection_BeginConnect(void) {
World_SetDimensions(128, 64, 128);
#endif
-#if defined CC_BUILD_N64 || defined CC_BUILD_NDS
+#if defined CC_BUILD_N64 || defined CC_BUILD_NDS || defined CC_BUILD_PS1
Gen_Active = &FlatgrassGen;
#else
Gen_Active = &NotchyGen;
@@ -223,10 +223,12 @@ static void SPConnection_Init(void) {
*--------------------------------------------------Multiplayer connection-------------------------------------------------*
*#########################################################################################################################*/
static cc_socket net_socket = -1;
+static cc_result net_writeFailure;
+static void OnClose(void);
+
+#ifdef CC_BUILD_NETWORKING
static cc_uint8 net_readBuffer[4096 * 5];
static cc_uint8* net_readCurrent;
-
-static cc_result net_writeFailure;
static double net_lastPacket;
static cc_uint8 lastOpcode;
@@ -234,7 +236,6 @@ static cc_bool net_connecting;
static double net_connectTimeout;
#define NET_TIMEOUT_SECS 15
-static void OnClose(void);
static void MPConnection_FinishConnect(void) {
net_connecting = false;
Event_RaiseVoid(&NetEvents.Connected);
@@ -475,8 +476,14 @@ static void MPConnection_Init(void) {
Server.SendData = MPConnection_SendData;
net_readCurrent = net_readBuffer;
}
+#else
+static void MPConnection_Init(void) { SPConnection_Init(); }
+#endif
+/*########################################################################################################################*
+*---------------------------------------------------Component interface---------------------------------------------------*
+*#########################################################################################################################*/
static void OnNewMap(void) {
int i;
if (Server.IsSinglePlayer) return;
diff --git a/src/Window_PS1.c b/src/Window_PS1.c
new file mode 100644
index 000000000..df1fa58d1
--- /dev/null
+++ b/src/Window_PS1.c
@@ -0,0 +1,198 @@
+#include "Core.h"
+#if defined CC_BUILD_PS1
+#include "Window.h"
+#include "Platform.h"
+#include "Input.h"
+#include "Event.h"
+#include "Graphics.h"
+#include "String.h"
+#include "Funcs.h"
+#include "Bitmap.h"
+#include "Errors.h"
+#include "ExtMath.h"
+#include "Logger.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+static cc_bool launcherMode;
+static char pad_buff[2][34];
+
+struct _DisplayData DisplayInfo;
+struct _WindowData WindowInfo;
+
+void Window_Init(void) {
+ DisplayInfo.Width = SCREEN_XRES;
+ DisplayInfo.Height = SCREEN_YRES;
+ DisplayInfo.Depth = 4; // 32 bit
+ DisplayInfo.ScaleX = 0.5f;
+ DisplayInfo.ScaleY = 0.5f;
+
+ Window_Main.Width = DisplayInfo.Width;
+ Window_Main.Height = DisplayInfo.Height;
+ Window_Main.Focused = true;
+ Window_Main.Exists = true;
+
+ Input.Sources = INPUT_SOURCE_GAMEPAD;
+ DisplayInfo.ContentOffsetX = 10;
+ DisplayInfo.ContentOffsetY = 10;
+
+// http://lameguy64.net/tutorials/pstutorials/chapter1/4-controllers.html
+ InitPAD(&pad_buff[0][0], 34, &pad_buff[1][0], 34);
+ pad_buff[0][0] = pad_buff[0][1] = 0xff;
+ pad_buff[1][0] = pad_buff[1][1] = 0xff;
+ StartPAD();
+ ChangeClearPAD(0);
+}
+
+void Window_Free(void) { }
+
+void Window_Create3D(int width, int height) {
+ launcherMode = false;
+}
+
+void Window_SetTitle(const cc_string* title) { }
+void Clipboard_GetText(cc_string* value) { }
+void Clipboard_SetText(const cc_string* value) { }
+
+int Window_GetWindowState(void) { return WINDOW_STATE_FULLSCREEN; }
+cc_result Window_EnterFullscreen(void) { return 0; }
+cc_result Window_ExitFullscreen(void) { return 0; }
+int Window_IsObscured(void) { return 0; }
+
+void Window_Show(void) { }
+void Window_SetSize(int width, int height) { }
+
+void Window_RequestClose(void) {
+ Event_RaiseVoid(&WindowEvents.Closing);
+}
+
+
+/*########################################################################################################################*
+*----------------------------------------------------Input processing-----------------------------------------------------*
+*#########################################################################################################################*/
+static void HandleButtons(int buttons) {
+ // Confusingly, it seems that when a bit is on, it means the button is NOT pressed
+ // So just flip the bits to make more sense
+ buttons = ~buttons;
+
+ Input_SetNonRepeatable(CCPAD_A, buttons & PAD_TRIANGLE);
+ Input_SetNonRepeatable(CCPAD_B, buttons & PAD_SQUARE);
+ Input_SetNonRepeatable(CCPAD_X, buttons & PAD_CROSS);
+ Input_SetNonRepeatable(CCPAD_Y, buttons & PAD_CIRCLE);
+
+ Input_SetNonRepeatable(CCPAD_START, buttons & PAD_START);
+ Input_SetNonRepeatable(CCPAD_SELECT, buttons & PAD_SELECT);
+
+ Input_SetNonRepeatable(CCPAD_LEFT, buttons & PAD_LEFT);
+ Input_SetNonRepeatable(CCPAD_RIGHT, buttons & PAD_RIGHT);
+ Input_SetNonRepeatable(CCPAD_UP, buttons & PAD_UP);
+ Input_SetNonRepeatable(CCPAD_DOWN, buttons & PAD_DOWN);
+
+ Input_SetNonRepeatable(CCPAD_L, buttons & PAD_L1);
+ Input_SetNonRepeatable(CCPAD_R, buttons & PAD_R1);
+ Input_SetNonRepeatable(CCPAD_ZL, buttons & PAD_L2);
+ Input_SetNonRepeatable(CCPAD_ZR, buttons & PAD_R2);
+}
+
+static void ProcessPadInput(PADTYPE* pad, double delta) {
+ HandleButtons(pad->btn);
+}
+
+void Window_ProcessEvents(double delta) {
+ PADTYPE* pad = (PADTYPE*)&pad_buff[0][0];
+ if (pad->stat == 0) ProcessPadInput(pad, delta);
+}
+
+void Cursor_SetPosition(int x, int y) { } // Makes no sense for PS Vita
+
+void Window_EnableRawMouse(void) { Input.RawMode = true; }
+void Window_UpdateRawMouse(void) { }
+void Window_DisableRawMouse(void) { Input.RawMode = false; }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Framebuffer--------------------------------------------------------*
+*#########################################################################################################################*/
+void Window_Create2D(int width, int height) {
+ launcherMode = true;
+}
+
+static DISPENV disp;
+static cc_uint16* fb;
+
+void Window_AllocFramebuffer(struct Bitmap* bmp) {
+ SetDefDispEnv(&disp, 0, 0, SCREEN_XRES, SCREEN_YRES);
+ disp.isinter = 1;
+
+ PutDispEnv(&disp);
+ SetDispMask(1);
+
+ bmp->scan0 = (BitmapCol*)Mem_Alloc(bmp->width * bmp->height, 4, "window pixels");
+ fb = Mem_Alloc(bmp->width * bmp->height, 2, "real surface");
+}
+
+#define BGRA8_to_PS1(src) \
+ ((src[2] & 0xF8) >> 3) | ((src[1] & 0xF8) << 2) | ((src[0] & 0xF8) << 7) | 0x8000
+
+void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
+ RECT rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = SCREEN_XRES;
+ rect.h = SCREEN_YRES;
+
+ for (int y = r.y; y < r.y + r.Height; y++)
+ {
+ cc_uint32* src = bmp->scan0 + y * bmp->width;
+ cc_uint16* dst = fb + y * bmp->width;
+
+ for (int x = r.x; x < r.x + r.Width; x++) {
+ cc_uint8* color = (cc_uint8*)&src[x];
+ dst[x] = BGRA8_to_PS1(color);
+ }
+ }
+
+ LoadImage(&rect, fb);
+ DrawSync(0);
+}
+
+void Window_FreeFramebuffer(struct Bitmap* bmp) {
+ Mem_Free(bmp->scan0);
+ Mem_Free(fb);
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Soft keyboard------------------------------------------------------*
+*#########################################################################################################################*/
+void Window_OpenKeyboard(struct OpenKeyboardArgs* args) { /* TODO implement */ }
+void Window_SetKeyboardText(const cc_string* text) { }
+void Window_CloseKeyboard(void) { /* TODO implement */ }
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Misc/Other--------------------------------------------------------*
+*#########################################################################################################################*/
+void Window_ShowDialog(const char* title, const char* msg) {
+ /* TODO implement */
+ Platform_LogConst(title);
+ Platform_LogConst(msg);
+}
+
+cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) {
+ return ERR_NOT_SUPPORTED;
+}
+
+cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) {
+ return ERR_NOT_SUPPORTED;
+}
+#endif
diff --git a/src/_PlatformConsole.h b/src/_PlatformConsole.h
index 9845244ae..65a4c2dab 100644
--- a/src/_PlatformConsole.h
+++ b/src/_PlatformConsole.h
@@ -70,6 +70,11 @@ int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* arg
// Consoles *sometimes* doesn't use argv[0] for program name and so argc will be 0
// (e.g. when running via some emulators)
if (!argc) return 0;
+
+#ifdef CC_BUILD_PS1
+ // When running in DuckStation at least, argv was a five element array of empty strings ???
+ return 0;
+#endif
argc--; argv++; // skip executable path argument
@@ -215,4 +220,4 @@ cc_result Platform_Decrypt(const void* data, int len, cc_string* dst) {
String_AppendAll(dst, header, min(dataLen, ENC_SIZE));
}
return 0;
-}
\ No newline at end of file
+}