diff --git a/src/platform/gba/Makefile b/src/platform/gba/Makefile new file mode 100644 index 0000000..2c9c2d0 --- /dev/null +++ b/src/platform/gba/Makefile @@ -0,0 +1,167 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/gba_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary data +# GRAPHICS is a list of directories containing files to be processed by grit +# +# All directories are specified relative to the project directory where +# the makefile is found +# +#--------------------------------------------------------------------------------- +TARGET := OpenLara +BUILD := build +SOURCES := . +INCLUDES := include +DATA := data +MUSIC := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O3\ + -mcpu=arm7tdmi -mtune=arm7tdmi\ + -fomit-frame-pointer\ + -ffast-math\ + $(ARCH) + +CFLAGS += $(INCLUDE) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lmm -lgba + + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBGBA) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- + + +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +ifneq ($(strip $(MUSIC)),) + export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) + BINFILES += soundbank.bin +endif + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba + + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- + +$(OUTPUT).gba : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SOURCES) : $(HFILES) + +#--------------------------------------------------------------------------------- +# The bin2o rule should be copied and modified +# for each extension used in the data directories +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +# rule to build soundbank from music files +#--------------------------------------------------------------------------------- +soundbank.bin soundbank.h : $(AUDIOFILES) +#--------------------------------------------------------------------------------- + @mmutil $^ -osoundbank.bin -hsoundbank.h + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.PHD.o %_PHD.h : %.PHD +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/src/platform/gba/OpenLara.sln b/src/platform/gba/OpenLara.sln new file mode 100644 index 0000000..5cfa43b --- /dev/null +++ b/src/platform/gba/OpenLara.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1022 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenLara", "OpenLara.vcxproj", "{990C6F40-6226-4011-B52C-FF042EBB7F15}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x64.ActiveCfg = Debug|x64 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x64.Build.0 = Debug|x64 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x86.ActiveCfg = Debug|Win32 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x86.Build.0 = Debug|Win32 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x64.ActiveCfg = Release|x64 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x64.Build.0 = Release|x64 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x86.ActiveCfg = Release|Win32 + {990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8D133CA9-7AA0-4806-9B61-82E8A3044A15} + EndGlobalSection +EndGlobal diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj new file mode 100644 index 0000000..fca9492 --- /dev/null +++ b/src/platform/gba/OpenLara.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + 15.0 + {990C6F40-6226-4011-B52C-FF042EBB7F15} + Win32Proj + OpenLara + 10.0.17763.0 + + + + Application + true + v141 + NotSet + + + Application + false + v141 + true + NotSet + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level2 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level2 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h new file mode 100644 index 0000000..da6c4ed --- /dev/null +++ b/src/platform/gba/common.h @@ -0,0 +1,269 @@ +#ifndef H_COMMON +#define H_COMMON + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +//#define USE_MODE_5 + +#ifdef USE_MODE_5 + #define WIDTH 160 + #define HEIGHT 128 + #define FRAME_WIDTH 160 + #define FRAME_HEIGHT 128 + #define PIXEL_SIZE 1 +#else // MODE_4 + #define WIDTH 240 + #define HEIGHT 160 + #define FRAME_WIDTH 240 + #define FRAME_HEIGHT 160 + #define PIXEL_SIZE 2 +#endif + +#define WND_SCALE 4 + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef signed long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef int16 Index; + +#define PI 3.14159265358979323846f +#define PIH (PI * 0.5f) +#define PI2 (PI * 2.0f) +#define DEG2RAD (PI / 180.0f) +#define RAD2DEG (180.0f / PI) + +#ifdef _WIN32 + extern uint8* LEVEL1_PHD; + + extern uint32 VRAM[WIDTH * HEIGHT]; + + #ifdef USE_MODE_5 + extern uint16 fb[WIDTH * HEIGHT]; + #else + extern uint8 fb[WIDTH * HEIGHT]; + #endif + + #define IWRAM_CODE + #define EWRAM_DATA + + #define dmaCopy(src,dst,size) memcpy(dst,src,size) + #define ALIGN4 + + struct ObjAffineSource { + int16 sX; + int16 sY; + uint16 theta; + }; + + struct ObjAffineDest { + int16 pa; + int16 pb; + int16 pc; + int16 pd; + }; + + static void ObjAffineSet(ObjAffineSource *source, ObjAffineDest *dest, int32 num, int32 offset) { + float ang = (source->theta >> 8) * PI / 128.0f; + + int32 c = int32(cosf(ang) * 16384.0f); + int32 s = int32(sinf(ang) * 16384.0f); + + dest->pa = ( source->sX * c) >> 14; + dest->pb = (-source->sX * s) >> 14; + dest->pc = ( source->sY * s) >> 14; + dest->pd = ( source->sY * c) >> 14; + } + +#else + #define ALIGN4 __attribute__ ((aligned (4))) + + extern uint32 fb; +#endif + +enum InputKey { + IK_UP, + IK_RIGHT, + IK_DOWN, + IK_LEFT, + IK_A, + IK_B, + IK_L, + IK_R, + IK_MAX +}; + +extern bool keys[IK_MAX]; + +struct Quad { + Index indices[4]; + uint16 flags; +}; + +struct Triangle { + Index indices[3]; + uint16 flags; +}; + +struct Room { + struct Info { + int32 x, z; + int32 yBottom, yTop; + }; + + struct Vertex { + int16 x, y, z; + uint16 lighting; + }; + + struct Sprite { + Index index; + uint16 texture; + }; + + struct Portal { + struct Vertex { + int16 x, y, z; + }; + + uint16 roomIndex; + Vertex n; + Vertex v[4]; + }; + + struct Sector { + uint16 floorIndex; + uint16 boxIndex; + uint8 roomBelow; + int8 floor; + uint8 roomAbove; + int8 ceiling; + }; + + struct Light { + // int32 x, y, z; + // uint16 intensity; + // uint32 radius; + uint8 dummy[18]; + }; + + struct Mesh { + // int32 x, y, z; + // uint16 rotation; + // uint16 intensity; + // uint16 meshID; + uint8 dummy[18]; + }; + + Info info; + uint32 dataSize; +/* + uint16 vCount; + Vertex* vertices; + + uint16 qCount; + Quad* quads; + + uint16 tCount; + Triangle* triangles; + + uint16 sCount; + Sprite* sprites; +*/ +}; + +struct Node { + uint32 flags; + int32 x, y, z; +}; + +struct Model { + uint16 type; + uint16 index; + uint16 mCount; + uint16 mStart; + uint32 node; + uint32 frame; + uint16 animation; +}; + +struct Texture { + uint16 attribute; + uint16 tile:14, :2; + uint8 xh0, x0, yh0, y0; + uint8 xh1, x1, yh1, y1; + uint8 xh2, x2, yh2, y2; + uint8 xh3, x3, yh3, y3; +}; + +struct Sprite { + uint16 tile; + uint8 u, v; + uint16 w, h; + int16 l, t, r, b; +}; + +struct SpriteSequence { + uint16 type; + uint16 unused; + int16 sCount; + int16 sStart; +}; + +struct Rect { + int32 x0; + int32 y0; + int32 x1; + int32 y1; +}; + +struct Vertex { + int16 x, y; + int16 z; + uint8 u, v, g, clip; +}; + +struct Face { + uint16 flags; + int16 depth; + int16 start; + int8 indices[4]; +}; + +#define MAX_VERTICES 1024 +#define MAX_FACES 384 +#define MAX_DIST (16 * 1024) + +#define FACE_TRIANGLE 0x8000 +#define FACE_COLORED 0x4000 +#define FACE_TEXTURE 0x3FFF + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +void drawGlyph(int32 index, int32 x, int32 y); +void clear(); + +#endif \ No newline at end of file diff --git a/src/platform/gba/common.iwram.cpp b/src/platform/gba/common.iwram.cpp new file mode 100644 index 0000000..8b33a6e --- /dev/null +++ b/src/platform/gba/common.iwram.cpp @@ -0,0 +1,993 @@ +#include "common.h" + +#define DIV_TABLE_SIZE 512 + +uint16 divTable[DIV_TABLE_SIZE]; + +#ifdef _WIN32 + uint8* LEVEL1_PHD; + + uint32 VRAM[WIDTH * HEIGHT]; + + #ifdef USE_MODE_5 + uint16 fb[WIDTH * HEIGHT]; + #else + uint8 fb[WIDTH * HEIGHT]; + #endif + + #define MyDiv(Number, Divisor) ((Number) / (Divisor)) +#else + uint32 fb = VRAM; + + #define MyDiv(Number, Divisor) ((Number) / (Divisor)) + //#define MyDiv(Number, Divisor) Div(Number, Divisor) +#endif + +#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) +#define FixedInvU(x) divTable[x] + +bool keys[IK_MAX] = {}; + +#if defined(USE_MODE_5) || defined(_WIN32) +uint16 palette[256]; +#endif + +uint8 lightmap[256 * 32]; + +Vertex gVertices[MAX_VERTICES]; +uint32 gVerticesCount = 0; + +EWRAM_DATA Face gFaces[MAX_FACES]; +int32 gFacesCount = 0; + +Rect clip; + +const uint8* tiles[15]; + +const uint8* curTile; + +uint16 mipMask; + +int32 fps; + +int32 camSinY; +int32 camCosY; + +int32 camX = 75162; +int32 camY = 3072 - 1024; +int32 camZ = 5000; + +Vertex* Ledges[4]; +Vertex* Redges[4]; +int32 Lindex, Rindex; + +uint16 roomsCount; +const Room* rooms; + +uint32 texturesCount; +const Texture* textures; + +const Sprite* sprites; + +uint32 spriteSequencesCount; +const SpriteSequence* spriteSequences; + +int32 seqGlyphs; + +const uint8* meshData; +const uint32* meshOffsets; + +const int32* nodes; +const Model* models; + +#ifdef WIN32 + #define INLINE inline +#else + #define INLINE __attribute__((always_inline)) inline +#endif + +int32 clamp(int32 x, int32 a, int32 b) { + return x < a ? a : (x > b ? b : x); +} + +template +inline void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; +} + +bool checkBackface(const Vertex *a, const Vertex *b, const Vertex *c) { + return (b->x - a->x) * (c->y - a->y) - + (c->x - a->x) * (b->y - a->y) <= 0; +} + +INLINE void sortVertices(Vertex *&t, Vertex *&m, Vertex *&b) { + if (t->y > m->y) swap(t, m); + if (t->y > b->y) swap(t, b); + if (m->y > b->y) swap(m, b); +} + +INLINE void sortVertices(Vertex *&t, Vertex *&m, Vertex *&o, Vertex *&b) { + if (t->y > m->y) swap(t, m); + if (o->y > b->y) swap(o, b); + if (t->y > o->y) swap(t, o); + if (m->y > b->y) swap(m, b); + if (m->y > o->y) swap(m, o); +} + +int32 classify(const Vertex* v) { + return (v->x < clip.x0 ? 1 : 0) | + (v->x > clip.x1 ? 2 : 0) | + (v->y < clip.y0 ? 4 : 0) | + (v->y > clip.y1 ? 8 : 0); +} + +void transform(int32 vx, int32 vy, int32 vz, int32 vg, int32 x, int32 y, int32 z) { +#ifdef _WIN32 + if (gVerticesCount >= MAX_VERTICES) { + DebugBreak(); + return; + } +#endif + + Vertex &res = gVertices[gVerticesCount++]; + + int32 px = vx + x; + int32 pz = vz + z; + + int32 cz = px * camSinY + pz * camCosY; + cz >>= 16; + + // znear / zfar clip + if (cz < 32 || cz > MAX_DIST) { + res.z = -1; + return; + } + + int32 py = vy + y; + +#if 0 + int32 cx = (px * camCosY - pz * camSinY) >> 16; + int32 cy = py; + + uint32 czInv = MyDiv(1 << 16, uint32(cz)); + + cx = cx * czInv; + cy = cy * czInv; +#else + int32 cx = px * camCosY - pz * camSinY; + int32 cy = py << 16; + + cx = MyDiv(cx, cz); + cy = MyDiv(cy, cz); +#endif + cy = cy * FRAME_WIDTH / FRAME_HEIGHT; + + cx = clamp(cx, -2 << 16, 2 << 16); + cy = clamp(cy, -2 << 16, 2 << 16); + + res.x = ( ( (1 << 16) + cx ) * (FRAME_WIDTH / 2) ) >> 16; + res.y = ( ( (1 << 16) + cy ) * (FRAME_HEIGHT / 2) ) >> 16; + res.z = cz; + res.clip = classify(&res); + + int32 fog = vg - ((cz * cz) >> 15); + if (fog < 0) { + fog = 0; + } + + res.g = uint32(255 - (fog >> 5)) >> 3; +} + +#define FETCH_T() curTile[(t & 0xFF00) | (t >> 24)] +#define FETCH_T_MIP() curTile[(t & 0xFF00) | (t >> 24) & mipMask] +#define FETCH_GT() lightmap[(g & 0x1F00) | FETCH_T()] +#define FETCH_G(palIndex) lightmap[(g & 0x1F00) | palIndex] +#define FETCH_GT_PAL() palette[FETCH_GT()] +#define FETCH_G_PAL(palIndex) palette[FETCH_G(palIndex)] + +struct Edge { + int32 h; + int32 x; + int32 g; + uint32 t; + int32 dx; + int32 dg; + uint32 dt; + + int32 index; + Vertex* vert[8]; + + Edge() : h(0), dx(0), dg(0), dt(0) {} + + INLINE void stepG() { + x += dx; + g += dg; + } + + INLINE void stepGT() { + x += dx; + g += dg; + t += dt; + } + + INLINE bool nextG() { + if (index == 0) { + return false; + } + + Vertex* v1 = vert[index--]; + Vertex* v2 = vert[index]; + + h = v2->y - v1->y; + x = v1->x << 16; + g = v1->g << 16; + + if (h > 1) { + uint32 d = FixedInvU(h); + + dx = d * (v2->x - v1->x); + dg = d * (v2->g - v1->g); + } + + return true; + } + + INLINE bool nextGT() { + if (index == 0) { + return false; + } + + Vertex* v1 = vert[index--]; + Vertex* v2 = vert[index]; + + h = v2->y - v1->y; + x = v1->x << 16; + g = v1->g << 16; + t = (v1->u << 24) | (v1->v << 8); + + if (h > 1) { + uint32 d = FixedInvU(h); + + dx = d * (v2->x - v1->x); + dg = d * (v2->g - v1->g); + + int32 du = d * (v2->u - v1->u); + int32 dv = d * (v2->v - v1->v); + + dt = ((du << 8) & 0xFFFF0000) | int16(dv >> 8); + } + + return true; + } +}; + +INLINE void scanlineG(uint16* buffer, int32 x1, int32 x2, uint8 palIndex, uint32 g, int32 dgdx) { + #ifdef USE_MODE_5 + uint16* pixel = buffer + x1; + int32 width = (x2 - x1); + + while (width--) + { + *pixel++ = FETCH_G_PAL(palIndex); + g += dgdx; + } + #else + if (x1 & 1) + { + uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1); + p = (p & 0x00FF) | (FETCH_G(palIndex) << 8); + g += dgdx; + x1++; + } + + int32 width = (x2 - x1) >> 1; + uint16* pixel = (uint16*)((uint8*)buffer + x1); + + dgdx <<= 1; + + if (width && (x1 & 3)) + { + uint16 p = FETCH_G(palIndex); + *pixel++ = p | (FETCH_G(palIndex) << 8); + + g += dgdx; + + width--; + } + + while (width-- > 0) + { + uint32 p = FETCH_G(palIndex); + p |= (FETCH_G(palIndex) << 8); + + g += dgdx; + + if (width-- > 0) + { + p |= (FETCH_G(palIndex) << 16); + p |= (FETCH_G(palIndex) << 24); + + g += dgdx; + + *(uint32*)pixel = p; + pixel += 2; + } else { + *(uint16*)pixel = p; + pixel += 1; + } + } + + if (x2 & 1) + { + *pixel = (*pixel & 0xFF00) | FETCH_G(palIndex); + } + #endif +} + +INLINE void scanlineGT(uint16* buffer, int32 x1, int32 x2, uint32 g, uint32 t, int32 dgdx, uint32 dtdx) { + #ifdef USE_MODE_5 + uint16* pixel = buffer + x1; + int32 width = (x2 - x1); + + while (width--) + { + *pixel++ = FETCH_GT_PAL(); + t += dtdx; + g += dgdx; + } + #else + if (x1 & 1) + { + uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1); + p = (p & 0x00FF) | (FETCH_GT() << 8); + t += dtdx; + g += dgdx; + x1++; + } + + int32 width = (x2 - x1) >> 1; + uint16* pixel = (uint16*)((uint8*)buffer + x1); + + dgdx <<= 1; + + if (width && (x1 & 3)) + { + uint16 p = FETCH_GT(); + t += dtdx; + *pixel++ = p | (FETCH_GT() << 8); + t += dtdx; + + g += dgdx; + + width--; + } + + while (width-- > 0) + { + uint32 p = FETCH_GT(); + t += dtdx; + p |= (FETCH_GT() << 8); + t += dtdx; + + g += dgdx; + + if (width-- > 0) + { + p |= (FETCH_GT() << 16); + t += dtdx; + p |= (FETCH_GT() << 24); + t += dtdx; + + g += dgdx; + + *(uint32*)pixel = p; + pixel += 2; + } else { + *(uint16*)pixel = p; + pixel += 1; + } + } + + if (x2 & 1) + { + *pixel = (*pixel & 0xFF00) | FETCH_GT(); + } + #endif +} + +void rasterizeG(uint16* buffer, int32 palIndex, Edge &L, Edge &R) +{ + while (1) + { + while (L.h <= 0) + { + if (!L.nextG()) + { + return; + } + } + + while (R.h <= 0) + { + if (!R.nextG()) + { + return; + } + } + + int32 h = MIN(L.h, R.h); + L.h -= h; + R.h -= h; + + while (h--) { + int32 x1 = L.x >> 16; + int32 x2 = R.x >> 16; + + if (x2 > x1) + { + int32 d = FixedInvU(x2 - x1); + + int32 dgdx = d * ((R.g - L.g) >> 8) >> 16; + + scanlineG(buffer, x1, x2, palIndex, L.g >> 8, dgdx); + } + + buffer += WIDTH / PIXEL_SIZE; + + L.stepG(); + R.stepG(); + } + } +} + +void rasterizeGT(uint16* buffer, Edge &L, Edge &R) +{ + while (1) + { + while (L.h <= 0) + { + if (!L.nextGT()) + { + return; + } + } + + while (R.h <= 0) + { + if (!R.nextGT()) + { + return; + } + } + + int32 h = MIN(L.h, R.h); + L.h -= h; + R.h -= h; + + while (h--) { + int32 x1 = L.x >> 16; + int32 x2 = R.x >> 16; + + if (x2 > x1) + { + uint32 d = FixedInvU(x2 - x1); + + int32 dgdx = d * ((R.g - L.g) >> 8) >> 16; + + uint32 u = d * ((R.t >> 16) - (L.t >> 16)); + uint32 v = d * ((R.t & 0xFFFF) - (L.t & 0xFFFF)); + uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + + scanlineGT(buffer, x1, x2, L.g >> 8, L.t, dgdx, dtdx); + } + + buffer += WIDTH / PIXEL_SIZE; + + L.stepGT(); + R.stepGT(); + } + } +} + +void drawTriangle(uint16 flags, int32 start, const int8* indices) +{ + Vertex *v1, *v2, *v3; + + bool clipped = indices[0] == indices[1]; + + if (clipped) { + v1 = gVertices + start; + v2 = v1 + 1; + v3 = v1 + 2; + } else { + v1 = gVertices + start; + v2 = v1 + indices[1]; + v3 = v1 + indices[2]; + } + + uint16 palIndex = flags & FACE_TEXTURE; + + if (!(flags & FACE_COLORED)) { + const Texture &tex = textures[palIndex]; + palIndex = 0xFFFF; + curTile = tiles[tex.tile]; + if (!clipped) { + v1->u = tex.x0; + v1->v = tex.y0; + v2->u = tex.x1; + v2->v = tex.y1; + v3->u = tex.x2; + v3->v = tex.y2; + } + } + + sortVertices(v1, v2, v3); + + int32 temp = (v2->y - v1->y) * FixedInvU(v3->y - v1->y); + + int32 longest = ((temp * (v3->x - v1->x)) >> 16) + (v1->x - v2->x); + if (longest == 0) + { + return; + } + + Edge L, R; + + if (longest < 0) + { + R.vert[0] = v3; + R.vert[1] = v2; + R.vert[2] = v1; + R.index = 2; + L.vert[0] = v3; + L.vert[1] = v1; + L.index = 1; + } + else + { + L.vert[0] = v3; + L.vert[1] = v2; + L.vert[2] = v1; + L.index = 2; + R.vert[0] = v3; + R.vert[1] = v1; + R.index = 1; + } + + if (palIndex != 0xFFFF) { + rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R); + } else { + rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R); + } +} + +void drawQuad(uint16 flags, int32 start, const int8* indices) { + Vertex *v1, *v2, *v3, *v4; + + bool clipped = indices[0] == indices[1]; + + if (clipped) { + v1 = gVertices + start; + v2 = v1 + 1; + v3 = v1 + 2; + v4 = v1 + 3; + } else { + v1 = gVertices + start; + v2 = v1 + indices[1]; + v3 = v1 + indices[2]; + v4 = v1 + indices[3]; + } + + uint16 palIndex = flags & FACE_TEXTURE; + + if (!(flags & FACE_COLORED)) { + const Texture &tex = textures[palIndex]; + palIndex = 0xFFFF; + curTile = tiles[tex.tile]; + if (!clipped) { + v1->u = int32(tex.x0); + v1->v = int32(tex.y0); + v2->u = int32(tex.x1); + v2->v = int32(tex.y1); + v3->u = int32(tex.x2); + v3->v = int32(tex.y2); + v4->u = int32(tex.x3); + v4->v = int32(tex.y3); + } + } + + sortVertices(v1, v2, v3, v4); + + int32 temp = (v2->y - v1->y) * FixedInvU(v4->y - v1->y); + + int32 longest = ((temp * (v4->x - v1->x)) >> 16) + (v1->x - v2->x); + if (longest == 0) + { + return; + } + + Edge L, R; + + if (checkBackface(v1, v4, v2) == checkBackface(v1, v4, v3)) + { + if (longest < 0) + { + L.vert[0] = v4; + L.vert[1] = v1; + L.index = 1; + R.vert[0] = v4; + R.vert[1] = v3; + R.vert[2] = v2; + R.vert[3] = v1; + R.index = 3; + } + else + { + R.vert[0] = v4; + R.vert[1] = v1; + R.index = 1; + L.vert[0] = v4; + L.vert[1] = v3; + L.vert[2] = v2; + L.vert[3] = v1; + L.index = 3; + } + } + else + { + R.vert[0] = v4; + R.vert[1] = v3; + R.vert[2] = v1; + R.index = 2; + L.vert[0] = v4; + L.vert[1] = v2; + L.vert[2] = v1; + L.index = 2; + + if (longest < 0) + { + swap(L.vert[1], R.vert[1]); + } + } + + if (palIndex != 0xFFFF) { + rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R); + } else { + rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R); + } +} + +void drawPoly(uint16 flags, int32 start, int32 count) { + uint16 palIndex = flags & FACE_TEXTURE; + + if (!(flags & FACE_COLORED)) { + const Texture &tex = textures[palIndex]; + palIndex = 0xFFFF; + curTile = tiles[tex.tile]; + } + + int32 minY = 0x7FFF; + int32 maxY = -0x7FFF; + int32 t = start, b = start; + + for (int i = 0; i < count; i++) { + Vertex *v = gVertices + start + i; + + if (v->y < minY) { + minY = v->y; + t = i; + } + + if (v->y > maxY) { + maxY = v->y; + b = i; + } + } + + Edge L, R; + L.vert[L.index = 0] = gVertices + start + b; + R.vert[R.index = 0] = gVertices + start + b; + + for (int i = 0; i < count; i++) { + int32 idx = (b + count + i) % count; + + L.vert[++L.index] = gVertices + start + idx; + + if (idx == t) { + break; + } + } + + for (int i = 0; i < count; i++) { + int32 idx = (b + count - i) % count; + + R.vert[++R.index] = gVertices + start + idx; + + if (idx == t) { + break; + } + } + + Vertex *v1 = gVertices + start + t; + + if (palIndex != 0xFFFF) { + rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R); + } else { + rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R); + } +} + +void drawGlyph(int32 index, int32 x, int32 y) { + const Sprite* sprite = sprites + spriteSequences[seqGlyphs].sStart + index; + + int32 w = sprite->r - sprite->l; + int32 h = sprite->b - sprite->t; + + w = (w >> 1) << 1; // make it even + + int32 ix = x + sprite->l; + int32 iy = y + sprite->t; + + uint16* ptr = (uint16*)fb + iy * (WIDTH / PIXEL_SIZE) + (ix >> 1); + + const uint8* glyphData = tiles[sprite->tile] + 256 * sprite->v + sprite->u; + + while (h--) + { + const uint8* p = glyphData; + + for (int i = 0; i < (w / 2); i++) { + + if (p[0] || p[1]) { + uint16 d = ptr[i]; + + if (p[0]) d = (d & 0xFF00) | p[0]; + if (p[1]) d = (d & 0x00FF) | (p[1] << 8); + + ptr[i] = d; + } + + p += 2; + } + + ptr += WIDTH / PIXEL_SIZE; + glyphData += 256; + } +} + +void faceAddPolyClip(uint16 flags, Vertex** poly, int32 pCount) +{ + #define LERP(a,b,t) (b + ((a - b) * t >> 16)) + + #define CLIP_AXIS(x, y, edge, output) {\ + uint32 t = MyDiv((edge - b->x) << 16, a->x - b->x);\ + Vertex* v = output + count++;\ + v->x = edge;\ + v->y = LERP(a->y, b->y, t);\ + v->z = LERP(a->z, b->z, t);\ + v->u = LERP(a->u, b->u, t);\ + v->v = LERP(a->v, b->v, t);\ + v->g = LERP(a->g, b->g, t);\ + } + + #define CLIP_VERTEX(x, y, x0, x1, input, output) {\ + const Vertex *a, *b = input[pCount - 1];\ + for (int32 i = 0; i < pCount; i++) {\ + a = b;\ + b = input[i];\ + if (a->x < x0) {\ + if (b->x < x0) continue;\ + CLIP_AXIS(x, y, x0, output);\ + } else if (a->x > x1) {\ + if (b->x > x1) continue;\ + CLIP_AXIS(x, y, x1, output);\ + }\ + if (b->x < x0) {\ + CLIP_AXIS(x, y, x0, output);\ + } else if (b->x > x1) {\ + CLIP_AXIS(x, y, x1, output);\ + } else {\ + output[count++] = *b;\ + }\ + }\ + if (count < 3) return;\ + } + + if (!(flags & FACE_COLORED)) { + const Texture &tex = textures[flags & FACE_TEXTURE]; + curTile = tiles[tex.tile]; + poly[0]->u = int32(tex.x0); + poly[0]->v = int32(tex.y0); + poly[1]->u = int32(tex.x1); + poly[1]->v = int32(tex.y1); + poly[2]->u = int32(tex.x2); + poly[2]->v = int32(tex.y2); + if (pCount == 4) { + poly[3]->u = int32(tex.x3); + poly[3]->v = int32(tex.y3); + } + } + + Vertex tmp[8]; + int32 count = 0; + +// clip x + int32 x0 = clip.x0; + int32 x1 = clip.x1; + CLIP_VERTEX(x, y, x0, x1, poly, tmp); + + pCount = count; + count = 0; + + Vertex* output = gVertices + gVerticesCount; + +// clip y + int32 y0 = clip.y0; + int32 y1 = clip.y1; + CLIP_VERTEX(y, x, y0, y1, &tmp, output); + + Face &f = gFaces[gFacesCount++]; + f.flags = flags; + f.start = gVerticesCount; + f.indices[0] = count; + f.indices[1] = count; + /* + if (count == 3) { + f.flags |= FACE_TRIANGLE; + f.depth = (output[0].z + output[1].z + output[2].z) / 3; + } else if (count == 4) { + f.depth = (output[0].z + output[1].z + output[2].z + output[3].z) >> 2; + } else*/ { + int32 depth = output[0].z; + for (int32 i = 1; i < count; i++) { + depth = (depth + output[i].z) >> 1; + } + f.depth = depth; + } + + gVerticesCount += count; +} + +void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex) { +#ifdef _WIN32 + if (gFacesCount >= MAX_FACES) { + DebugBreak(); + } +#endif + Vertex* v1 = gVertices + startVertex + indices[0]; + Vertex* v2 = gVertices + startVertex + indices[1]; + Vertex* v3 = gVertices + startVertex + indices[2]; + Vertex* v4 = gVertices + startVertex + indices[3]; + + if (v1->z < 0 || v2->z < 0 || v3->z < 0 || v4->z < 0) + return; + + if (checkBackface(v1, v2, v3)) + return; + + if (v1->clip & v2->clip & v3->clip & v4->clip) + return; + + if (v1->clip | v2->clip | v3->clip | v4->clip) { + Vertex* poly[4] = { v1, v2, v3, v4 }; + faceAddPolyClip(flags, poly, 4); + } else { + Face &f = gFaces[gFacesCount++]; + f.flags = flags; + f.depth = (v1->z + v2->z + v3->z + v4->z) >> 2; + f.start = startVertex + indices[0]; + f.indices[0] = 0; + f.indices[1] = indices[1] - indices[0]; + f.indices[2] = indices[2] - indices[0]; + f.indices[3] = indices[3] - indices[0]; + } +} + +void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex) { +#ifdef _WIN32 + if (gFacesCount >= MAX_FACES) { + DebugBreak(); + } +#endif + Vertex* v1 = gVertices + startVertex + indices[0]; + Vertex* v2 = gVertices + startVertex + indices[1]; + Vertex* v3 = gVertices + startVertex + indices[2]; + + if (v1->z < 0 || v2->z < 0 || v3->z < 0) + return; + + if (checkBackface(v1, v2, v3)) + return; + + if (v1->clip & v2->clip & v3->clip) + return; + + if (v1->clip | v2->clip | v3->clip) { + Vertex* poly[3] = { v1, v2, v3 }; + faceAddPolyClip(flags, poly, 3); + } else { + Face &f = gFaces[gFacesCount++]; + f.flags = flags | FACE_TRIANGLE; + f.depth = (v1->z + v2->z + v3->z) / 3; + f.start = startVertex + indices[0]; + f.indices[0] = 0; + f.indices[1] = indices[1] - indices[0]; + f.indices[2] = indices[2] - indices[0]; + } +} + +int faceCmp(const void *a, const void *b) { + return ((Face*)b)->depth - ((Face*)a)->depth; +} + +//int32 gFacesCountMax, gVerticesCountMax; + +void flush() { + if (gFacesCount) { + qsort(gFaces, gFacesCount, sizeof(Face), faceCmp); + + //const uint16 mips[] = { 0xFFFF, 0xFEFE, 0xFCFC, 0xF8F8 }; + + for (int32 i = 0; i < gFacesCount; i++) { + const Face &f = gFaces[i]; + + // TODO + //mipMask = mips[MIN(3, f.depth / 2048)]; + + if (f.flags & FACE_TRIANGLE) { + drawTriangle(f.flags, f.start, f.indices); + } else { + if (f.indices[0] == f.indices[1] /* && f.indices[0] > 4 */) { + drawPoly(f.flags, f.start, f.indices[0]); + } else { + drawQuad(f.flags, f.start, f.indices); + } + } + } + } + + //if (gFacesCount > gFacesCountMax) gFacesCountMax = gFacesCount; + //if (gVerticesCount > gVerticesCountMax) gVerticesCountMax = gVerticesCount; + //printf("%d %d\n", gFacesCountMax, gVerticesCountMax); + + gVerticesCount = 0; + gFacesCount = 0; +} + +void initRender() { + divTable[0] = 0; + for (uint32 i = 1; i < DIV_TABLE_SIZE; i++) { + divTable[i] = MyDiv(1 << 16, i); + } +} + +void clear() { + uint32* dst = (uint32*)fb; + + #ifdef USE_MODE_5 + uint32* end = dst + (WIDTH * HEIGHT >> 1); + #else + uint32* end = dst + (WIDTH * HEIGHT >> 2); + #endif + + while (dst < end) { + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + *dst++ = 0; + } +} \ No newline at end of file diff --git a/src/platform/gba/icon.png b/src/platform/gba/icon.png new file mode 100644 index 0000000..1c882e3 Binary files /dev/null and b/src/platform/gba/icon.png differ diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp new file mode 100644 index 0000000..305a8fc --- /dev/null +++ b/src/platform/gba/main.cpp @@ -0,0 +1,595 @@ +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LEVEL1_PHD.h" +#endif + +#include "common.h" + +//#define PROFILE + +#if defined(USE_MODE_5) || defined(_WIN32) +extern uint16 palette[256]; +#endif + +extern uint8 lightmap[256 * 32]; + +extern Vertex gVertices[MAX_VERTICES]; +extern uint32 gVerticesCount; + +extern Face gFaces[MAX_FACES]; +extern int32 gFacesCount; + +extern Rect clip; + +extern const uint8* tiles[15]; + +extern const uint8* curTile; +uint32 tilesCount; + +extern int32 fps; + +uint16 camRotY = 16 << 8; + +extern int32 camSinY; +extern int32 camCosY; + +extern int32 camX; +extern int32 camY; +extern int32 camZ; + +extern uint16 roomsCount; +extern const Room* rooms; + +extern uint32 texturesCount; +extern const Texture* textures; + +extern const Sprite* sprites; + +extern uint32 spriteSequencesCount; +extern const SpriteSequence* spriteSequences; + +extern int32 seqGlyphs; + +extern const uint8* meshData; +extern const uint32* meshOffsets; + +extern const int32* nodes; +extern const Model* models; + +extern void transform(int32 vx, int32 vy, int32 vz, int32 vg, int32 x, int32 y, int32 z); +extern void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex); +extern void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex); +extern void flush(); +extern void initRender(); + +void drawRoom(int16 roomIndex) { + const Room *room = rooms; + + //Room::Portal *portals; + uint16 portalsCount; + + //Room::Sector* sectors; + uint16 zSectors, xSectors; + + //uint16 ambient; + + //Room::Light* lights; + uint16 lightsCount; + + //Room::Mesh* meshes; + uint16 meshesCount; + + uint8 *ptr = (uint8*)room; + + while (roomIndex--) { + uint32 dataSize; + memcpy(&dataSize, &room->dataSize, sizeof(dataSize)); + ptr += sizeof(Room) + dataSize * 2; + + portalsCount = *((uint16*)ptr); + ptr += 2; + //portals = (Room::Portal*)ptr; + ptr += sizeof(Room::Portal) * portalsCount; + + zSectors = *((uint16*)ptr); + ptr += 2; + xSectors = *((uint16*)ptr); + ptr += 2; + //sectors = (Room::Sector*)sectors; + ptr += sizeof(Room::Sector) * zSectors * xSectors; + + //ambient = *((uint16*)ptr); + ptr += 2; + + lightsCount = *((uint16*)ptr); + ptr += 2; + //lights = (Room::Light*)ptr; + ptr += sizeof(Room::Light) * lightsCount; + + meshesCount = *((uint16*)ptr); + ptr += 2; + //meshes = (Room::Mesh*)ptr; + ptr += sizeof(Room::Mesh) * meshesCount; + + ptr += 2 + 2; // skip alternateRoom and flags + + room = (Room*)ptr; + } + + ptr += sizeof(Room); + + uint16 vCount = *((uint16*)ptr); + ptr += 2; + Room::Vertex* vertices = (Room::Vertex*)ptr; + ptr += sizeof(Room::Vertex) * vCount; + + // non-aligned data + int32 roomX; + int32 roomZ; + memcpy(&roomX, &room->info.x, sizeof(roomX)); + memcpy(&roomZ, &room->info.z, sizeof(roomZ)); + + int32 dx = -camX + roomX; + int32 dy = -camY; + int32 dz = -camZ + roomZ; + + int32 startVertex = gVerticesCount; + + for (uint16 i = 0; i < vCount; i++) { + const Room::Vertex &v = vertices[i]; + transform(v.x, v.y, v.z, v.lighting, dx, dy, dz); + } + + uint16 qCount = *((uint16*)ptr); + ptr += 2; + Quad* quads = (Quad*)ptr; + ptr += sizeof(Quad) * qCount; + + for (uint16 i = 0; i < qCount; i++) { + faceAddQuad(quads[i].flags, quads[i].indices, startVertex); + } + + uint16 tCount = *((uint16*)ptr); + ptr += 2; + Triangle* triangles = (Triangle*)ptr; + ptr += sizeof(Triangle) * tCount; + + for (uint16 i = 0; i < tCount; i++) { + faceAddTriangle(triangles[i].flags, triangles[i].indices, startVertex); + } +} + +void drawMesh(int16 meshIndex, int32 x, int32 y, int32 z) { + uint32 offset = meshOffsets[meshIndex]; + const uint8* ptr = meshData + offset; + + //int16 cx = *(int16*)ptr; ptr += 2; + //int16 cy = *(int16*)ptr; ptr += 2; + //int16 cz = *(int16*)ptr; ptr += 2; + //int16 r = *(int16*)ptr; ptr += 2; + //ptr += 2; // skip flags + ptr += 2 * 5; + + int16 vCount = *(int16*)ptr; ptr += 2; + const int16* vertices = (int16*)ptr; + ptr += vCount * 3 * sizeof(int16); + + int16 nCount = *(int16*)ptr; ptr += 2; + //const int16* normals = (int16*)ptr; + if (nCount > 0) { // normals + ptr += nCount * 3 * sizeof(int16); + } else { // intensity + ptr += vCount * sizeof(int16); + } + + int16 rCount = *(int16*)ptr; ptr += 2; + Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad); + + int16 tCount = *(int16*)ptr; ptr += 2; + Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle); + + int16 crCount = *(int16*)ptr; ptr += 2; + Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad); + + int16 ctCount = *(int16*)ptr; ptr += 2; + Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle); + + int32 startVertex = gVerticesCount; + + int32 dx = x - camX; + int32 dy = y - camY; + int32 dz = z - camZ; + + const int16* v = vertices; + for (uint16 i = 0; i < vCount; i++) { + transform(v[0], v[1], v[2], 4096, dx, dy, dz); + v += 3; + } + + for (int i = 0; i < rCount; i++) { + faceAddQuad(rFaces[i].flags, rFaces[i].indices, startVertex); + } + + for (int i = 0; i < crCount; i++) { + faceAddQuad(crFaces[i].flags | FACE_COLORED, crFaces[i].indices, startVertex); + } + + for (int i = 0; i < tCount; i++) { + faceAddTriangle(tFaces[i].flags, tFaces[i].indices, startVertex); + } + + for (int i = 0; i < ctCount; i++) { + faceAddTriangle(ctFaces[i].flags | FACE_COLORED, ctFaces[i].indices, startVertex); + } +} + +void drawModel(int32 modelIndex, int32 x, int32 y, int32 z) { + const Model* model = models + modelIndex; + + // non-aligned access + uint32 node, frame; + memcpy(&node, &model->node, sizeof(node)); + memcpy(&frame, &model->frame, sizeof(frame)); + + Node bones[32]; + memcpy(bones, nodes + node, (model->mCount - 1) * sizeof(Node)); + + const Node* n = bones; + + struct StackItem { + int32 x, y, z; + } stack[4]; + StackItem *s = stack; + + drawMesh(model->mStart, x, y, z); + + for (int i = 1; i < model->mCount; i++) { + if (n->flags & 1) { + s--; + x = s->x; + y = s->y; + z = s->z; + } + + if (n->flags & 2) { + s->x = x; + s->y = y; + s->z = z; + s++; + } + + x += n->x; + y += n->y; + z += n->z; + n++; + + drawMesh(model->mStart + i, x, y, z); + } +} + +void readLevel(const uint8 *data) { + tilesCount = *((uint32*)(data + 4)); + for (uint32 i = 0; i < tilesCount; i++) { + tiles[i] = data + 8 + 256 * 256 * i; + } + + roomsCount = *((uint16*)(data + 720908)); + rooms = (Room*)(data + 720908 + 2); + + texturesCount = *((uint32*)(data + 1271686)); + textures = (Texture*)(data + 1271686 + 4); + + sprites = (Sprite*)(data + 1289634); + + spriteSequencesCount = *((uint32*)(data + 1292130)); + spriteSequences = (SpriteSequence*)(data + 1292130 + 4); + + for (uint32 i = 0; i < spriteSequencesCount; i++) { + if (spriteSequences[i].type == 190) { + seqGlyphs = i; + break; + } + } + + meshData = data + 908172 + 4; + meshOffsets = (uint32*)(data + 975724 + 4); + + nodes = (int32*)(data + 990318); + + models = (Model*)(data + 1270670); + + const uint8* f_lightmap = data + 1320576; + memcpy(lightmap, f_lightmap, sizeof(lightmap)); + +#if !(defined(USE_MODE_5) || defined(_WIN32)) + uint16 palette[256]; +#endif + + const uint8* f_palette = data + 1328768; + + const uint8* p = f_palette; + + for (int i = 0; i < 256; i++) { + palette[i] = (p[0] >> 1) | ((p[1] >> 1) << 5) | ((p[2] >> 1) << 10); + p += 3; + } + +#ifndef _WIN32 + palette[1] = RGB8(0, 255, 0); + + #ifndef USE_MODE_5 + SetPalette(palette); + #endif +#endif +} + +#ifdef _WIN32 + #define CAM_SPEED (1 << 3) + #define CAM_ROT_SPEED (1 << 3) +#else + #define CAM_SPEED (1 << 6) + #define CAM_ROT_SPEED (1 << 8) +#endif + + +void updateCamera() { + if (keys[IK_LEFT]) camRotY -= CAM_ROT_SPEED; + if (keys[IK_RIGHT]) camRotY += CAM_ROT_SPEED; + + { + ALIGN4 ObjAffineSource src; + ALIGN4 ObjAffineDest dst; + + src.sX = 0x0100; + src.sY = 0x0100; + src.theta = camRotY; + + ObjAffineSet(&src, &dst, 1, 2); + + camCosY = dst.pd << 8; + camSinY = dst.pc << 8; + } + + int32 dx = camSinY; + int32 dz = camCosY; + + dx *= CAM_SPEED; + dz *= CAM_SPEED; + + dx >>= 16; + dz >>= 16; + + if (keys[IK_UP]) { + camX += int32(dx); + camZ += int32(dz); + } + + if (keys[IK_DOWN]) { + camX -= int32(dx); + camZ -= int32(dz); + } + + if (keys[IK_L]) { + camX -= int32(dz); + camZ += int32(dx); + } + + if (keys[IK_R]) { + camX += int32(dz); + camZ -= int32(dx); + } + + if (keys[IK_A]) camY -= CAM_SPEED; + if (keys[IK_B]) camY += CAM_SPEED; + + clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; +} + +void drawNumber(int32 number, int32 x, int32 y) { + const int32 widths[] = { 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 }; + + while (number > 0) { + x -= widths[number % 10]; + drawGlyph(52 + (number % 10), x, y); + number /= 10; + } +} + +void update(int32 frames) { + for (int32 i = 0; i < frames; i++) { + updateCamera(); + } +} + +void render() { + clear(); + + drawRoom(6); + flush(); + + drawRoom(0); + drawModel(0, 75162, 3072 - 512, 5000 + 1024); + flush(); + + drawNumber(fps, WIDTH, 16); +} + +#ifdef _WIN32 +HDC hDC; + +void VBlankIntrWait() { + #ifdef USE_MODE_5 + for (int i = 0; i < WIDTH * HEIGHT; i++) { + uint16 c = fb[i]; + VRAM[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; + } + #else + for (int i = 0; i < WIDTH * HEIGHT; i++) { + uint16 c = palette[fb[i]]; + VRAM[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; + } + #endif + + const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), WIDTH, -HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; + StretchDIBits(hDC, 0, 0, 240 * WND_SCALE, 160 * WND_SCALE, 0, 0, WIDTH, HEIGHT, VRAM, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_DESTROY : + PostQuitMessage(0); + break; + case WM_KEYDOWN : + case WM_KEYUP : { + InputKey key = IK_MAX; + switch (wParam) { + case VK_UP : key = IK_UP; break; + case VK_RIGHT : key = IK_RIGHT; break; + case VK_DOWN : key = IK_DOWN; break; + case VK_LEFT : key = IK_LEFT; break; + case 'Z' : key = IK_A; break; + case 'X' : key = IK_B; break; + case 'A' : key = IK_L; break; + case 'S' : key = IK_R; break; + } + if (key != IK_MAX) { + keys[key] = msg != WM_KEYUP; + } + break; + } + default : + return DefWindowProc(hWnd, msg, wParam, lParam); + } + return 0; +} +#endif + +int32 frameIndex = 0; +int32 fpsCounter = 0; + +void vblank() { + frameIndex++; +} + +int main(void) { +#ifdef _WIN32 + { + FILE *f = fopen("C:/Projects/TR/TR1_ANDROID/LEVEL1.PHD", "rb"); + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + LEVEL1_PHD = new uint8[size]; + fread(LEVEL1_PHD, 1, size, f); + fclose(f); + } +#else + // set low latency mode via WAITCNT register (thanks to GValiente) + #define BIT_SET(y, flag) (y |= (flag)) + #define REG_WAITCNT_NV *(u16*)(REG_BASE + 0x0204) + + BIT_SET(REG_WAITCNT_NV, 0x0008 | 0x0010 | 0x4000); +#endif + + initRender(); + + readLevel(LEVEL1_PHD); + +#ifdef _WIN32 + RECT r = { 0, 0, 240 * WND_SCALE, 160 * WND_SCALE }; + + AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false); + int wx = (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2; + int wy = (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2; + + HWND hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0); + hDC = GetDC(hWnd); + + SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&wndProc); + ShowWindow(hWnd, SW_SHOWDEFAULT); + + MSG msg; + + do { + if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + + update(1); + render(); + + VBlankIntrWait(); + } + } while (msg.message != WM_QUIT); + +#else + irqInit(); + irqSet(IRQ_VBLANK, vblank); + irqEnable(IRQ_VBLANK); + + uint16 mode = BG2_ON | BACKBUFFER; + + #ifdef USE_MODE_5 + mode |= MODE_5; + + REG_BG2PA = 256 - 64 - 16 - 4 - 1; + REG_BG2PD = 256 - 48 - 2; + #else + mode |= MODE_4; + #endif + + int32 lastFrameIndex = -1; + + #ifdef PROFILE + int counter = 0; + #endif + + while (1) { + //VBlankIntrWait(); + + #ifdef PROFILE + if (counter++ >= 10) return 0; + #endif + + SetMode(mode ^= BACKBUFFER); + fb ^= 0xA000; + + scanKeys(); + uint16 key = keysDown() | keysHeld(); + keys[IK_UP] = (key & KEY_UP); + keys[IK_RIGHT] = (key & KEY_RIGHT); + keys[IK_DOWN] = (key & KEY_DOWN); + keys[IK_LEFT] = (key & KEY_LEFT); + keys[IK_A] = (key & KEY_A); + keys[IK_B] = (key & KEY_B); + keys[IK_L] = (key & KEY_L); + keys[IK_R] = (key & KEY_R); + + int32 frame = frameIndex; + update(frame - lastFrameIndex); + lastFrameIndex = frame; + + render(); + + fpsCounter++; + if (frameIndex >= 60) { + frameIndex -= 60; + lastFrameIndex -= 60; + + fps = fpsCounter; + + fpsCounter = 0; + } + + } +#endif +} diff --git a/src/platform/gba/profile.bat b/src/platform/gba/profile.bat new file mode 100644 index 0000000..8cf71a2 --- /dev/null +++ b/src/platform/gba/profile.bat @@ -0,0 +1 @@ +NO$GBA C:\Projects\OpenLara\src\platform\gba\OpenLara.elf \ No newline at end of file diff --git a/src/platform/gba/sdiv32.s b/src/platform/gba/sdiv32.s new file mode 100644 index 0000000..a28236e --- /dev/null +++ b/src/platform/gba/sdiv32.s @@ -0,0 +1,51 @@ +@-------------------------------------------------------------------------------- +@ udiv.s +@-------------------------------------------------------------------------------- +@ Provides an implementation of signed division +@-------------------------------------------------------------------------------- + +@ refer to the unsigned division + .extern __aeabi_uidivmod + .extern __aeabi_uidiv + +@ r0: the numerator / r1: the denominator +@ after it, r0 has the quotient and r1 has the modulo + .section .iwram, "ax", %progbits + .align 2 + .arm + .global __aeabi_idivmod + .type __aeabi_idivmod STT_FUNC +__aeabi_idivmod: + + .section .iwram, "ax", %progbits + .align 2 + .arm + .global __aeabi_idiv + .type __aeabi_idiv STT_FUNC +__aeabi_idiv: + + @ Move the lr to r12 and make the numbers positive + mov r12, lr + + cmp r0, #0 + rsblt r0, #0 + orrlt r12, #1 << 30 + + cmp r1, #0 + rsblt r1, #0 + orrlt r12, #1 << 31 + + @ Call the unsigned division + .extern udiv32pastzero + bl udiv32pastzero + + @ Test the old sign bits + tst r12, #1 << 30 + rsbne r0, r0, #0 + rsbne r1, r0, #0 + tst r12, #1 << 31 + rsbne r0, r0, #0 + + @ Erase the sign bits from the return address, and return + bic r12, #3 << 30 + bx r12 diff --git a/src/platform/gba/udiv32.s b/src/platform/gba/udiv32.s new file mode 100644 index 0000000..a2f60ce --- /dev/null +++ b/src/platform/gba/udiv32.s @@ -0,0 +1,79 @@ +@-------------------------------------------------------------------------------- +@ udiv.s +@-------------------------------------------------------------------------------- +@ Provides an implementation of unsigned division +@-------------------------------------------------------------------------------- + +@ Source code taken from https://www.chiark.greenend.org.uk/~theom/riscos/docs/ultimate/a252div.txt +@ r0: the numerator / r1: the denominator +@ after it, r0 has the quotient and r1 has the modulo + .section .iwram, "ax", %progbits + .align 2 + .arm + .global __aeabi_uidivmod + .type __aeabi_uidivmod STT_FUNC +__aeabi_uidivmod: + + .section .iwram, "ax", %progbits + .align 2 + .arm + .global __aeabi_uidiv + .type __aeabi_uidiv STT_FUNC +__aeabi_uidiv: + + @ Check for division by zero + cmp r1, #0 + bxeq lr + + .global udiv32pastzero +udiv32pastzero: + @ If n < d, just bail out as well + cmp r0, r1 @ n, d + movlo r1, r0 @ mod = n + movlo r0, #0 @ quot = 0 + bxlo lr + + @ Move the denominator to r2 and start to build a counter that + @ counts the difference on the number of bits on each numerator + @ and denominator + @ From now on: r0 = quot/num, r1 = mod, r2 = denom, r3 = counter + mov r2, r1 + mov r3, #28 @ first guess on difference + mov r1, r0, lsr #4 @ r1 = num >> 4 + + @ Iterate three times to get the counter up to 4-bit precision + cmp r2, r1, lsr #12 + suble r3, r3, #16 + movle r1, r1, lsr #16 + + cmp r2, r1, lsr #4 + suble r3, r3, #8 + movle r1, r1, lsr #8 + + cmp r2, r1 + suble r3, r3, #4 + movle r1, r1, lsr #4 + + @ shift the numerator by the counter and flip the sign of the denom + mov r0, r0, lsl r3 + adds r0, r0, r0 + rsb r2, r2, #0 + + @ dynamically jump to the exact copy of the iteration + add r3, r3, r3, lsl #1 @ counter *= 3 + add pc, pc, r3, lsl #2 @ jump + mov r0, r0 @ pipelining issues + + @ here, r0 = num << (r3 + 1), r1 = num >> (32-r3), r2 = -denom + @ now, the real iteration part + .global divIteration +divIteration: + .rept 32 + adcs r1, r2, r1, lsl #1 + sublo r1, r1, r2 + adcs r0, r0, r0 + .endr + + @ and then finally quit + @ r0 = quotient, r1 = remainder + bx lr