├── id1psx ├── .empty └── maps │ └── .empty ├── tools ├── bin │ └── .empty ├── .clangd ├── mkresmap │ └── Makefile ├── mkpics │ └── Makefile ├── bspconvpsx │ ├── xmdl.h │ ├── Makefile │ ├── qsfx.h │ ├── qmdl.h │ ├── qbsp.h │ ├── qent.h │ ├── xbsp.h │ ├── util.h │ ├── qbsp.c │ ├── util.c │ ├── qsfx.c │ └── qent.c ├── common │ ├── idpak.h │ ├── wad.h │ ├── pak.h │ ├── idwad.h │ ├── util.h │ ├── psxmdl.h │ ├── idmdl.h │ ├── psxtypes.h │ ├── pak.c │ ├── wad.c │ ├── util.c │ ├── libpsxav │ │ ├── cdrom.c │ │ └── libpsxav.h │ ├── idbsp.h │ └── psxbsp.h ├── cfg │ └── id1 │ │ ├── mdlprops.txt │ │ ├── picmap.txt │ │ ├── entmap.txt │ │ └── mdlmap.txt └── scripts │ ├── cook_id1.sh │ └── redux │ └── profiler.lua ├── system.cnf ├── .gitmodules ├── doc └── img │ ├── screenshot0.png │ ├── screenshot1.png │ └── screenshot2.png ├── src ├── profile.h ├── sprite.h ├── common.c ├── .clangd ├── dbgfont.h ├── progs.h ├── progs │ ├── monster_fish.c │ ├── picids.h │ ├── monster_hell_knight.c │ ├── mdlids.h │ ├── effects.c │ ├── entclasses.h │ ├── func.c │ ├── misc.c │ ├── sbar.c │ └── prcommon.c ├── cd.h ├── mathlib.h ├── input.h ├── types.h ├── spu.h ├── alias.h ├── main.c ├── profile.c ├── menu.h ├── memory.h ├── entity.c ├── fixed.h ├── screen.h ├── sprite.c ├── system.h ├── exception.h ├── move.h ├── sound.h ├── world.h ├── game.h ├── input.c ├── spu.c ├── common.h ├── r_light.c ├── memory.c ├── cd.c ├── bspfile.h ├── entity.h ├── menu.c ├── exception.c ├── vector.h ├── mathlib.c ├── model.h ├── render.h ├── vector_a.s └── sound.c ├── .gitignore ├── CMakePresets.json ├── iso.xml ├── CMakeLists.txt ├── iso_cdda.xml └── README.md /id1psx/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /id1psx/maps/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/bin/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /system.cnf: -------------------------------------------------------------------------------- 1 | BOOT=cdrom:\quakepsx.exe;1 2 | TCB=4 3 | EVENT=10 4 | STACK=801FFFF0 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nugget"] 2 | path = nugget 3 | url = https://github.com/pcsx-redux/nugget 4 | -------------------------------------------------------------------------------- /doc/img/screenshot0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsfdsfgs/quakepsx/HEAD/doc/img/screenshot0.png -------------------------------------------------------------------------------- /doc/img/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsfdsfgs/quakepsx/HEAD/doc/img/screenshot1.png -------------------------------------------------------------------------------- /doc/img/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsfdsfgs/quakepsx/HEAD/doc/img/screenshot2.png -------------------------------------------------------------------------------- /tools/.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Add: [-Og, -g, -xc, -Wall] 3 | CompilationDatabase: 4 | None 5 | 6 | Diagnostics: 7 | Suppress: [asm_unknown_register_name, unused-includes] 8 | -------------------------------------------------------------------------------- /src/profile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PRF_NO_INSTRUMENT __attribute__((no_instrument_function)) 4 | 5 | PRF_NO_INSTRUMENT void Prf_StartFrame(void); 6 | PRF_NO_INSTRUMENT void Prf_EndFrame(void); 7 | -------------------------------------------------------------------------------- /tools/mkresmap/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mkresmap 2 | SRC = $(wildcard *.c) ../common/pak.c ../common/util.c 3 | CC ?= gcc 4 | 5 | ../bin/$(TARGET).exe: $(SRC) 6 | $(CC) -o $@ $^ 7 | 8 | clean: 9 | rm -f ../bin/$(TARGET).exe 10 | 11 | .PHONY: clean 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | id1psx/*/* 3 | tools/tmp 4 | tools/tmp/*/* 5 | !id1psx/.empty 6 | !id1psx/maps/.empty 7 | *.o 8 | *.elf 9 | *.exe 10 | *.iso 11 | *.psb 12 | *.bsp 13 | *.dat 14 | *.bin 15 | *.cue 16 | .vscode 17 | compile_flags.txt 18 | compile_commands.json 19 | -------------------------------------------------------------------------------- /src/sprite.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define CONCHARS_SIZE 128 6 | 7 | typedef struct { 8 | u16 tpage; 9 | u8vec2_t uv; 10 | u8vec2_t size; 11 | } pic_t; 12 | 13 | void Spr_Init(void); 14 | const pic_t *Spr_GetPic(const int id); 15 | -------------------------------------------------------------------------------- /tools/mkpics/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mkpics 2 | SRC = $(wildcard *.c) ../common/pak.c ../common/wad.c ../common/util.c 3 | CC ?= gcc 4 | 5 | ../bin/$(TARGET).exe: $(SRC) 6 | $(CC) -o $@ $^ -lm 7 | 8 | clean: 9 | rm -f ../bin/$(TARGET).exe 10 | 11 | .PHONY: clean 12 | -------------------------------------------------------------------------------- /tools/bspconvpsx/xmdl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../common/psxtypes.h" 4 | #include "../common/psxmdl.h" 5 | #include "qmdl.h" 6 | 7 | xaliashdr_t *xbsp_entmodel_add_from_qmdl(qmdl_t *qm); 8 | xaliashdr_t *xbsp_entmodel_add_from_qbsp(qbsp_t *qm, s16 id); 9 | xaliashdr_t *xbsp_entmodel_find(const s16 id); 10 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "system.h" 3 | 4 | qboolean com_registered = false; 5 | 6 | char com_vastring[MAX_VA_STRING]; 7 | 8 | static void COM_InitFilesystem(void) { 9 | com_registered = Sys_FileExists(FS_BASE "\\MAPS\\E2M1.PSB;1"); 10 | } 11 | 12 | void COM_Init(void) { 13 | COM_InitFilesystem(); 14 | } 15 | -------------------------------------------------------------------------------- /tools/bspconvpsx/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = bspconvpsx 2 | SRC = $(wildcard *.c) ../common/pak.c ../common/util.c ../common/libpsxav/adpcm.c ../common/libpsxav/cdrom.c 3 | CC ?= gcc 4 | 5 | ../bin/$(TARGET).exe: $(SRC) 6 | $(CC) -Og -g -Werror -Wno-unused-result -o $@ $^ -lm 7 | 8 | clean: 9 | rm -f ../bin/$(TARGET).exe 10 | 11 | .PHONY: clean 12 | -------------------------------------------------------------------------------- /src/.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Remove: [-march=r3000, -mtune=r3000, -mno-llsc, -fsection-anchors, -mdivide-breaks, -flimit-function-alignment, -fallow-store-data-races, -freg-struct-return] 3 | Add: [-march=mips1, -mtune=r3000, -xc, -Wno-pragma-pack] 4 | 5 | Diagnostics: 6 | Suppress: [asm_unknown_register_name, -Wunknown-pragmas, -Waddress-of-packed-member] 7 | -------------------------------------------------------------------------------- /src/dbgfont.h: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | #define DBGFONT_WIDTH 8 4 | #define DBGFONT_HEIGHT 5 5 | #define DBGFONT_START '+' 6 | #define DBGFONT_END 'Z' 7 | #define DBGFONT_NUMCHRS (DBGFONT_END - DBGFONT_START + 1) 8 | 9 | extern const u8 sys_dbgfont[DBGFONT_NUMCHRS][DBGFONT_HEIGHT]; 10 | 11 | void Dbg_PrintReset(const int start_x, const int start_y, const u8 bg_r, const u8 bg_g, const u8 bg_b); 12 | void Dbg_PrintString(const char *s); 13 | -------------------------------------------------------------------------------- /tools/common/idpak.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | 5 | #define PACK_ID 0x4B434150U // "PACK" 6 | #define MAX_PACK_PATH 56 7 | #define MAX_FILES_IN_PACK 2048 8 | 9 | #pragma pack(push, 1) 10 | 11 | typedef struct { 12 | char name[MAX_PACK_PATH]; 13 | s32 filepos; 14 | s32 filelen; 15 | } qpackfile_t; 16 | 17 | typedef struct { 18 | u32 id; 19 | s32 dirofs; 20 | s32 dirlen; 21 | } qpackheader_t; 22 | 23 | #pragma pack(pop) 24 | -------------------------------------------------------------------------------- /tools/common/wad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "psxtypes.h" 5 | #include "idwad.h" 6 | 7 | typedef struct { 8 | u32 size; 9 | qwadinfo_t *info; 10 | qlumpinfo_t *lumps; 11 | u8 *data; 12 | } wad_t; 13 | 14 | int wad_open(wad_t *wad, u8 *data, const size_t size); 15 | void wad_close(wad_t *wad); 16 | u8 *wad_get_lump(wad_t *wad, const char *lname, size_t *outsize); 17 | u8 *wad_get_lump_by_num(wad_t *wad, const int lid, size_t *outsize); 18 | -------------------------------------------------------------------------------- /tools/common/pak.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "psxtypes.h" 5 | #include "idpak.h" 6 | 7 | typedef struct { 8 | qpackheader_t header; 9 | int numfiles; 10 | qpackfile_t files[MAX_FILES_IN_PACK]; 11 | FILE *fp; 12 | int findptr; 13 | } pak_t; 14 | 15 | int pak_open(pak_t *pak, const char *fname); 16 | void pak_close(pak_t *pak); 17 | u8 *pak_readfile(pak_t *pak, const char *fname, size_t *outsize); 18 | const char *pak_findfile(pak_t *pak, const char *match, const char *prev); 19 | -------------------------------------------------------------------------------- /src/progs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // progs stuff that is directly used by the engine 4 | 5 | #include "common.h" 6 | #include "game.h" 7 | #include "menu.h" 8 | #include "progs/entclasses.h" 9 | #include "progs/picids.h" 10 | 11 | // progs' main menu 12 | extern menu_t menu_main; 13 | 14 | // global 15 | void Progs_NewMap(void); 16 | 17 | // status bar 18 | void Sbar_Init(void); 19 | void Sbar_Draw(const player_state_t *p); 20 | 21 | // player 22 | void Player_PreThink(edict_t *ent); 23 | void Player_PostThink(edict_t *ent); 24 | -------------------------------------------------------------------------------- /src/progs/monster_fish.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | static void monster_fish_think(edict_t *self) { 4 | cycler_think(self, 40, 57); 5 | } 6 | 7 | void spawn_monster_fish(edict_t *self) { 8 | G_SetModel(self, MDLID_FISH); 9 | XVecSetInt(&self->v.mins, -16, -16, -24); 10 | XVecSetInt(&self->v.maxs, +16, +16, +24); 11 | G_SetSize(self, &self->v.mins, &self->v.maxs); 12 | self->v.think = monster_fish_think; 13 | self->v.nextthink = gs.time + 41; 14 | self->v.solid = SOLID_SLIDEBOX; 15 | self->v.movetype = MOVETYPE_STEP; 16 | self->v.flags = FL_MONSTER; 17 | } 18 | -------------------------------------------------------------------------------- /tools/common/idwad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | 5 | #pragma pack(push, 1) 6 | 7 | #define WAD_IDENT "WAD2" 8 | #define WAD_MAX_LUMPNAME 16 9 | 10 | typedef struct { 11 | char ident[4]; 12 | s32 numlumps; 13 | s32 infotableofs; 14 | } qwadinfo_t; 15 | 16 | typedef struct { 17 | s32 filepos; 18 | s32 disksize; 19 | s32 size; 20 | s8 type; 21 | s8 compression; 22 | s8 pad1, pad2; 23 | char name[WAD_MAX_LUMPNAME]; 24 | } qlumpinfo_t; 25 | 26 | typedef struct { 27 | s32 width, height; 28 | u8 data[]; 29 | } qpic_t; 30 | 31 | #pragma pack(pop) 32 | -------------------------------------------------------------------------------- /tools/cfg/id1/mdlprops.txt: -------------------------------------------------------------------------------- 1 | mdl progs/soldier.mdl 2 | delete 29..39 3 | 4 | mdl progs/ogre.mdl 5 | delete 136..146 6 | 7 | mdl progs/knight.mdl 8 | delete 67..75 9 | 10 | mdl progs/hknight.mdl 11 | delete 79..111 12 | 13 | mdl progs/v_axe.mdl 14 | viewmodel 15 | 16 | mdl progs/v_light.mdl 17 | viewmodel 18 | 19 | mdl progs/v_nail.mdl 20 | viewmodel 21 | 22 | mdl progs/v_nail2.mdl 23 | viewmodel 24 | 25 | mdl progs/v_rock.mdl 26 | viewmodel 27 | 28 | mdl progs/v_rock2.mdl 29 | viewmodel 30 | 31 | mdl progs/v_shot.mdl 32 | viewmodel 33 | 34 | mdl progs/v_shot2.mdl 35 | viewmodel 36 | -------------------------------------------------------------------------------- /src/cd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | typedef enum { 6 | CDMODE_NONE, 7 | CDMODE_DATA, 8 | CDMODE_AUDIO 9 | } cdmode_t; 10 | 11 | // 0 - 128 12 | extern s32 cd_volume; 13 | 14 | void CD_Init(void); 15 | void CD_Update(void); 16 | 17 | // 0 if in data mode or not playing anything 18 | u32 CD_GetCurrentTrack(void); 19 | 20 | qboolean CD_IsPlaybackPaused(void); 21 | 22 | // 0 - 128 23 | void CD_SetAudioVolume(u8 vol); 24 | 25 | void CD_PlayAudio(const u32 track); 26 | void CD_Pause(const qboolean pause); 27 | void CD_Stop(void); 28 | 29 | // blocking 30 | void CD_SetMode(const cdmode_t mode); 31 | -------------------------------------------------------------------------------- /src/mathlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "fixed.h" 5 | #include "vector.h" 6 | #include "model.h" 7 | 8 | enum angleidx_e { PITCH, YAW, ROLL }; 9 | 10 | x16 qatan2(s32 y, s32 x); 11 | 12 | void AngleVectors(const x16vec3_t *angles, x16vec3_t *forward, x16vec3_t *right, x16vec3_t *up); 13 | int BoxOnPlaneSide(const x32vec3_t *emins, const x32vec3_t *emaxs, const mplane_t *p); 14 | 15 | x16 VecToYaw(const x32vec3_t *vec); 16 | 17 | MATRIX *RotMatrixZY(SVECTOR *r, MATRIX *m); 18 | 19 | static inline x16 VecDeltaToYaw(const x32vec3_t *va, const x32vec3_t *vb) { 20 | x32vec3_t delta; 21 | XVecSub(va, vb, &delta); 22 | return VecToYaw(&delta); 23 | } 24 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "common.h" 5 | 6 | #define IN_DEADZONE_X 8 7 | #define IN_DEADZONE_Y 8 8 | 9 | typedef struct { 10 | u16 btn; 11 | u16 btn_prev; 12 | u16 btn_trig; 13 | s8vec2_t mouse; 14 | s8vec2_t sticks[2]; 15 | s32vec2_t stick_sens[2]; 16 | s32 stick_deadzone[2]; 17 | s32 mouse_sens; 18 | } input_t; 19 | 20 | extern input_t in; 21 | 22 | static inline int IN_ButtonHeld(const u16 btn) { 23 | return (in.btn & btn) != 0; 24 | } 25 | 26 | static inline int IN_ButtonPressed(const u16 btn) { 27 | return (in.btn_trig & btn) != 0; 28 | } 29 | 30 | void IN_Init(void); 31 | void IN_Update(void); 32 | void IN_Clear(void); 33 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qsfx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../common/psxtypes.h" 5 | 6 | #define MAX_QSFX 256 7 | #define MAX_QSFX_NAME 256 8 | 9 | typedef struct { 10 | int id; 11 | int samplerate; 12 | int loopstart; 13 | int numframes; 14 | int numsamples; 15 | s16 *samples; 16 | } qsfx_t; 17 | 18 | int qsfxmap_init(const char *mapfile); 19 | int qsfxmap_id_for_name(const char *name); 20 | const char *qsfxmap_name_for_id(const int id); 21 | 22 | qsfx_t *qsfx_add(const int in_id, const char *name, u8 *start, const size_t size); 23 | qsfx_t *qsfx_find(const int id); 24 | int qsfx_convert(qsfx_t *sfx, u8 *outptr, const int maxlen); 25 | void qsfx_free(qsfx_t *sfx); 26 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "default", 11 | "displayName": "Default configuration", 12 | "description": "Use this preset to build the project using PSn00bSDK.", 13 | "generator": "Unix Makefiles", 14 | "toolchainFile": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", 15 | "binaryDir": "${sourceDir}/build", 16 | "cacheVariables": { 17 | "CMAKE_BUILD_TYPE": "Debug", 18 | "PSN00BSDK_TC": "", 19 | "PSN00BSDK_TARGET": "mipsel-none-elf" 20 | }, 21 | "warnings": { 22 | "dev": false 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define FORCEINLINE __attribute__((always_inline)) inline 8 | 9 | #define PSX_SCRATCH ((void *)0x1F800000) 10 | 11 | #ifndef NULL 12 | #define NULL ((void *)0) 13 | #endif 14 | 15 | #ifndef TRUE 16 | #define TRUE 1 17 | #endif 18 | 19 | #ifndef FALSE 20 | #define FALSE 0 21 | #endif 22 | 23 | #ifndef true 24 | #define true TRUE 25 | #endif 26 | 27 | #ifndef false 28 | #define false FALSE 29 | #endif 30 | 31 | typedef uint8_t u8; 32 | typedef uint16_t u16; 33 | typedef uint32_t u32; 34 | typedef uint64_t u64; 35 | typedef int8_t s8; 36 | typedef int16_t s16; 37 | typedef int32_t s32; 38 | typedef int64_t s64; 39 | typedef u8 qboolean; 40 | -------------------------------------------------------------------------------- /src/spu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #define SPU_RAM_BASE 0x1100 6 | #define SPU_RAM_SIZE (0x80000 - SPU_RAM_BASE) 7 | #define SPU_NUM_VOICES 24 8 | #define SPU_MAX_VOLUME 0x3FFF 9 | 10 | void SPU_Init(void); 11 | void SPU_KeyOn(const u32 mask); 12 | void SPU_KeyOff(const u32 mask); 13 | void SPU_ClearVoice(const u32 v); 14 | void SPU_SetMasterVolume(const s16 v); 15 | void SPU_SetVoiceVolume(const u32 v, const s16 lvol, const s16 rvol); 16 | u32 SPU_GetVoiceEndMask(void); 17 | void SPU_PlaySample(const u32 ch, const u32 addr, const u32 freq); 18 | void SPU_WaitForTransfer(void); 19 | void SPU_ClearAllVoices(void); 20 | void SPU_StartUpload(const u32 dstaddr, const u8 *src, const u32 size); 21 | void SPU_EnableCDDA(const qboolean enable); 22 | 23 | static inline u16 FreqToPitch(const u32 hz) { 24 | return (hz << 12) / 44100; 25 | } 26 | -------------------------------------------------------------------------------- /tools/common/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "psxtypes.h" 9 | 10 | #define PSXRGB(r, g, b) ((((b) >> 3) << 10) | (((g) >> 3) << 5) | ((r) >> 3)) 11 | #define PSXTPAGE(tp, abr, x, y) ((((x)&0x3FF)>>6) | (((y)>>8)<<4) | (((abr)&0x3)<<5) | (((tp)&0x3)<<7)) 12 | 13 | #define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 14 | 15 | #define MAX_VA_LEN 4096 16 | #define MAX_VA_BUF 4 17 | 18 | #ifdef _WIN32 19 | #define qmkdir(dirn, dirp) mkdir(dirn) 20 | #else 21 | #define qmkdir(dirn, dirp) mkdir(dirn, dirp) 22 | #endif 23 | 24 | const char *strfmt(const char *fmt, ...); 25 | void panic(const char *fmt, ...); 26 | 27 | void *lmp_read(const char *dir, const char *fname, size_t *outsize); 28 | 29 | void convert_palette(u16 *dstpal, const u8 *pal, const int numcolors); 30 | -------------------------------------------------------------------------------- /src/alias.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "vector.h" 5 | 6 | /* XMDL STRUCTURE 7 | * aliashdr_t hdr; 8 | * aliastri_t tris[hdr.numtris]; 9 | * aliasvert_t frames[hdr.numframes][hdr.numverts]; 10 | */ 11 | 12 | #define MAX_XMDL_VERTS 512 13 | #define MAX_XMDL_FRAMES 256 14 | #define MAX_XMDL_TRIS 1024 15 | #define MAX_XMDL_SKINS 3 16 | 17 | #pragma pack(push, 1) 18 | 19 | typedef struct { 20 | u8vec2_t uvs[3]; 21 | u16 verts[3]; 22 | } atri_t; 23 | 24 | typedef struct { 25 | u16 tpage; 26 | u8vec2_t base; 27 | } askin_t; 28 | 29 | typedef struct { 30 | u8 type; 31 | u8 flags; 32 | s16 id; 33 | u16 numframes; 34 | u16 numverts; 35 | u16 numtris; 36 | u16 numskins; 37 | x16vec3_t scale; 38 | s16vec3_t offset; 39 | x32vec3_t mins; 40 | x32vec3_t maxs; 41 | askin_t skins[MAX_XMDL_SKINS]; 42 | atri_t *tris; 43 | u8vec3_t *frames; 44 | } aliashdr_t; 45 | 46 | #pragma pack(pop) 47 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "system.h" 3 | #include "cd.h" 4 | #include "render.h" 5 | #include "input.h" 6 | #include "game.h" 7 | #include "sound.h" 8 | #include "profile.h" 9 | 10 | int main(int argc, char **argv) { 11 | Mem_Init(); 12 | Sys_Init(); 13 | IN_Init(); 14 | CD_Init(); 15 | Snd_Init(); 16 | R_Init(); 17 | 18 | Mem_SetMark(MEM_MARK_LO); 19 | 20 | // default to normal skill 21 | gs.skill = 1; 22 | 23 | G_RequestMap("START"); 24 | 25 | while (1) { 26 | Sys_UpdateTime(); 27 | gs.frametime = Sys_FixedDeltaTime(); 28 | 29 | // if the map has changed, reset time 30 | if (G_CheckNextMap()) 31 | continue; 32 | 33 | Prf_StartFrame(); 34 | 35 | IN_Update(); 36 | G_Update(gs.frametime); 37 | CD_Update(); 38 | Snd_Update(&rs.origin, &rs.vright); 39 | R_UpdateLightStyles(gs.time); 40 | R_RenderView(); 41 | R_Flip(); 42 | 43 | Prf_EndFrame(); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qmdl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../common/psxtypes.h" 4 | #include "../common/idmdl.h" 5 | 6 | #define MAX_QMDLS 128 7 | #define MAX_QMDL_NAME 256 8 | 9 | typedef struct { 10 | char name[MAX_QMDL_NAME]; 11 | u8 *start; 12 | s16 id; 13 | size_t size; 14 | s32 viewmodel; 15 | qaliashdr_t *header; 16 | qaliasframe_t *frames; 17 | qaliastexcoord_t *texcoords; 18 | qaliastri_t *tris; 19 | qaliasskin_t *skins; 20 | } qmdl_t; 21 | 22 | extern int num_qmdls; 23 | extern qmdl_t qmdls[MAX_QMDLS]; 24 | 25 | int qmdlmap_init(const char *mapfile); 26 | int qmdlmap_id_for_name(const char *name); 27 | const char *qmdlmap_name_for_id(const int id); 28 | 29 | int qmdlprops_init(const char *propsfile); 30 | 31 | qmdl_t *qmdl_add(const int id, const char *name, u8 *start, const size_t size); 32 | qmdl_t *qmdl_find(const char *name); 33 | void qmdl_sort(qmdl_t *mdl); 34 | 35 | int qmdl_init(qmdl_t *mdl, u8 *start, const size_t size); 36 | void qmdl_free(qmdl_t *mdl); 37 | -------------------------------------------------------------------------------- /src/profile.c: -------------------------------------------------------------------------------- 1 | #include "profile.h" 2 | 3 | #ifdef INSTRUMENT_FUNCTIONS 4 | 5 | PRF_NO_INSTRUMENT static inline void PCSX_ExecSlot(const unsigned char slot) { 6 | *((volatile unsigned char* const)0x1f802081) = slot; 7 | } 8 | 9 | PRF_NO_INSTRUMENT void __cyg_profile_func_enter(void *this_fn, void *call_site) { 10 | register void *a0 asm("a0") = this_fn; 11 | register void *a1 asm("a1") = call_site; 12 | __asm__ volatile("" : : "r"(a0), "r"(a1)); 13 | PCSX_ExecSlot(0xfe); 14 | } 15 | 16 | PRF_NO_INSTRUMENT void __cyg_profile_func_exit(void *this_fn, void *call_site) { 17 | register void *a0 asm("a0") = this_fn; 18 | register void *a1 asm("a1") = call_site; 19 | __asm__ volatile("" : : "r"(a0), "r"(a1)); 20 | PCSX_ExecSlot(0xff); 21 | } 22 | 23 | #else 24 | 25 | #define PCSX_ExecSlot(a) 26 | 27 | #endif 28 | 29 | PRF_NO_INSTRUMENT void Prf_StartFrame(void) { 30 | PCSX_ExecSlot(0xfd); 31 | } 32 | 33 | PRF_NO_INSTRUMENT void Prf_EndFrame(void) { 34 | PCSX_ExecSlot(0xfc); 35 | } 36 | -------------------------------------------------------------------------------- /tools/common/psxmdl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | 5 | #pragma pack(push, 1) 6 | 7 | /* XMDL STRUCTURE 8 | * xaliashdr_t hdr; 9 | * xaliastri_t tris[hdr.numtris]; 10 | * xaliasvert_t frames[hdr.numframes][hdr.numverts]; 11 | */ 12 | 13 | #define MAX_XMDL_VERTS 512 14 | #define MAX_XMDL_FRAMES 256 15 | #define MAX_XMDL_TRIS 1024 16 | #define MAX_XMDL_SKINS 3 17 | 18 | // real coord = verts[i] * scale + offset 19 | typedef u8vec3_t xaliasvert_t; 20 | 21 | typedef u8vec2_t xaliastexcoord_t; 22 | 23 | typedef struct { 24 | u8vec2_t uvs[3]; 25 | u16 verts[3]; 26 | } xaliastri_t; 27 | 28 | typedef struct { 29 | u16 tpage; 30 | u8vec2_t base; 31 | } xaliasskin_t; 32 | 33 | typedef struct { 34 | u8 type; 35 | u8 flags; 36 | s16 id; 37 | u16 numframes; 38 | u16 numverts; 39 | u16 numtris; 40 | u16 numskins; 41 | x16vec3_t scale; 42 | s16vec3_t offset; 43 | x32vec3_t mins; 44 | x32vec3_t maxs; 45 | xaliasskin_t skins[MAX_XMDL_SKINS]; 46 | u32 trisofs; 47 | u32 framesofs; 48 | } xaliashdr_t; 49 | 50 | #pragma pack(pop) 51 | -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | typedef struct menuoption_s menuoption_t; 6 | typedef struct menu_s menu_t; 7 | typedef void (*menu_fn_t)(menuoption_t *self); 8 | 9 | enum optiontype_e { 10 | OPT_LABEL, 11 | OPT_BUTTON, 12 | OPT_CHOICE, 13 | OPT_SLIDER 14 | }; 15 | 16 | struct menuoption_s { 17 | u32 type; 18 | const char *title; 19 | void *value; 20 | menu_fn_t callback; 21 | union { 22 | struct { 23 | menu_t *nextmenu; 24 | } button; 25 | struct { 26 | const char **choices; 27 | s32 numchoices; 28 | } choice; 29 | struct { 30 | s32 min; 31 | s32 max; 32 | s32 step; 33 | } slider; 34 | struct { 35 | const char *path; 36 | const char *init; 37 | } file; 38 | }; 39 | }; 40 | 41 | struct menu_s { 42 | const char *title; 43 | menuoption_t *options; 44 | s32 numoptions; 45 | s32 selection; 46 | struct menu_s *prev; 47 | }; 48 | 49 | void Menu_Toggle(void); 50 | void Menu_Close(void); 51 | void Menu_Draw(void); 52 | void Menu_Update(void); 53 | 54 | qboolean Menu_IsOpen(void); 55 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // we're rolling our own stack allocator, but we'll leave some space for malloc 6 | // in case libc uses it or something 7 | 8 | #define MALLOC_HEAP_SIZE 16384 9 | #define STACK_SIZE 16384 10 | 11 | enum mem_mark { 12 | MEM_MARK_LO, 13 | MEM_MARK_HI, 14 | MEM_MARK_COUNT, 15 | }; 16 | 17 | void Mem_Init(void); 18 | 19 | // simple stack type allocator 20 | void *Mem_Alloc(const u32 size); 21 | void Mem_Free(void *ptr); 22 | 23 | // same as `mem_alloc`, but zero-fills the allocated area 24 | void *Mem_ZeroAlloc(const u32 size); 25 | 26 | // if `ptr` points to the top allocation, extends/shrinks it to `newsize` 27 | // if `ptr` is NULL, calls `mem_alloc(newsize)` 28 | void *Mem_Realloc(void *ptr, const u32 newsize); 29 | 30 | // marks current alloc position 31 | // mark can be MEM_MARK_LO or MEM_MARK_HI 32 | void Mem_SetMark(const int mark); 33 | 34 | // frees everything allocated after the mark set by last MemSetMark call with the same mark 35 | void Mem_FreeToMark(const int mark); 36 | 37 | // returns number of allocatable bytes 38 | u32 Mem_GetFreeSpace(void); 39 | -------------------------------------------------------------------------------- /tools/common/idmdl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | #include "idbsp.h" 5 | 6 | #define ALIAS_VERSION 6 7 | #define ALIAS_ONSEAM 0x0020 8 | 9 | #define MAX_SKINS 32 10 | #define MAX_FRAMENAME 16 11 | 12 | #pragma pack(push, 1) 13 | 14 | typedef struct { 15 | u8 v[3]; 16 | u8 lightnormalindex; 17 | } qtrivertx_t; 18 | 19 | typedef struct { 20 | s32 onseam; 21 | s32 s; 22 | s32 t; 23 | } qaliastexcoord_t; 24 | 25 | typedef struct { 26 | s32 front; 27 | s32 vertex[3]; 28 | } qaliastri_t; 29 | 30 | typedef struct { 31 | s32 type; 32 | qtrivertx_t min; 33 | qtrivertx_t max; 34 | char name[MAX_FRAMENAME]; 35 | qtrivertx_t *verts; 36 | } qaliasframe_t; 37 | 38 | typedef struct { 39 | s32 group; 40 | u8 *data; 41 | } qaliasskin_t; 42 | 43 | typedef struct { 44 | s32 ident; 45 | s32 version; 46 | qvec3_t scale; 47 | qvec3_t translate; 48 | f32 boundingradius; 49 | qvec3_t eyeposition; 50 | s32 numskins; 51 | s32 skinwidth; 52 | s32 skinheight; 53 | s32 numverts; 54 | s32 numtris; 55 | s32 numframes; 56 | s32 synctype; 57 | s32 flags; 58 | f32 size; 59 | } qaliashdr_t; 60 | 61 | #pragma pack(pop) 62 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qbsp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../common/psxtypes.h" 5 | #include "../common/idbsp.h" 6 | 7 | typedef struct { 8 | u8 *start; 9 | size_t size; 10 | 11 | qbsphdr_t *header; 12 | 13 | int numplanes; 14 | qplane_t *planes; 15 | 16 | int numnodes; 17 | qnode_t *nodes; 18 | 19 | int numcnodes; 20 | qclipnode_t *cnodes; 21 | 22 | int numleafs; 23 | qleaf_t *leafs; 24 | 25 | int nummarksurf; 26 | u16 *marksurf; 27 | 28 | int numvisdata; 29 | u8 *visdata; 30 | 31 | int numfaces; 32 | qface_t *faces; 33 | 34 | int numsurfedges; 35 | s32 *surfedges; 36 | 37 | int numedges; 38 | qedge_t *edges; 39 | 40 | int numtexinfos; 41 | qtexinfo_t *texinfos; 42 | 43 | int numverts; 44 | qvert_t *verts; 45 | 46 | int nummodels; 47 | qmodel_t *models; 48 | 49 | int numlightdata; 50 | u8 *lightdata; 51 | 52 | qmiptexlump_t *miptex; 53 | 54 | u8 *palette; 55 | } qbsp_t; 56 | 57 | extern qbsp_t qbsp; 58 | 59 | int qbsp_init(qbsp_t *qbsp, u8 *bsp, const size_t size); 60 | const qmiptex_t *qbsp_get_miptex(const qbsp_t *qbsp, const int i); 61 | u16 qbsp_light_for_vert(const qbsp_t *qbsp, const qface_t *qf, const qvec3_t v, qvec2_t sorg, qvec2_t sext, u16 *out); 62 | -------------------------------------------------------------------------------- /src/entity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include "world.h" 4 | #include "game.h" 5 | #include "system.h" 6 | #include "entity.h" 7 | 8 | edict_t *ED_Alloc(void) { 9 | int i; 10 | edict_t *e = gs.edicts + 1 + MAX_PLAYERS; 11 | for (i = 1 + MAX_PLAYERS; i < gs.num_edicts; ++i, ++e) { 12 | // the first couple seconds of server time can involve a lot of 13 | // freeing and allocating, so relax the replacement policy 14 | if (e->free && (e->freetime < FTOX(2.0) || gs.time - e->freetime > FTOX(0.5))) { 15 | memset(&e->v, 0, sizeof(e->v)); 16 | e->free = false; 17 | if (i > gs.max_edict) 18 | gs.max_edict = i; 19 | return e; 20 | } 21 | } 22 | 23 | Sys_Error("ED_Alloc(): no free edicts"); 24 | 25 | return NULL; 26 | } 27 | 28 | void ED_Free(edict_t *ed) { 29 | G_UnlinkEdict(ed); 30 | ed->free = true; 31 | ed->v.model = NULL; 32 | ed->v.modelnum = 0; 33 | ed->v.flags = 0; 34 | ed->v.frame = 0; 35 | ed->v.skin = 0; 36 | ed->v.origin = x32vec3_origin; 37 | ed->v.angles = x16vec3_origin; 38 | ed->v.nextthink = -1; 39 | ed->v.solid = SOLID_NOT; 40 | ed->v.touch = NULL; 41 | ed->v.think = NULL; 42 | ed->v.use = NULL; 43 | ed->v.classname = 0xff; 44 | ed->freetime = gs.time; 45 | } 46 | -------------------------------------------------------------------------------- /src/fixed.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "types.h" 5 | 6 | typedef s16 x16; // 1.03.12 fixed point 7 | typedef s32 x32; // 1.19.12 fixed point 8 | 9 | #define FIXSHIFT 12 10 | #define FIXSCALE (1 << 12) 11 | 12 | #ifndef ONE 13 | #define ONE FIXSCALE 14 | #endif 15 | 16 | #define ONEDEGREE (FIXSCALE / 360) 17 | 18 | #define HALF (ONE >> 1) 19 | 20 | #define TO_FIX32(x) ((x32)(x) << FIXSHIFT) 21 | #define TO_DEG16(x) (((x32)(x) << FIXSHIFT) / 360) 22 | 23 | // these will almost always overflow with 32-bit stuff, so only use them for x16 * x16 or x16 * x32 24 | #define XMUL16(x, y) (((x32)(x) * (x32)(y)) >> FIXSHIFT) 25 | #define XDIV16(x, y) (((x32)(x) << FIXSHIFT) / (x32)(y)) 26 | 27 | // only use for constants 28 | #define XRECIP(x) (x32)(((double)ONE) / ((double)(x))) 29 | #define FTOX(x) (x32)(((double)ONE) * ((double)(x))) 30 | 31 | // NOTE: these compile into trash without at least -O2 32 | 33 | #pragma GCC push_options 34 | #pragma GCC optimize("-O3") 35 | 36 | FORCEINLINE x32 xmul32(const x32 x, const x32 y) { 37 | return ((s64)x * y) >> FIXSHIFT; 38 | } 39 | 40 | FORCEINLINE x32 xdiv32(const x32 x, const x32 y) { 41 | return (((s64)x << FIXSHIFT) / y); 42 | } 43 | 44 | FORCEINLINE x32 xsign32(const x32 x) { 45 | return (x > 0) - (x < 0); 46 | } 47 | 48 | FORCEINLINE x32 xrand32(void) { 49 | return rand() & (ONE - 1); 50 | } 51 | 52 | #pragma GCC pop_options 53 | -------------------------------------------------------------------------------- /src/screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "render.h" 5 | 6 | #define MAX_SCR_LINE 128 7 | #define SCR_LINE_TIME TO_FIX32(2) 8 | #define SCR_FLASH_TIME (ONE / 3) 9 | 10 | #define FNT_SMALL_W 8 11 | #define FNT_SMALL_H 8 12 | #define FNT_BIG_W 24 13 | #define FNT_BIG_H 24 14 | 15 | #define C_WHITE 0x00808080u 16 | #define C_GREY 0x00404040u 17 | #define C_RED 0x00000080u 18 | #define C_DKRED 0x00000030u 19 | #define C_YELLOW 0x00008080u 20 | #define C_DKYELLOW 0x00003030u 21 | 22 | void Scr_Init(void); 23 | 24 | void Scr_DrawScreen(const int debug_mode); 25 | 26 | void Scr_DrawText(const s16 x, const s16 y, const u32 rgb, const char *str); 27 | void Scr_DrawTextOffset(const u8 chofs, const s16 x, const s16 y, const u32 rgb, const char *str); 28 | void Scr_DrawDigits(const s16 x, const s16 y, const u32 rgb, const char *str); 29 | void Scr_DrawPic(const s16 x, const s16 y, const u32 rgb, const pic_t *pic); 30 | void Scr_DrawRect(const s16 x, const s16 y, const s16 w, const s16 h, const u32 rgb, const u8 blend); 31 | void Scr_DrawBlendAdd(const u8 r, const u8 g, const u8 b); 32 | void Scr_DrawBlendHalf(const u8 r, const u8 g, const u8 b); 33 | 34 | void Scr_SetTopMsg(const char *str); 35 | void Scr_SetCenterMsg(const char *str); 36 | void Scr_SetBlend(const u32 color, const x32 time); 37 | 38 | void Scr_BeginLoading(void); 39 | void Scr_TickLoading(void); 40 | void Scr_EndLoading(void); 41 | -------------------------------------------------------------------------------- /src/sprite.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "render.h" 3 | #include "system.h" 4 | #include "sprite.h" 5 | 6 | #define GFX_FILE FS_BASE "\\GFX.DAT;1" 7 | 8 | static pic_t *spr_pics = NULL; 9 | static u16 spr_num_pics = 0; 10 | 11 | void Spr_Init(void) { 12 | int fhandle; 13 | const int vramsize = VRAM_PIC_WIDTH * VRAM_PIC_HEIGHT * 2; 14 | const int fsize = Sys_FileOpenRead(GFX_FILE, &fhandle); 15 | if (fsize < sizeof(spr_num_pics) + vramsize) 16 | Sys_Error("Spr_Init: invalid GFX.DAT"); 17 | 18 | // file starts with the CLUTs, upload them right away 19 | u16 *clut = Mem_Alloc(VID_NUM_CLUTS * VID_NUM_COLORS * sizeof(u16)); 20 | Sys_FileRead(fhandle, clut, VID_NUM_CLUTS * VID_NUM_COLORS * sizeof(u16)); 21 | R_UploadCluts(clut); 22 | Mem_Free(clut); 23 | 24 | Sys_FileRead(fhandle, &spr_num_pics, sizeof(spr_num_pics)); 25 | 26 | if (spr_num_pics) { 27 | spr_pics = Mem_Alloc(spr_num_pics * sizeof(*spr_pics)); 28 | Sys_FileRead(fhandle, spr_pics, spr_num_pics * sizeof(*spr_pics)); 29 | void *vramdata = Mem_Alloc(vramsize); 30 | Sys_FileRead(fhandle, vramdata, vramsize); 31 | R_UploadTexture(vramdata, VRAM_PIC_XSTART, 0, VRAM_PIC_WIDTH, VRAM_PIC_HEIGHT); 32 | Mem_Free(vramdata); 33 | } 34 | 35 | Sys_FileClose(fhandle); 36 | } 37 | 38 | const pic_t *Spr_GetPic(const int id) { 39 | if (!spr_pics || id <= 0 || id >= spr_num_pics) 40 | return NULL; 41 | return &spr_pics[id]; 42 | } 43 | -------------------------------------------------------------------------------- /src/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "common.h" 5 | 6 | #define MAX_ERROR 1024 7 | 8 | #define Sys_Printf(...) printf(__VA_ARGS__) 9 | #define ASSERT(x) if (!(x)) Sys_Error("ASSERTION FAILED:\n`%s` at %s:%d", #x, __FILE__, __LINE__) 10 | 11 | extern x32 sys_time; 12 | extern x16 sys_delta_time; 13 | extern u32 sys_last_counter; 14 | 15 | // initialize low level utilities 16 | void Sys_Init(void); 17 | 18 | // updates sys_time etc 19 | void Sys_UpdateTime(void); 20 | 21 | // r/o file operations 22 | int Sys_FileOpenRead(const char *path, int *hndl); 23 | void Sys_FileClose(int handle); 24 | void Sys_FileSeek(int handle, int position); 25 | int Sys_FileRead(int handle, void *dest, int count); 26 | qboolean Sys_FileExists(const char *fname); 27 | 28 | // an error will cause the entire program to exit 29 | void Sys_Error(const char *error, ...) __attribute__((noreturn)); 30 | 31 | // installs CPU exception handler (see exception.c) 32 | void Sys_InstallExceptionHandler(void); 33 | 34 | // returns vblanks since app start 35 | s32 Sys_Frames(void); 36 | 37 | // waits for n vblanks 38 | void Sys_Wait(int n); 39 | 40 | // returns seconds since app start in 1.19.12 fixed point 41 | static inline x32 Sys_FixedTime(void) { 42 | return sys_time; 43 | } 44 | 45 | // returns seconds since last Sys_UpdateTime call in 1.19.12 fixed point 46 | static inline x16 Sys_FixedDeltaTime(void) { 47 | return sys_delta_time; 48 | } 49 | -------------------------------------------------------------------------------- /tools/common/psxtypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef unsigned char u8; 4 | typedef unsigned short u16; 5 | typedef unsigned int u32; 6 | typedef signed char s8; 7 | typedef signed short s16; 8 | typedef signed int s32; 9 | typedef signed short x16; // 1.03.12 10 | typedef signed int x32; // 1.19.12 11 | typedef float f32; // not actually used on the psx, but eh 12 | 13 | #define FIXSHIFT 12 14 | #define FIXSCALE (1 << 12) 15 | #define ONE FIXSCALE 16 | #define HALF (ONE >> 1) 17 | 18 | static inline x32 xmul(const x32 x, const x32 y) { 19 | return (x * y) >> FIXSHIFT; 20 | } 21 | 22 | static inline x32 xdiv(const x32 x, const x32 y) { 23 | return (x * FIXSCALE) / y; 24 | } 25 | 26 | #pragma pack(push, 1) 27 | 28 | #define DECLARE_VEC3_T(ctype) \ 29 | typedef union ctype ## vec3_u { \ 30 | struct { ctype x, y, z; }; \ 31 | struct { ctype u, v, w; }; \ 32 | struct { ctype r, g, b; }; \ 33 | ctype d[3]; \ 34 | } ctype ## vec3_t 35 | 36 | #define DECLARE_VEC2_T(ctype) \ 37 | typedef union ctype ## vec2_u { \ 38 | struct { ctype x, y; }; \ 39 | struct { ctype u, v; }; \ 40 | ctype d[2]; \ 41 | } ctype ## vec2_t 42 | 43 | DECLARE_VEC3_T(x32); 44 | DECLARE_VEC3_T(s32); 45 | DECLARE_VEC3_T(x16); 46 | DECLARE_VEC3_T(s16); 47 | DECLARE_VEC3_T(u8); 48 | DECLARE_VEC3_T(s8); 49 | 50 | DECLARE_VEC2_T(x32); 51 | DECLARE_VEC2_T(s32); 52 | DECLARE_VEC2_T(x16); 53 | DECLARE_VEC2_T(s16); 54 | DECLARE_VEC2_T(u8); 55 | 56 | #pragma pack(pop) 57 | -------------------------------------------------------------------------------- /tools/scripts/cook_id1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while [[ $# -gt 0 ]]; do 4 | case $1 in 5 | -t|--rebuild-tools) rebuild_tools=1;; 6 | -g|--recook-gfx) recook_gfx=1;; 7 | -*|--*) echo "Unknown option $1"; exit 1;; 8 | *) id1path="$1";; 9 | esac 10 | shift 11 | done 12 | 13 | if [[ -z "$id1path" ]]; then 14 | echo "Specify path to id1 directory." 15 | exit 1 16 | fi 17 | 18 | if [[ ! -d ./tools/ ]]; then 19 | echo "Run this from the root quakepsx repo directory." 20 | exit 1 21 | fi 22 | 23 | # build tools if not present 24 | if [[ -n $rebuild_tools ]]; then 25 | rm ./tools/bin/* 26 | fi 27 | if [[ ! -d ./tools/bin/ ]]; then 28 | mkdir -p ./tools/bin/ || exit 1 29 | fi 30 | if [[ ! -f ./tools/bin/bspconvpsx.exe || ! -f ./tools/bin/mkpics.exe ]]; then 31 | pushd ./tools 32 | make -C bspconvpsx/ || exit 1 33 | make -C mkpics/ || exit 1 34 | popd 35 | fi 36 | 37 | # build GFX.DAT if not present 38 | if [[ ! -f ./id1psx/gfx.dat || -n "$recook_gfx" ]]; then 39 | ./tools/bin/mkpics.exe "$id1path" ./tools/cfg/id1 ./id1psx/gfx.dat || exit 1; 40 | fi 41 | 42 | # build maps 43 | maps=("start" "e1m1" "e1m2" "e1m3" "e1m4" "e1m5" "e1m6" "e1m7" "e1m8") 44 | # maps+=("e2m1" "e2m2" "e2m3" "e2m4" "e2m5" "e2m6" "e2m7") 45 | # maps+=("e3m1" "e3m2" "e3m3" "e3m4" "e3m5" "e3m6" "e3m7") 46 | # maps+=("e4m1" "e4m2" "e4m3" "e4m4" "e4m5" "e4m6" "e4m7" "e4m8") 47 | # maps+=("end") 48 | for m in "${maps[@]}"; do 49 | echo ":: converting $m" 50 | ./tools/bin/bspconvpsx.exe ./tools/cfg/id1 "$id1path" maps/$m.bsp ./id1psx/maps/$m.psb || exit 1 51 | done 52 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../common/psxtypes.h" 4 | #include "../common/idbsp.h" 5 | #include "../common/psxbsp.h" 6 | 7 | #define MAX_ALIASES 3 8 | #define MAX_ENT_MDLS 32 9 | #define MAX_ENT_SFX 32 10 | #define MAX_ENT_CLASSES 0x80 11 | #define MAX_ENT_CLASSNAME 64 12 | #define MAX_ENT_FIELDS 64 13 | 14 | enum qfieldtype_e { 15 | QFT_STRING, 16 | QFT_VECTOR, 17 | QFT_FLOAT, 18 | }; 19 | 20 | typedef struct { 21 | char classname[MAX_ENT_CLASSNAME]; 22 | 23 | int classnum; 24 | 25 | int num_mdlnums; 26 | int mdlnums[MAX_ENT_MDLS][MAX_ALIASES]; 27 | 28 | int num_sfxnums; 29 | int sfxnums[MAX_ENT_SFX][MAX_ALIASES]; 30 | } qentmap_t; 31 | 32 | typedef struct { 33 | int keylen; 34 | int valuelen; 35 | char *key; 36 | char *value; 37 | char strdata[]; 38 | } qentfield_t; 39 | 40 | typedef struct { 41 | int numfields; 42 | qentfield_t *fields[MAX_ENT_FIELDS]; 43 | const char *classname; // points to one of the fields' values 44 | const qentmap_t *info; 45 | } qent_t; 46 | 47 | extern int num_qents; 48 | extern qent_t qents[MAX_ENTITIES]; 49 | 50 | int qentmap_init(const char *mapfile); 51 | qentmap_t *qentmap_find(const char *classname); 52 | int qentmap_link(const char *resfile); 53 | 54 | void qent_load(const char *data, int datalen); 55 | 56 | const char *qent_get_string(qent_t *ent, const char *key); 57 | const char *qent_get_int(qent_t *ent, const char *key, int *out); 58 | const char *qent_get_float(qent_t *ent, const char *key, float *out); 59 | const char *qent_get_vector(qent_t *ent, const char *key, qvec3_t out); 60 | -------------------------------------------------------------------------------- /src/progs/picids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum picid_e { 4 | PICID_NONE = 0x00, 5 | PICID_CONCHARS = 0x01, 6 | PICID_NUM_0 = 0x02, 7 | PICID_NUM_1 = 0x03, 8 | PICID_NUM_2 = 0x04, 9 | PICID_NUM_3 = 0x05, 10 | PICID_NUM_4 = 0x06, 11 | PICID_NUM_5 = 0x07, 12 | PICID_NUM_6 = 0x08, 13 | PICID_NUM_7 = 0x09, 14 | PICID_NUM_8 = 0x0a, 15 | PICID_NUM_9 = 0x0b, 16 | PICID_NUM_SLASH = 0x0c, 17 | PICID_NUM_COLON = 0x0d, 18 | PICID_FACE5 = 0x0e, 19 | PICID_FACE4 = 0x0f, 20 | PICID_FACE3 = 0x10, 21 | PICID_FACE2 = 0x11, 22 | PICID_FACE1 = 0x12, 23 | PICID_FACE_P5 = 0x13, 24 | PICID_FACE_P4 = 0x14, 25 | PICID_FACE_P3 = 0x15, 26 | PICID_FACE_P2 = 0x16, 27 | PICID_FACE_P1 = 0x17, 28 | PICID_FACE_QUAD = 0x18, 29 | PICID_FACE_INVIS = 0x19, 30 | PICID_FACE_INVUL2 = 0x1a, 31 | PICID_FACE_INV2 = 0x1b, 32 | PICID_QPLAQUE = 0x1c, 33 | PICID_SB_ARMOR1 = 0x1d, 34 | PICID_SB_ARMOR2 = 0x1e, 35 | PICID_SB_ARMOR3 = 0x1f, 36 | PICID_SB_SHELLS = 0x20, 37 | PICID_SB_NAILS = 0x21, 38 | PICID_SB_ROCKET = 0x22, 39 | PICID_SB_CELLS = 0x23, 40 | PICID_SB_KEY1 = 0x24, 41 | PICID_SB_KEY2 = 0x25, 42 | PICID_SB_SIGIL1 = 0x26, 43 | PICID_SB_SIGIL2 = 0x27, 44 | PICID_SB_SIGIL3 = 0x28, 45 | PICID_SB_SIGIL4 = 0x29, 46 | PICID_SBA1_INVIS = 0x2a, 47 | PICID_SBA1_INVUL = 0x2b, 48 | PICID_SBA1_SUIT = 0x2c, 49 | PICID_SBA1_QUAD = 0x2d, 50 | PICID_MENUDOT1 = 0x2e, 51 | PICID_MENUDOT2 = 0x2f, 52 | PICID_MENUDOT3 = 0x30, 53 | PICID_MENUDOT4 = 0x31, 54 | PICID_MENUDOT5 = 0x32, 55 | PICID_MENUDOT6 = 0x33, 56 | PICID_PAUSE = 0x34, 57 | PICID_DISC = 0x35, 58 | }; 59 | -------------------------------------------------------------------------------- /tools/common/pak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "psxtypes.h" 7 | #include "util.h" 8 | #include "idpak.h" 9 | #include "pak.h" 10 | 11 | int pak_open(pak_t *pak, const char *fname) { 12 | FILE *f = fopen(fname, "rb"); 13 | if (!f) return -1; 14 | 15 | fread(&pak->header, sizeof(pak->header), 1, f); 16 | if (pak->header.id != PACK_ID) { fclose(f); return -2; } 17 | 18 | pak->numfiles = pak->header.dirlen / sizeof(qpackfile_t); 19 | assert(pak->numfiles <= MAX_FILES_IN_PACK); 20 | 21 | fseek(f, pak->header.dirofs, SEEK_SET); 22 | fread(pak->files, sizeof(*pak->files), pak->numfiles, f); 23 | 24 | pak->fp = f; 25 | return 0; 26 | } 27 | 28 | void pak_close(pak_t *pak) { 29 | fclose(pak->fp); 30 | memset(pak, 0, sizeof(*pak)); 31 | } 32 | 33 | u8 *pak_readfile(pak_t *pak, const char *fname, size_t *outsize) { 34 | for (int i = 0; i < pak->numfiles; ++i) { 35 | if (!strncmp(fname, pak->files[i].name, MAX_PACK_PATH)) { 36 | u8 *buf = malloc(pak->files[i].filelen); 37 | assert(buf); 38 | fseek(pak->fp, pak->files[i].filepos, SEEK_SET); 39 | fread(buf, pak->files[i].filelen, 1, pak->fp); 40 | if (outsize) *outsize = pak->files[i].filelen; 41 | return buf; 42 | } 43 | } 44 | return NULL; 45 | } 46 | 47 | const char *pak_findfile(pak_t *pak, const char *match, const char *prev) { 48 | if (!prev || pak->findptr >= MAX_FILES_IN_PACK) 49 | pak->findptr = 0; 50 | for (; pak->findptr < MAX_FILES_IN_PACK; ++pak->findptr) { 51 | if (strstr(pak->files[pak->findptr].name, match)) 52 | return pak->files[pak->findptr++].name; 53 | } 54 | return NULL; 55 | } 56 | -------------------------------------------------------------------------------- /tools/common/wad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "psxtypes.h" 8 | #include "util.h" 9 | #include "idwad.h" 10 | #include "wad.h" 11 | 12 | int wad_open(wad_t *wad, u8 *data, const size_t size) { 13 | qwadinfo_t *header = (qwadinfo_t *)data; 14 | 15 | if (size < sizeof(*header) || memcmp(header->ident, WAD_IDENT, sizeof(header->ident)) != 0) 16 | return -1; 17 | 18 | assert(size >= header->infotableofs + header->numlumps * sizeof(qlumpinfo_t)); 19 | 20 | wad->info = header; 21 | wad->lumps = (qlumpinfo_t *)(data + header->infotableofs); 22 | wad->data = data; 23 | wad->size = size; 24 | 25 | return 0; 26 | } 27 | 28 | void wad_close(wad_t *wad) { 29 | wad->data = NULL; 30 | wad->size = 0; 31 | wad->lumps = NULL; 32 | free(wad->info); 33 | wad->info = NULL; 34 | } 35 | 36 | u8 *wad_get_lump(wad_t *wad, const char *lname, size_t *outsize) { 37 | if (!wad || !wad->info || !wad->lumps || !wad->size || !wad->info->numlumps) 38 | return NULL; 39 | 40 | char upper[WAD_MAX_LUMPNAME + 1]; 41 | int i; 42 | for (i = 0; i < WAD_MAX_LUMPNAME && lname[i]; ++i) 43 | upper[i] = toupper(lname[i]); 44 | upper[i] = '\0'; 45 | 46 | for (s32 i = 0; i < wad->info->numlumps; ++i) { 47 | if (!strncmp(wad->lumps[i].name, upper, WAD_MAX_LUMPNAME)) { 48 | if (outsize) *outsize = wad->lumps[i].size; 49 | return wad->data + wad->lumps[i].filepos; 50 | } 51 | } 52 | 53 | return NULL; 54 | } 55 | 56 | u8 *wad_get_lump_by_num(wad_t *wad, const int lid, size_t *outsize) { 57 | if (!wad || !wad->info || !wad->lumps || !wad->size || !wad->info->numlumps || lid < 0 || lid >= wad->info->numlumps) 58 | return NULL; 59 | if (outsize) *outsize = wad->lumps[lid].size; 60 | return wad->data + wad->lumps[lid].filepos; 61 | } 62 | -------------------------------------------------------------------------------- /iso.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/exception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #define R_R0 0 6 | #define R_R1 1 7 | #define R_R2 2 8 | #define R_R3 3 9 | #define R_R4 4 10 | #define R_R5 5 11 | #define R_R6 6 12 | #define R_R7 7 13 | #define R_R8 8 14 | #define R_R9 9 15 | #define R_R10 10 16 | #define R_R11 11 17 | #define R_R12 12 18 | #define R_R13 13 19 | #define R_R14 14 20 | #define R_R15 15 21 | #define R_R16 16 22 | #define R_R17 17 23 | #define R_R18 18 24 | #define R_R19 19 25 | #define R_R20 20 26 | #define R_R21 21 27 | #define R_R22 22 28 | #define R_R23 23 29 | #define R_R24 24 30 | #define R_R25 25 31 | #define R_R26 26 32 | #define R_R27 27 33 | #define R_R28 28 34 | #define R_R29 29 35 | #define R_R30 30 36 | #define R_R31 31 37 | #define R_EPC 32 38 | #define R_MDHI 33 39 | #define R_MDLO 34 40 | #define R_SR 35 41 | #define R_CAUSE 36 42 | #define NREGS 40 43 | 44 | #define R_ZERO R_R0 45 | #define R_AT R_R1 46 | #define R_V0 R_R2 47 | #define R_V1 R_R3 48 | #define R_A0 R_R4 49 | #define R_A1 R_R5 50 | #define R_A2 R_R6 51 | #define R_A3 R_R7 52 | #define R_T0 R_R8 53 | #define R_T1 R_R9 54 | #define R_T2 R_R10 55 | #define R_T3 R_R11 56 | #define R_T4 R_R12 57 | #define R_T5 R_R13 58 | #define R_T6 R_R14 59 | #define R_T7 R_R15 60 | #define R_S0 R_R16 61 | #define R_S1 R_R17 62 | #define R_S2 R_R18 63 | #define R_S3 R_R19 64 | #define R_S4 R_R20 65 | #define R_S5 R_R21 66 | #define R_S6 R_R22 67 | #define R_S7 R_R23 68 | #define R_T8 R_R24 69 | #define R_T9 R_R25 70 | #define R_K0 R_R26 71 | #define R_K1 R_R27 72 | #define R_GP R_R28 73 | #define R_SP R_R29 74 | #define R_FP R_R30 75 | #define R_RA R_R31 76 | 77 | struct ToT { 78 | u32 *head; 79 | s32 size; 80 | }; 81 | 82 | struct TCBH { 83 | struct TCB *entry; 84 | s32 flag; 85 | }; 86 | 87 | struct TCB { 88 | s32 status; 89 | s32 mode; 90 | u32 reg[NREGS]; 91 | s32 system[6]; 92 | }; 93 | 94 | #define EvSpTRAP 0x1000 95 | -------------------------------------------------------------------------------- /src/move.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "entity.h" 5 | #include "world.h" 6 | 7 | #define MOVE_NORMAL 0 8 | #define MOVE_NOMONSTERS 1 9 | #define MOVE_MISSILE 2 10 | 11 | #define G_MAXSPEED TO_FIX32(320) 12 | #define G_ACCELERATE TO_FIX32(10) 13 | #define G_FRICTION TO_FIX32(4) 14 | #define G_STOPSPEED TO_FIX32(100) 15 | #define G_GRAVITY 800 16 | 17 | #define G_FORWARDSPEED TO_FIX32(200) 18 | #define G_JUMPSPEED TO_FIX32(270) 19 | #define G_YAWSPEED TO_DEG16(120) 20 | #define G_PITCHSPEED TO_DEG16(130) 21 | 22 | #define G_FLOORNORMALZ 2867 // TO_FIX32(0.7) 23 | 24 | #define G_STEPSIZE TO_FIX32(18) 25 | 26 | typedef struct { 27 | x32vec3_t boxmins, boxmaxs; // enclose the test object along entire move 28 | const x32vec3_t *mins, *maxs; // size of the moving object 29 | x32vec3_t mins2, maxs2; // size when clipping against mosnters 30 | const x32vec3_t *start, *end; 31 | trace_t trace, tmptrace; 32 | int type; 33 | const edict_t *passedict; 34 | } moveclip_t; 35 | 36 | typedef struct { 37 | x32vec3_t wishvel; 38 | x16vec3_t wishdir; 39 | x16vec3_t right; 40 | x16vec3_t up; 41 | x32vec3_t *origin; 42 | x32vec3_t *velocity; 43 | x32 wishspeed; 44 | qboolean onground; 45 | } moveplayer_t; 46 | 47 | typedef struct { 48 | moveclip_t clip; 49 | moveplayer_t pm; 50 | } movevars_t; 51 | 52 | // this resides in the scratch and is used by G_Move and G_PlayerMove 53 | extern movevars_t *const movevars; 54 | 55 | edict_t *G_TestEntityPosition(const edict_t *ent); 56 | const trace_t *G_Move(const x32vec3_t *start, const x32vec3_t *mins, const x32vec3_t *maxs, const x32vec3_t *end, const int type, const edict_t *passedict); 57 | 58 | int G_FlyMove(edict_t *ent, x16 time, const trace_t **steptrace); 59 | void G_WalkMove(edict_t *ent); 60 | void G_PushMove(edict_t *pusher, x16 time); 61 | 62 | qboolean G_DropToFloor(edict_t *ent); 63 | qboolean G_CheckBottom(edict_t *ent); 64 | 65 | void PM_PlayerMove(const x16 dt); 66 | 67 | void G_Physics(void); 68 | -------------------------------------------------------------------------------- /src/sound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "spu.h" 5 | 6 | // we downsample everything to this 7 | #define SND_FREQ 11025 8 | 9 | #define SND_MAXVOL 250 10 | 11 | #define SND_NUM_CH (SPU_NUM_VOICES) 12 | #define SND_NUM_CH_AMBIENT 11 13 | #define SND_NUM_CH_STATIC (1 + SND_NUM_CH_AMBIENT) // 1 for CD audio 14 | #define SND_NUM_CH_DYNAMIC (SND_NUM_CH - SND_NUM_CH_STATIC) 15 | 16 | #define SND_CH_CDMUSIC 0 17 | #define SND_CH_AMBIENT0 1 18 | #define SND_CH_DYNAMIC0 SND_NUM_CH_STATIC 19 | 20 | #define SND_CLIPDIST 1024 // integer 21 | #define SND_INV_CLIPDIST XRECIP(SND_CLIPDIST) 22 | #define SND_STATICSCALE XRECIP(64) 23 | 24 | #define ATTN_NONE 0 25 | #define ATTN_NORM TO_FIX32(1) 26 | #define ATTN_IDLE TO_FIX32(2) 27 | #define ATTN_STATIC TO_FIX32(3) 28 | 29 | #define CHAN_AUTO 0 30 | #define CHAN_WEAPON 1 31 | #define CHAN_VOICE 2 32 | #define CHAN_ITEM 3 33 | #define CHAN_BODY 4 34 | 35 | typedef struct { 36 | s16 id; 37 | u16 frames; 38 | u32 spuaddr; 39 | } sfx_t; 40 | 41 | // 0 - 128 42 | extern s32 snd_volume; 43 | 44 | void Snd_Init(void); 45 | 46 | void Snd_SetBank(const sfx_t *sfx, const int num_sfx, const u8 *spudata, const u32 spudata_size); 47 | void Snd_ResetBank(void); 48 | 49 | void Snd_NewMap(void); 50 | 51 | const sfx_t *Snd_FindSound(const s16 id); 52 | void Snd_StartSound(const s16 entnum, const s16 entch, const sfx_t *sfx, const x32vec3_t *origin, s16 vol, x32 attn); 53 | void Snd_StaticSound(const sfx_t *sfx, const x32vec3_t *origin, s16 vol, x32 attn); 54 | 55 | void Snd_Update(x32vec3_t *lorigin, x16vec3_t *lright); 56 | 57 | void Snd_SetVolume(const s32 vol); 58 | 59 | static inline void Snd_StartSoundId(const s16 entnum, const s16 entch, const s16 sfxid, const x32vec3_t *origin, s16 vol, x32 attn) { 60 | Snd_StartSound(entnum, entch, Snd_FindSound(sfxid), origin, vol, attn); 61 | } 62 | 63 | static inline void Snd_StaticSoundId(const s16 sfxid, const x32vec3_t *origin, s16 vol, x32 attn) { 64 | Snd_StaticSound(Snd_FindSound(sfxid), origin, vol, attn); 65 | } 66 | -------------------------------------------------------------------------------- /tools/cfg/id1/picmap.txt: -------------------------------------------------------------------------------- 1 | # pics without path are taken from gfx.wad 2 | # pic NAME VRAM_X VRAM_Y [COLORKEY=0xFF] 3 | 4 | # conchars has a different transparent color for some reason 5 | pic conchars 960 0 0 6 | 7 | # big HUD numbers 8 | pic num_0 960 128 9 | pic num_1 972 128 10 | pic num_2 984 128 11 | pic num_3 996 128 12 | pic num_4 1008 128 13 | pic num_5 960 152 14 | pic num_6 972 152 15 | pic num_7 984 152 16 | pic num_8 996 152 17 | pic num_9 1008 152 18 | pic num_slash 960 176 19 | pic num_colon 968 176 20 | 21 | # regular faces (the order is actually inverted for some reason) 22 | pic face5 960 200 23 | pic face4 972 200 24 | pic face3 984 200 25 | pic face2 996 200 26 | pic face1 1008 200 27 | 28 | # pain faces (the order is actually inverted for some reason) 29 | pic face_p5 960 224 30 | pic face_p4 972 224 31 | pic face_p3 984 224 32 | pic face_p2 996 224 33 | pic face_p1 1008 224 34 | 35 | # powerup faces; last is invis + invul 36 | pic face_quad 976 176 37 | pic face_invis 988 176 38 | pic face_invul2 1000 176 39 | pic face_inv2 1012 176 40 | 41 | # quake logo bar 42 | pic gfx/qplaque.lmp 960 256 43 | 44 | # items 45 | pic sb_armor1 976 256 46 | pic sb_armor2 988 256 47 | pic sb_armor3 1000 256 48 | pic sb_shells 1012 256 49 | pic sb_nails 976 280 50 | pic sb_rocket 988 280 51 | pic sb_cells 1000 280 52 | pic sb_key1 976 304 53 | pic sb_key2 984 304 54 | pic sb_sigil1 992 304 55 | pic sb_sigil2 996 304 56 | pic sb_sigil3 1000 304 57 | pic sb_sigil4 1004 304 58 | pic sba1_invis 1008 304 59 | pic sba1_invul 1016 304 60 | pic sba1_suit 976 320 61 | pic sba1_quad 984 320 62 | 63 | # spinny quake icon 64 | pic gfx/menudot1.lmp 976 352 65 | pic gfx/menudot2.lmp 984 352 66 | pic gfx/menudot3.lmp 992 352 67 | pic gfx/menudot4.lmp 1000 352 68 | pic gfx/menudot5.lmp 1008 352 69 | pic gfx/menudot6.lmp 1016 352 70 | 71 | # PAUSED 72 | pic gfx/pause.lmp 960 400 73 | 74 | # loading icon has to be in 16bpp so we could blit it with one MoveImage call 75 | pic16 disc 960 424 76 | -------------------------------------------------------------------------------- /src/world.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "entity.h" 5 | 6 | #define AREA_DEPTH 4 7 | #define AREA_NODES 32 8 | 9 | typedef struct { 10 | x16vec3_t normal; 11 | x32 dist; 12 | } plane_t; 13 | 14 | typedef struct { 15 | qboolean allsolid; // if true, plane is not valid 16 | qboolean startsolid; // if true, the initial point was in a solid area 17 | qboolean inopen; 18 | qboolean inwater; 19 | x16 fraction; // time completed, 1.0 = didn't hit anything 20 | x32vec3_t endpos; // final position 21 | plane_t plane; // surface normal at impact 22 | edict_t *ent; // entity the surface is on 23 | } trace_t; 24 | 25 | typedef struct areanode_s 26 | { 27 | s32 axis; // -1 = leaf node 28 | x32 dist; 29 | struct areanode_s *children[2]; 30 | link_t trigger_edicts; 31 | link_t solid_edicts; 32 | } areanode_t; 33 | 34 | extern areanode_t g_areanodes[AREA_NODES]; 35 | extern int g_numareanodes; 36 | 37 | void G_InitBoxHull(void); 38 | hull_t *G_HullForEntity(const edict_t *ent, const x32vec3_t *mins, const x32vec3_t *maxs, x32vec3_t *offset); 39 | 40 | int G_HullPointContents(const hull_t *hull, int num, const x32vec3_t *p); 41 | int G_PointContents(const x32vec3_t *p); 42 | int G_TruePointContents(const x32vec3_t *p); 43 | 44 | qboolean G_RecursiveHullCheck(const hull_t *hull, const int num, const x32 p1f, const x32 p2f, const x32vec3_t *p1, const x32vec3_t *p2, trace_t *trace); 45 | 46 | void G_ClearWorld(void); 47 | void G_UnlinkEdict(edict_t *ent); 48 | void G_LinkEdict(edict_t *ent, const qboolean touch_triggers); 49 | 50 | void G_MoveBounds(const x32vec3_t *start, const x32vec3_t *mins, const x32vec3_t *maxs, const x32vec3_t *end, x32vec3_t *boxmins, x32vec3_t *boxmaxs); 51 | 52 | // NOTE: radius is an integer 53 | edict_t *G_FindInRadius(const x32vec3_t *origin, const s32 radius); 54 | edict_t *G_FindByTarget(edict_t *start, const u16 target); 55 | edict_t *G_FindByTargetname(edict_t *start, const u16 targetname); 56 | edict_t *G_FindByClassname(edict_t *start, const u8 classname); 57 | edict_t *G_CheckClient(edict_t *ent); 58 | -------------------------------------------------------------------------------- /src/game.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "model.h" 5 | #include "entity.h" 6 | 7 | // progs frame time 8 | #define PR_FRAMETIME 410 // ~0.1 sec 9 | 10 | #define MAX_AMMO_TYPES 4 11 | #define MAX_POWERUPS 5 12 | 13 | enum player_flags_e { 14 | PFL_JUMPED = (1 << 0), 15 | PFL_INWATER = (1 << 1), 16 | PFL_NOAIR = (1 << 2), 17 | }; 18 | 19 | typedef struct stats_s { 20 | s16 armor; 21 | s16 armortype; 22 | s16 weaponnum; 23 | s16 ammonum; 24 | u32 items; 25 | s16 ammo[MAX_AMMO_TYPES]; 26 | } stats_t; 27 | 28 | typedef struct player_state_s { 29 | stats_t stats; 30 | x16vec3_t viewangles; 31 | x16vec3_t anglemove; 32 | s16vec3_t vmodelofs; 33 | x32vec3_t move; 34 | x32vec3_t waterjump; 35 | x32vec3_t viewofs; 36 | edict_t *ent; 37 | amodel_t *vmodel; 38 | think_fn_t vmodel_think; 39 | x32 fallspeed; 40 | x32 show_hostile; 41 | x32 attack_finished; 42 | x32 pain_finished; 43 | x32 air_finished; 44 | x32 teleport_time; 45 | x32 dmg_time; 46 | x32 power_time[MAX_POWERUPS]; 47 | x32 power_warn[MAX_POWERUPS]; 48 | u32 buttons; 49 | u16 flags; 50 | s16 vmodel_frame; 51 | s16 vmodel_end_frame; 52 | x16 punchangle; 53 | } player_state_t; 54 | 55 | typedef struct game_state_s { 56 | x32 time; 57 | x32 lastchecktime; 58 | s32 lastcheck; 59 | x16 frametime; 60 | s16 num_edicts; 61 | s16 max_edict; 62 | s16 force_retouch; 63 | s16 skill; 64 | s16 gravity; 65 | qboolean paused; 66 | bmodel_t *worldmodel; 67 | bmodel_t **bmodels; 68 | amodel_t *amodels; 69 | edict_t *edicts; 70 | player_state_t player[MAX_PLAYERS]; 71 | } game_state_t; 72 | 73 | extern game_state_t gs; 74 | 75 | extern char g_map[MAX_OSPATH]; 76 | 77 | void G_NewGame(void); 78 | void G_RestartMap(void); 79 | void G_RequestMap(const char *mapname); 80 | qboolean G_CheckNextMap(void); 81 | void G_StartMap(const char *path); 82 | void G_Update(const x16 dt); 83 | 84 | amodel_t *G_FindAliasModel(const s16 modelid); 85 | void G_SetModel(edict_t *ent, s16 modelnum); 86 | void G_SetSize(edict_t *ent, const x32vec3_t *mins, const x32vec3_t *maxs); 87 | -------------------------------------------------------------------------------- /src/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "input.h" 6 | 7 | input_t in; 8 | 9 | static u8 pad_buff[2][34]; 10 | static PADTYPE *pad[2]; 11 | 12 | void IN_Init(void) { 13 | InitPAD(&pad_buff[0][0], 34, &pad_buff[1][0], 34); 14 | StartPAD(); 15 | 16 | // don't make pad driver acknowledge vblank IRQ 17 | ChangeClearPAD(0); 18 | pad[0] = (PADTYPE *)&pad_buff[0][0]; 19 | pad[1] = (PADTYPE *)&pad_buff[1][0]; 20 | 21 | // default sensitivity 22 | in.stick_sens[0].x = 2; 23 | in.stick_sens[0].y = 2; 24 | in.stick_sens[1].x = 16; 25 | in.stick_sens[1].y = 12; 26 | in.mouse_sens = 32; 27 | 28 | // default deadzone 29 | in.stick_deadzone[0] = 16; 30 | in.stick_deadzone[1] = 16; 31 | } 32 | 33 | static inline void IN_UpdateAnalog(const PADTYPE *pad) { 34 | const u8 sx[2] = { pad->ls_x, pad->rs_x }; 35 | const u8 sy[2] = { pad->ls_y, pad->rs_y }; 36 | 37 | for (int i = 0; i < 2; ++i) { 38 | const s16 x = sx[i] - 0x80; 39 | const s16 y = sy[i] - 0x80; 40 | const s16 dead = in.stick_deadzone[i]; 41 | if ((x * x + y * y) < dead * dead) { 42 | in.sticks[i].x = 0; 43 | in.sticks[i].y = 0; 44 | } else { 45 | in.sticks[i].x = x; 46 | in.sticks[i].y = y; 47 | } 48 | } 49 | } 50 | 51 | static inline void IN_UpdateMouse(const PADTYPE *pad) { 52 | in.mouse.x = pad->x_mov; 53 | in.mouse.y = pad->y_mov; 54 | 55 | // HACK: for now translate mouse buttons to L2/R2 56 | const u16 btn = ~pad->btn; 57 | if (btn & PAD_R1) 58 | in.btn |= PAD_R2; 59 | if (btn & PAD_L1) 60 | in.btn |= PAD_L2; 61 | } 62 | 63 | void IN_Update(void) { 64 | if (pad[0]->stat != 0) 65 | return; 66 | 67 | in.btn_prev = in.btn; 68 | in.btn = ~pad[0]->btn; 69 | 70 | // if port 1 is an analog pad, update the sticks 71 | if (pad[0]->type == PAD_ID_ANALOG) 72 | IN_UpdateAnalog(pad[0]); 73 | 74 | // if port 2 is a mouse, update the mouse deltas 75 | if (pad[1]->stat == 0 && pad[1]->type == PAD_ID_MOUSE) 76 | IN_UpdateMouse(pad[1]); 77 | 78 | in.btn_trig = ~in.btn_prev & in.btn; 79 | } 80 | 81 | void IN_Clear(void) { 82 | in.btn_trig = 0; 83 | } 84 | -------------------------------------------------------------------------------- /tools/cfg/id1/entmap.txt: -------------------------------------------------------------------------------- 1 | 00 worldspawn 2 | 01 player 3 | 02 air_bubbles 4 | 03 ambient_comp_hum 5 | 04 ambient_drip 6 | 05 ambient_drone 7 | 06 ambient_suck_wind 8 | 07 ambient_swamp1 9 | 08 ambient_swamp2 10 | 09 event_lightning 11 | 0a func_bossgate 12 | 0b func_button 13 | 0c func_door 14 | 0d func_door_secret 15 | 0e func_episodegate 16 | 0f func_illusionary 17 | 10 func_plat 18 | 11 func_train 19 | 12 func_wall 20 | 13 info_intermission 21 | 14 info_null 22 | 15 info_player_coop 23 | 16 info_player_deathmatch 24 | 17 info_player_start 25 | 18 info_player_start2 26 | 19 info_teleport_destination 27 | 1a item_armor1 28 | 1b item_armor2 29 | 1c item_armorInv 30 | 1d item_artifact_envirosuit 31 | 1e item_artifact_invisibility 32 | 1f item_artifact_invulnerability 33 | 20 item_artifact_super_damage 34 | 21 item_cells 35 | 22 item_health 36 | 23 item_key1 37 | 24 item_key2 38 | 25 item_rockets 39 | 26 item_shells 40 | 27 item_sigil 41 | 28 item_spikes 42 | 29 item_weapon 43 | 2a light 44 | 2b light_flame_large_yellow 45 | 2c light_flame_small_white 46 | 2d light_flame_small_yellow 47 | 2e light_fluoro 48 | 2f light_fluorospark 49 | 30 light_globe 50 | 31 light_torch_small_walltorch 51 | 32 misc_explobox 52 | 33 misc_explobox2 53 | 34 misc_fireball 54 | 35 misc_teleporttrain 55 | 36 monster_army 56 | 37 monster_boss 57 | 38 monster_demon1 58 | 39 monster_dog 59 | 3a monster_enforcer 60 | 3b monster_fish 61 | 3c monster_hell_knight 62 | 3d monster_knight 63 | 3e monster_ogre 64 | 3f monster_oldone 65 | 40 monster_shalrath 66 | 41 monster_shambler 67 | 42 monster_tarbaby 68 | 43 monster_wizard 69 | 44 monster_zombie 70 | 45 path_corner 71 | 46 trap_spikeshooter 72 | 47 trigger_changelevel 73 | 48 trigger_counter 74 | 49 trigger_hurt 75 | 4a trigger_monsterjump 76 | 4b trigger_multiple 77 | 4c trigger_once 78 | 4d trigger_onlyregistered 79 | 4e trigger_push 80 | 4f trigger_relay 81 | 50 trigger_secret 82 | 51 trigger_setskill 83 | 52 trigger_teleport 84 | 53 weapon_grenadelauncher 85 | 54 weapon_lightning 86 | 55 weapon_nailgun 87 | 56 weapon_rocketlauncher 88 | 57 weapon_supernailgun 89 | 58 weapon_supershotgun 90 | 59 temp_entity 91 | 5a spike 92 | 5b grenade 93 | 5c rocket 94 | 5d delayed_use 95 | 5e viewthing 96 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(quakepsx LANGUAGES C ASM) 4 | 5 | option(INSTRUMENT_FUNCTIONS "Compile with -finstrument-functions and enable Redux profiler" OFF) 6 | option(ENABLE_LTO "Compile with -flto" ON) 7 | option(WITH_CDDA "Build full image with CD Audio tracks (requires said tracks)" OFF) 8 | 9 | file(GLOB _sources src/*.c src/progs/*.c src/*.s) 10 | 11 | include_directories(src) 12 | 13 | add_compile_options(-Wall -Wextra -Wno-strict-aliasing -Wno-unused-parameter -Wno-sign-compare) 14 | 15 | set(OPT_FLAGS 16 | # replace the default -O2 with -O3 17 | -O3 -freg-struct-return -fsection-anchors 18 | # these can apparently result in slower code and they don't seem to make much of a size difference 19 | -fno-data-sections -fno-function-sections 20 | # we check for zero division by hand when necessary 21 | -mno-check-zero-division 22 | # alignment makes no difference 23 | -flimit-function-alignment -fno-align-functions -fno-align-labels -fno-align-jumps -fno-align-loops 24 | # we have no data cache 25 | --param=l1-cache-line-size=0 --param=l1-cache-size=0 --param=l2-cache-size=0 26 | # don't care about any data races 27 | -fallow-store-data-races 28 | # don't care about this since we do no dynamic linking 29 | -fno-semantic-interposition 30 | ) 31 | # this can't fit into the above list because it has a comma 32 | add_compile_options(-Wa,--strip-local-absolute) 33 | 34 | if(INSTRUMENT_FUNCTIONS) 35 | add_compile_options(-finstrument-functions) 36 | add_definitions(-DINSTRUMENT_FUNCTIONS) 37 | elseif(ENABLE_LTO) 38 | add_compile_options(-flto -fwhole-program) 39 | add_link_options(-flto -fwhole-program) 40 | endif() 41 | 42 | if(WITH_CDDA) 43 | message(STATUS "CD audio enabled.") 44 | set(ISO_CONFIG "iso_cdda.xml") 45 | else() 46 | set(ISO_CONFIG "iso.xml") 47 | endif() 48 | 49 | get_target_property(OLD_OPTS psn00bsdk INTERFACE_COMPILE_OPTIONS) 50 | string(REPLACE "-O2" "${OPT_FLAGS}" NEW_OPTS "${OLD_OPTS}") 51 | set_target_properties(psn00bsdk PROPERTIES INTERFACE_COMPILE_OPTIONS "${NEW_OPTS}") 52 | 53 | psn00bsdk_add_executable(quakepsx GPREL ${_sources}) 54 | psn00bsdk_add_cd_image(quakepsx_iso quakepsx ${ISO_CONFIG} DEPENDS quakepsx) 55 | -------------------------------------------------------------------------------- /tools/cfg/id1/mdlmap.txt: -------------------------------------------------------------------------------- 1 | 01 maps/b_batt0.bsp 2 | 02 maps/b_batt1.bsp 3 | 03 maps/b_bh10.bsp 4 | 04 maps/b_bh100.bsp 5 | 05 maps/b_bh25.bsp 6 | 06 maps/b_exbox2.bsp 7 | 07 maps/b_explob.bsp 8 | 08 maps/b_nail0.bsp 9 | 09 maps/b_nail1.bsp 10 | 0a maps/b_rock0.bsp 11 | 0b maps/b_rock1.bsp 12 | 0c maps/b_shell0.bsp 13 | 0d maps/b_shell1.bsp 14 | 0e progs/armor.mdl 15 | 0f progs/b_g_key.mdl 16 | 10 progs/b_s_key.mdl 17 | 11 progs/backpack.mdl 18 | 12 progs/bolt.mdl 19 | 13 progs/bolt2.mdl 20 | 14 progs/bolt3.mdl 21 | 15 progs/boss.mdl 22 | 16 progs/demon.mdl 23 | 17 progs/dog.mdl 24 | 18 progs/end1.mdl 25 | 19 progs/end2.mdl 26 | 1a progs/end3.mdl 27 | 1b progs/end4.mdl 28 | 1c progs/enforcer.mdl 29 | 1d progs/eyes.mdl 30 | 1e progs/fish.mdl 31 | 1f progs/flame.mdl 32 | 20 progs/flame2.mdl 33 | 21 progs/g_light.mdl 34 | 22 progs/g_nail.mdl 35 | 23 progs/g_nail2.mdl 36 | 24 progs/g_rock.mdl 37 | 25 progs/g_rock2.mdl 38 | 26 progs/g_shot.mdl 39 | 27 progs/gib1.mdl 40 | 28 progs/gib2.mdl 41 | 29 progs/gib3.mdl 42 | 2a progs/grenade.mdl 43 | 2b progs/h_demon.mdl 44 | 2c progs/h_dog.mdl 45 | 2d progs/h_guard.mdl 46 | 2e progs/h_hellkn.mdl 47 | 2f progs/h_knight.mdl 48 | 30 progs/h_mega.mdl 49 | 31 progs/h_ogre.mdl 50 | 32 progs/h_player.mdl 51 | 33 progs/h_shal.mdl 52 | 34 progs/h_shams.mdl 53 | 35 progs/h_wizard.mdl 54 | 36 progs/h_zombie.mdl 55 | 37 progs/hknight.mdl 56 | 38 progs/invisibl.mdl 57 | 39 progs/invulner.mdl 58 | 3a progs/k_spike.mdl 59 | 3b progs/knight.mdl 60 | 3c progs/laser.mdl 61 | 3d progs/lavaball.mdl 62 | 3e progs/m_g_key.mdl 63 | 3f progs/m_s_key.mdl 64 | 40 progs/missile.mdl 65 | 41 progs/ogre.mdl 66 | 42 progs/oldone.mdl 67 | 43 progs/player.mdl 68 | 44 progs/quaddama.mdl 69 | 45 progs/s_light.mdl 70 | 46 progs/s_spike.mdl 71 | 47 progs/shalrath.mdl 72 | 48 progs/shambler.mdl 73 | 49 progs/soldier.mdl 74 | 4a progs/spike.mdl 75 | 4b progs/suit.mdl 76 | 4c progs/tarbaby.mdl 77 | 4d progs/teleport.mdl 78 | 4e progs/v_axe.mdl 79 | 4f progs/v_light.mdl 80 | 50 progs/v_nail.mdl 81 | 51 progs/v_nail2.mdl 82 | 52 progs/v_rock.mdl 83 | 53 progs/v_rock2.mdl 84 | 54 progs/v_shot.mdl 85 | 55 progs/v_shot2.mdl 86 | 56 progs/v_spike.mdl 87 | 57 progs/w_g_key.mdl 88 | 58 progs/w_s_key.mdl 89 | 59 progs/w_spike.mdl 90 | 5a progs/wizard.mdl 91 | 5b progs/zom_gib.mdl 92 | 5c progs/zombie.mdl 93 | -------------------------------------------------------------------------------- /tools/common/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "pak.h" 5 | #include "util.h" 6 | 7 | void panic(const char *fmt, ...) { 8 | va_list args; 9 | fputs("fatal error: ", stderr); 10 | va_start(args, fmt); 11 | vfprintf(stderr, fmt, args); 12 | va_end(args); 13 | fputs("\n", stderr); 14 | printf("fuck\n"); 15 | exit(1); 16 | } 17 | 18 | void *lmp_read(const char *dir, const char *fname, size_t *outsize) { 19 | // scan pak files first 20 | pak_t pak; 21 | int err; 22 | for (int i = 0; i <= 9; ++i) { 23 | err = pak_open(&pak, strfmt("%s/pak%d.pak", dir, i)); 24 | #ifndef _WIN32 25 | // on case-sensitive filesystems also try the full-caps pak name as used by the og game 26 | if (err != 0) 27 | err = pak_open(&pak, strfmt("%s/PAK%d.PAK", dir, i)); 28 | #endif 29 | if (err == 0) { 30 | void *data = pak_readfile(&pak, fname, outsize); 31 | pak_close(&pak); 32 | if (data) return data; 33 | } 34 | } 35 | 36 | // try raw file in mod directory and in working directory 37 | FILE *f = fopen(strfmt("%s/%s", dir, fname), "rb"); 38 | if (!f) f = fopen(fname, "rb"); 39 | if (f) { 40 | fseek(f, 0, SEEK_END); 41 | int sz = ftell(f); 42 | fseek(f, 0, SEEK_SET); 43 | void *buf = malloc(sz); 44 | assert(buf); 45 | fread(buf, sz, 1, f); 46 | fclose(f); 47 | if (outsize) *outsize = sz; 48 | return buf; 49 | } 50 | 51 | fprintf(stderr, "warning: could not find '%s' in '%s' or in cwd\n", fname, dir); 52 | 53 | // never gets here 54 | return NULL; 55 | } 56 | 57 | const char *strfmt(const char *fmt, ...) { 58 | static char buf[MAX_VA_BUF][MAX_VA_LEN]; 59 | static int bufidx = 0; 60 | bufidx = (bufidx + 1) % MAX_VA_BUF; 61 | va_list args; 62 | va_start(args, fmt); 63 | vsnprintf(buf[bufidx], sizeof(buf[bufidx]), fmt, args); 64 | va_end(args); 65 | return buf[bufidx]; 66 | } 67 | 68 | void convert_palette(u16 *dstpal, const u8 *pal, const int numcolors) { 69 | for (int i = 0; i < numcolors - 1; ++i) { 70 | const u8 r = *pal++; 71 | const u8 g = *pal++; 72 | const u8 b = *pal++; 73 | dstpal[i] = PSXRGB(r, g, b); 74 | if (!dstpal[i]) 75 | dstpal[i] = 0x8000; // replace with non-transparent black 76 | } 77 | // last color is transparent 78 | dstpal[numcolors - 1] = 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/progs/monster_hell_knight.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | enum hknight_frames_e { 4 | STAND1, STAND2, STAND3, STAND4, STAND5, STAND6, STAND7, STAND8, STAND9, 5 | 6 | WALK1, WALK2, WALK3, WALK4, WALK5, WALK6, WALK7, WALK8, WALK9, 7 | WALK10, WALK11, WALK12, WALK13, WALK14, WALK15, WALK16, WALK17, 8 | WALK18, WALK19, WALK20, 9 | 10 | RUN1, RUN2, RUN3, RUN4, RUN5, RUN6, RUN7, RUN8, 11 | 12 | PAIN1, PAIN2, PAIN3, PAIN4, PAIN5, 13 | 14 | DEATH1, DEATH2, DEATH3, DEATH4, DEATH5, DEATH6, DEATH7, DEATH8, 15 | DEATH9, DEATH10, DEATH11, DEATH12, 16 | 17 | DEATHB1, DEATHB2, DEATHB3, DEATHB4, DEATHB5, DEATHB6, DEATHB7, DEATHB8, 18 | DEATHB9, 19 | 20 | CHAR_A1, CHAR_A2, CHAR_A3, CHAR_A4, CHAR_A5, CHAR_A6, CHAR_A7, CHAR_A8, 21 | CHAR_A9, CHAR_A10, CHAR_A11, CHAR_A12, CHAR_A13, CHAR_A14, CHAR_A15, CHAR_A16, 22 | 23 | /* // unused 24 | MAGICA1, MAGICA2, MAGICA3, MAGICA4, MAGICA5, MAGICA6, MAGICA7, MAGICA8, 25 | MAGICA9, MAGICA10, MAGICA11, MAGICA12, MAGICA13, MAGICA14, 26 | 27 | MAGICB1, MAGICB2, MAGICB3, MAGICB4, MAGICB5, MAGICB6, MAGICB7, MAGICB8, 28 | MAGICB9, MAGICB10, MAGICB11, MAGICB12, MAGICB13, 29 | 30 | CHAR_B1, CHAR_B2, CHAR_B3, CHAR_B4, CHAR_B5, CHAR_B6, 31 | */ 32 | 33 | SLICE1, SLICE2, SLICE3, SLICE4, SLICE5, SLICE6, SLICE7, SLICE8, SLICE9, SLICE10, 34 | 35 | SMASH1, SMASH2, SMASH3, SMASH4, SMASH5, SMASH6, SMASH7, SMASH8, SMASH9, SMASH10, 36 | SMASH11, 37 | 38 | W_ATTACK1, W_ATTACK2, W_ATTACK3, W_ATTACK4, W_ATTACK5, W_ATTACK6, W_ATTACK7, 39 | W_ATTACK8, W_ATTACK9, W_ATTACK10, W_ATTACK11, W_ATTACK12, W_ATTACK13, W_ATTACK14, 40 | W_ATTACK15, W_ATTACK16, W_ATTACK17, W_ATTACK18, W_ATTACK19, W_ATTACK20, 41 | W_ATTACK21, W_ATTACK22, 42 | 43 | MAGICC1, MAGICC2, MAGICC3, MAGICC4, MAGICC5, MAGICC6, MAGICC7, MAGICC8, 44 | MAGICC9, MAGICC10, MAGICC11, 45 | }; 46 | 47 | static void monster_hell_knight_think(edict_t *self) { 48 | cycler_think(self, 0, 8); 49 | } 50 | 51 | void spawn_monster_hell_knight(edict_t *self) { 52 | G_SetModel(self, MDLID_HKNIGHT); 53 | XVecSetInt(&self->v.mins, -16, -16, -24); 54 | XVecSetInt(&self->v.maxs, +16, +16, +24); 55 | G_SetSize(self, &self->v.mins, &self->v.maxs); 56 | self->v.think = monster_hell_knight_think; 57 | self->v.nextthink = gs.time + 41; 58 | self->v.solid = SOLID_SLIDEBOX; 59 | self->v.movetype = MOVETYPE_STEP; 60 | self->v.flags = FL_MONSTER; 61 | } 62 | -------------------------------------------------------------------------------- /tools/common/libpsxav/cdrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | libpsxav: MDEC video + SPU/XA-ADPCM audio library 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include "libpsxav.h" 26 | 27 | static uint32_t psx_cdrom_calculate_edc(uint8_t *sector, uint32_t offset, uint32_t size) 28 | { 29 | uint32_t edc = 0; 30 | for (int i = offset; i < offset+size; i++) { 31 | edc ^= 0xFF&(uint32_t)sector[i]; 32 | for (int ibit = 0; ibit < 8; ibit++) { 33 | edc = (edc>>1)^(0xD8018001*(edc&0x1)); 34 | } 35 | } 36 | return edc; 37 | } 38 | 39 | void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type) 40 | { 41 | switch (type) { 42 | case PSX_CDROM_SECTOR_TYPE_MODE1: { 43 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x0, 0x810); 44 | sector[0x810] = (uint8_t)(edc); 45 | sector[0x811] = (uint8_t)(edc >> 8); 46 | sector[0x812] = (uint8_t)(edc >> 16); 47 | sector[0x813] = (uint8_t)(edc >> 24); 48 | 49 | memset(sector + 0x814, 0, 8); 50 | // TODO: ECC 51 | } break; 52 | case PSX_CDROM_SECTOR_TYPE_MODE2_FORM1: { 53 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x808); 54 | sector[0x818] = (uint8_t)(edc); 55 | sector[0x819] = (uint8_t)(edc >> 8); 56 | sector[0x81A] = (uint8_t)(edc >> 16); 57 | sector[0x81B] = (uint8_t)(edc >> 24); 58 | 59 | // TODO: ECC 60 | } break; 61 | case PSX_CDROM_SECTOR_TYPE_MODE2_FORM2: { 62 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x91C); 63 | sector[0x92C] = (uint8_t)(edc); 64 | sector[0x92D] = (uint8_t)(edc >> 8); 65 | sector[0x92E] = (uint8_t)(edc >> 16); 66 | sector[0x92F] = (uint8_t)(edc >> 24); 67 | } break; 68 | } 69 | } -------------------------------------------------------------------------------- /src/progs/mdlids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum mdlid_e { 4 | MDLID_NONE = 0x00, 5 | MDLID_B_BATT0 = 0x01, 6 | MDLID_B_BATT1 = 0x02, 7 | MDLID_B_BH10 = 0x03, 8 | MDLID_B_BH100 = 0x04, 9 | MDLID_B_BH25 = 0x05, 10 | MDLID_B_EXBOX2 = 0x06, 11 | MDLID_B_EXPLOB = 0x07, 12 | MDLID_B_NAIL0 = 0x08, 13 | MDLID_B_NAIL1 = 0x09, 14 | MDLID_B_ROCK0 = 0x0a, 15 | MDLID_B_ROCK1 = 0x0b, 16 | MDLID_B_SHELL0 = 0x0c, 17 | MDLID_B_SHELL1 = 0x0d, 18 | MDLID_ARMOR = 0x0e, 19 | MDLID_B_G_KEY = 0x0f, 20 | MDLID_B_S_KEY = 0x10, 21 | MDLID_BACKPACK = 0x11, 22 | MDLID_BOLT = 0x12, 23 | MDLID_BOLT2 = 0x13, 24 | MDLID_BOLT3 = 0x14, 25 | MDLID_BOSS = 0x15, 26 | MDLID_DEMON = 0x16, 27 | MDLID_DOG = 0x17, 28 | MDLID_END1 = 0x18, 29 | MDLID_END2 = 0x19, 30 | MDLID_END3 = 0x1a, 31 | MDLID_END4 = 0x1b, 32 | MDLID_ENFORCER = 0x1c, 33 | MDLID_EYES = 0x1d, 34 | MDLID_FISH = 0x1e, 35 | MDLID_FLAME = 0x1f, 36 | MDLID_FLAME2 = 0x20, 37 | MDLID_G_LIGHT = 0x21, 38 | MDLID_G_NAIL = 0x22, 39 | MDLID_G_NAIL2 = 0x23, 40 | MDLID_G_ROCK = 0x24, 41 | MDLID_G_ROCK2 = 0x25, 42 | MDLID_G_SHOT = 0x26, 43 | MDLID_GIB1 = 0x27, 44 | MDLID_GIB2 = 0x28, 45 | MDLID_GIB3 = 0x29, 46 | MDLID_GRENADE = 0x2a, 47 | MDLID_H_DEMON = 0x2b, 48 | MDLID_H_DOG = 0x2c, 49 | MDLID_H_GUARD = 0x2d, 50 | MDLID_H_HELLKN = 0x2e, 51 | MDLID_H_KNIGHT = 0x2f, 52 | MDLID_H_MEGA = 0x30, 53 | MDLID_H_OGRE = 0x31, 54 | MDLID_H_PLAYER = 0x32, 55 | MDLID_H_SHAL = 0x33, 56 | MDLID_H_SHAMS = 0x34, 57 | MDLID_H_WIZARD = 0x35, 58 | MDLID_H_ZOMBIE = 0x36, 59 | MDLID_HKNIGHT = 0x37, 60 | MDLID_INVISIBL = 0x38, 61 | MDLID_INVULNER = 0x39, 62 | MDLID_K_SPIKE = 0x3a, 63 | MDLID_KNIGHT = 0x3b, 64 | MDLID_LASER = 0x3c, 65 | MDLID_LAVABALL = 0x3d, 66 | MDLID_M_G_KEY = 0x3e, 67 | MDLID_M_S_KEY = 0x3f, 68 | MDLID_MISSILE = 0x40, 69 | MDLID_OGRE = 0x41, 70 | MDLID_OLDONE = 0x42, 71 | MDLID_PLAYER = 0x43, 72 | MDLID_QUADDAMA = 0x44, 73 | MDLID_S_LIGHT = 0x45, 74 | MDLID_S_SPIKE = 0x46, 75 | MDLID_SHALRATH = 0x47, 76 | MDLID_SHAMBLER = 0x48, 77 | MDLID_SOLDIER = 0x49, 78 | MDLID_SPIKE = 0x4a, 79 | MDLID_SUIT = 0x4b, 80 | MDLID_TARBABY = 0x4c, 81 | MDLID_TELEPORT = 0x4d, 82 | MDLID_V_AXE = 0x4e, 83 | MDLID_V_LIGHT = 0x4f, 84 | MDLID_V_NAIL = 0x50, 85 | MDLID_V_NAIL2 = 0x51, 86 | MDLID_V_ROCK = 0x52, 87 | MDLID_V_ROCK2 = 0x53, 88 | MDLID_V_SHOT = 0x54, 89 | MDLID_V_SHOT2 = 0x55, 90 | MDLID_V_SPIKE = 0x56, 91 | MDLID_W_G_KEY = 0x57, 92 | MDLID_W_S_KEY = 0x58, 93 | MDLID_W_SPIKE = 0x59, 94 | MDLID_WIZARD = 0x5a, 95 | MDLID_ZOM_GIB = 0x5b, 96 | MDLID_ZOMBIE = 0x5c, 97 | }; 98 | -------------------------------------------------------------------------------- /iso_cdda.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tools/bspconvpsx/xbsp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../common/psxbsp.h" 4 | #include "../common/psxmdl.h" 5 | #include "../common/idbsp.h" 6 | #include "../common/idmdl.h" 7 | 8 | #include "qbsp.h" 9 | #include "qmdl.h" 10 | #include "qsfx.h" 11 | 12 | extern xbsphdr_t xbsp_header; 13 | extern xvert_t xbsp_verts[MAX_XMAP_VERTS]; 14 | extern int xbsp_numverts; 15 | extern xplane_t xbsp_planes[MAX_XMAP_PLANES]; 16 | extern int xbsp_numplanes; 17 | extern xtexinfo_t xbsp_texinfos[MAX_XMAP_TEXTURES]; 18 | extern int xbsp_numtexinfos; 19 | extern xface_t xbsp_faces[MAX_XMAP_FACES]; 20 | extern int xbsp_numfaces; 21 | extern xnode_t xbsp_nodes[MAX_XMAP_NODES]; 22 | extern int xbsp_numnodes; 23 | extern xclipnode_t xbsp_clipnodes[MAX_XMAP_CLIPNODES]; 24 | extern int xbsp_numclipnodes; 25 | extern u16 xbsp_marksurfs[MAX_XMAP_MARKSURF]; 26 | extern int xbsp_nummarksurfs; 27 | extern xleaf_t xbsp_leafs[MAX_XMAP_LEAFS]; 28 | extern int xbsp_numleafs; 29 | extern u8 xbsp_visdata[MAX_XMAP_VISIBILITY]; 30 | extern int xbsp_numvisdata; 31 | extern xmodel_t xbsp_models[MAX_XMAP_MODELS]; 32 | extern int xbsp_nummodels; 33 | extern char xbsp_strings[MAX_XMAP_STRINGS]; 34 | extern int xbsp_numstrings; 35 | extern xaliashdr_t xbsp_entmodels[MAX_XMAP_ENTMODELS]; 36 | extern u8 xbsp_entmodeldata[1 * 1024 * 1024]; 37 | extern u8 *xbsp_entmodeldataptr; 38 | extern int xbsp_numentmodels; 39 | extern xmapent_t xbsp_entities[MAX_ENTITIES]; 40 | extern int xbsp_numentities; 41 | extern xmapsnd_t xbsp_sounds[MAX_SOUNDS]; 42 | extern int xbsp_numsounds; 43 | extern u32 xbsp_spuptr; 44 | extern u16 xbsp_texatlas[VRAM_TOTAL_HEIGHT][VRAM_TOTAL_WIDTH]; 45 | extern xlump_t xbsp_lumps[XLMP_COUNT]; 46 | 47 | xmapsnd_t *xbsp_spu_fit(qsfx_t *src); 48 | int xbsp_vram_page_fit(xtexinfo_t *xti, const int pg, int w, int h, int *outx, int *outy); 49 | int xbsp_vram_fit(const qmiptex_t *qti, xtexinfo_t *xti, int *outx, int *outy); 50 | void xbsp_vram_store_miptex(const qmiptex_t *qti, const xtexinfo_t *xti, int x, int y); 51 | void xbsp_vram_store_mdltex(const u8 *data, int x, int y, int w, int h); 52 | void xbsp_vram_export(const char *fname, const u8 *pal); 53 | u16 xbsp_texture_flags(const qmiptex_t *qti); 54 | int xbsp_texture_shrink(int *w, int *h, const int maxw, const int maxh); 55 | int xbsp_vram_height(void); 56 | void xbsp_face_add(xface_t *xf, const qface_t *qf, const qbsp_t *qbsp); 57 | u16 xbsp_string_add(const char *str); 58 | u16 xbsp_string_add_upper(const char *str); 59 | u16 xbsp_targetname_id(const char *targetname); 60 | void xbsp_targetname_print(void); 61 | int xbsp_write(const char *fname); 62 | -------------------------------------------------------------------------------- /src/progs/effects.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | void fx_spawn_blood(const x32vec3_t *org, const s16 damage) { 4 | R_SpawnParticleEffect(PT_GRAV, org, &x32vec3_origin, 73, 1 + (damage >> 1)); 5 | } 6 | 7 | void fx_spawn_gunshot(const x32vec3_t *org) { 8 | R_SpawnParticleEffect(PT_GRAV, org, &x32vec3_origin, 1, 3); 9 | } 10 | 11 | void fx_spawn_explosion(const x32vec3_t *org) { 12 | R_SpawnParticleExplosion(org); 13 | } 14 | 15 | static void telefog_play(edict_t *self) { 16 | utl_sound(self, CHAN_VOICE, SFXID_MISC_R_TELE3, SND_MAXVOL, ATTN_NORM); 17 | utl_remove(self); 18 | } 19 | 20 | void fx_spawn_telefog(const x32vec3_t *org) { 21 | edict_t *s = ED_Alloc(); 22 | s->v.classname = ENT_TEMP_ENTITY; 23 | s->v.origin = *org; 24 | s->v.nextthink = gs.time + PR_FRAMETIME * 2; 25 | s->v.think = telefog_play; 26 | R_SpawnParticleTeleport(org); 27 | } 28 | 29 | static inline void velocity_for_damage(x32vec3_t *vel, const s16 damage) { 30 | vel->x = 200 * xrand32(); 31 | vel->y = 200 * xrand32(); 32 | vel->z = 200 + 200 * xrand32(); 33 | if (damage < -50 && damage > -200) { 34 | vel->x <<= 1; 35 | vel->y <<= 1; 36 | vel->z <<= 1; 37 | } else if (damage <= -200) { 38 | vel->x <<= 2; 39 | vel->y <<= 2; 40 | vel->z <<= 2; 41 | } 42 | } 43 | 44 | void fx_throw_gib(const x32vec3_t *org, const s16 mdlid, const s16 damage) { 45 | edict_t *gib = ED_Alloc(); 46 | gib->v.classname = ENT_TEMP_ENTITY; 47 | gib->v.origin = *org; 48 | gib->v.origin.z += TO_FIX32(1); 49 | gib->v.solid = SOLID_NOT; 50 | gib->v.movetype = MOVETYPE_BOUNCE; 51 | gib->v.ltime = gs.time; 52 | gib->v.effects = EF_GIB; 53 | velocity_for_damage(&gib->v.velocity, damage); 54 | gib->v.avelocity.x = xrand32(); 55 | gib->v.avelocity.y = xrand32(); 56 | gib->v.avelocity.z = xrand32(); 57 | gib->v.nextthink = gs.time + TO_FIX32(10) + 5 * xrand32(); 58 | gib->v.think = utl_remove; 59 | G_SetModel(gib, mdlid); 60 | G_SetSize(gib, &gib->v.mins, &gib->v.maxs); 61 | G_LinkEdict(gib, false); 62 | } 63 | 64 | void fx_throw_head(edict_t *self, const s16 mdlid, const s16 damage) { 65 | self->v.frame = 0; 66 | self->v.think = null_think; 67 | self->v.nextthink = -1; 68 | self->v.movetype = MOVETYPE_BOUNCE; 69 | self->v.solid = SOLID_NOT; 70 | self->v.flags &= ~(FL_TAKEDAMAGE | FL_AUTOAIM | FL_ONGROUND); 71 | self->v.viewheight = TO_FIX32(8); 72 | self->v.ltime = gs.time; 73 | self->v.effects = EF_GIB; 74 | velocity_for_damage(&self->v.velocity, damage); 75 | self->v.avelocity.y = xrand32(); 76 | XVecSetInt(&self->v.mins, -1, -1, -1); 77 | XVecSetInt(&self->v.maxs, +1, +1, +1); 78 | G_SetModel(self, mdlid); 79 | G_SetSize(self, &self->v.mins, &self->v.maxs); 80 | self->v.origin.z += TO_FIX32(24); 81 | G_LinkEdict(self, false); 82 | } 83 | -------------------------------------------------------------------------------- /src/spu.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "types.h" 4 | #include "spu.h" 5 | 6 | // still faster to operate the voices using the registers 7 | // than to get through libspu's attribute bullshit 8 | 9 | #define SPU_VOICE_BASE ((volatile u16 *)(0x1F801C00)) 10 | #define SPU_KEY_ON_LO ((volatile u16 *)(0x1F801D88)) 11 | #define SPU_KEY_ON_HI ((volatile u16 *)(0x1F801D8A)) 12 | #define SPU_KEY_OFF_LO ((volatile u16 *)(0x1F801D8C)) 13 | #define SPU_KEY_OFF_HI ((volatile u16 *)(0x1F801D8E)) 14 | #define SPU_KEY_END ((volatile u32 *)(0x1F801D9C)) 15 | 16 | #define SPU_KEYCH(x) (0x1L<<(x)) 17 | #define SPU_VOICECH(x) SPU_KEYCH(x) 18 | 19 | struct SPU_voice { 20 | volatile s16 vol_left; 21 | volatile s16 vol_right; 22 | volatile u16 sample_rate; 23 | volatile u16 sample_startaddr; 24 | volatile u16 attack_decay; 25 | volatile u16 sustain_release; 26 | volatile u16 vol_current; 27 | volatile u16 sample_repeataddr; 28 | }; 29 | #define SPU_VOICE(x) (((volatile struct SPU_voice *)SPU_VOICE_BASE) + (x)) 30 | 31 | #define PAN_SHIFT 8 32 | 33 | u32 s_spuram_ptr = SPU_RAM_BASE; 34 | 35 | void SPU_Init(void) { 36 | SpuInit(); 37 | SpuSetTransferMode(SPU_TRANSFER_BY_DMA); 38 | SpuSetCommonMasterVolume(0x3FFF, 0x3FFF); 39 | SPU_ClearAllVoices(); 40 | s_spuram_ptr = SPU_RAM_BASE; 41 | } 42 | 43 | void SPU_KeyOn(const u32 mask) { 44 | *SPU_KEY_ON_LO = mask; 45 | *SPU_KEY_ON_HI = mask >> 16; 46 | } 47 | 48 | void SPU_KeyOff(const u32 mask) { 49 | *SPU_KEY_OFF_LO = mask; 50 | *SPU_KEY_OFF_HI = mask >> 16; 51 | } 52 | 53 | void SPU_ClearVoice(const u32 v) { 54 | SPU_VOICE(v)->vol_left = 0; 55 | SPU_VOICE(v)->vol_right = 0; 56 | SPU_VOICE(v)->sample_rate = 0; 57 | SPU_VOICE(v)->sample_startaddr = 0; 58 | SPU_VOICE(v)->sample_repeataddr = 0; 59 | SPU_VOICE(v)->attack_decay = 0x000F; 60 | SPU_VOICE(v)->sustain_release = 0x0000; 61 | SPU_VOICE(v)->vol_current = 0; 62 | } 63 | 64 | void SPU_ClearAllVoices(void) { 65 | SPU_KeyOff(0xFFFFFFFFu); 66 | for (u32 i = 0; i < SPU_NUM_VOICES; ++i) 67 | SPU_ClearVoice(i); 68 | } 69 | 70 | void SPU_SetMasterVolume(const s16 v) { 71 | SpuSetCommonMasterVolume(v, v); 72 | } 73 | 74 | void SPU_SetVoiceVolume(const u32 v, const s16 lvol, const s16 rvol) { 75 | SPU_VOICE(v)->vol_left = lvol; 76 | SPU_VOICE(v)->vol_right = rvol; 77 | } 78 | 79 | u32 SPU_GetVoiceEndMask(void) { 80 | return *SPU_KEY_END; 81 | } 82 | 83 | void SPU_PlaySample(const u32 ch, const u32 addr, const u32 freq) { 84 | SPU_VOICE(ch)->sample_rate = FreqToPitch(freq); 85 | SPU_VOICE(ch)->sample_startaddr = (addr >> 3); 86 | SPU_KeyOn(SPU_VOICECH(ch)); // this restarts the channel on the new address 87 | } 88 | 89 | void SPU_WaitForTransfer(void) { 90 | SpuIsTransferCompleted(SPU_TRANSFER_WAIT); 91 | } 92 | 93 | void SPU_StartUpload(const u32 dstaddr, const u8 *src, const u32 size) { 94 | SpuSetTransferStartAddr(dstaddr); 95 | SpuWrite((u32 *)src, size); 96 | } 97 | 98 | void SPU_EnableCDDA(const qboolean enable) { 99 | if (enable) 100 | SPU_CTRL |= 1; 101 | else 102 | SPU_CTRL &= ~1; 103 | } 104 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "types.h" 7 | #include "fixed.h" 8 | #include "vector.h" 9 | #include "mathlib.h" 10 | #include "memory.h" 11 | 12 | // extra types 13 | typedef struct link_s { 14 | struct link_s *prev, *next; 15 | } link_t; 16 | 17 | #define STRUCT_FROM_LINK(l, t, m) ((t *)((u8 *)l - (int)&(((t *)0)->m))) 18 | 19 | // general limits 20 | #define MAX_EDICTS 600 21 | #define MAX_SOUNDS 255 // +1 invalid 22 | #define MAX_MODELS 255 // +1 invalid 23 | #define MAX_TEX_WIDTH 256 24 | #define MAX_TEX_HEIGHT 256 25 | #define MAX_OSPATH 64 26 | #define MAX_PLAYERS 1 27 | #define MAX_LIGHTSTYLES 64 28 | #define MAX_USERSTYLES 16 29 | #define MAX_STYLESTRING 64 30 | #define MAX_VA_STRING 1024 31 | 32 | // buttons 33 | #define BTN_FIRE 1 34 | #define BTN_JUMP 2 35 | #define BTN_SPEED 4 36 | #define BTN_NEXTWEAPON 8 37 | #define BTN_PREVWEAPON 16 38 | #define BTN_RESTART 32 // when dead 39 | 40 | // graphics-related 41 | #define VID_WIDTH 320 42 | #define VID_HEIGHT 240 43 | #define VID_CENTER_X (VID_WIDTH >> 1) 44 | #define VID_CENTER_Y (VID_HEIGHT >> 1) 45 | #define VID_NUM_COLORS 256 46 | #define VID_NUM_CLUTS 6 // one for each gamma level 47 | #define VID_MAXFPS 60 // TODO: PAL? 48 | 49 | // texture flags 50 | #define TEX_SPECIAL 1 51 | #define TEX_LIQUID 2 52 | #define TEX_SKY 4 53 | #define TEX_INVISIBLE 8 54 | #define TEX_ANIMATED 16 55 | #define TEX_NULL 0x80 56 | 57 | // VRAM layout 58 | #define VRAM_PIC_XSTART 960 59 | #define VRAM_PIC_WIDTH 64 60 | #define VRAM_PIC_HEIGHT 512 61 | #define VRAM_TEX_XSTART 320 62 | #define VRAM_TEX_YSTART 0 63 | #define VRAM_TEX_WIDTH (1024 - VRAM_PIC_WIDTH - VRAM_TEX_XSTART) 64 | #define VRAM_TEX_HEIGHT 512 65 | #define VRAM_PAL_XSTART 0 66 | #define VRAM_PAL_YSTART VID_HEIGHT 67 | #define VRAM_PAL_WIDTH VID_NUM_COLORS 68 | #define VRAM_PAL_HEIGHT 1 69 | 70 | // filesystem stuff 71 | #define FS_BASE "\\ID1PSX" 72 | 73 | #define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 74 | #define ARRAYCOUNT(a) (sizeof(a) / sizeof((a)[0])) 75 | 76 | FORCEINLINE void ClearLink(link_t *l) { 77 | l->prev = l->next = l; 78 | } 79 | 80 | FORCEINLINE void RemoveLink(link_t *l) { 81 | l->next->prev = l->prev; 82 | l->prev->next = l->next; 83 | } 84 | 85 | FORCEINLINE void InsertLinkBefore(link_t *l, link_t *before) { 86 | l->next = before; 87 | l->prev = before->prev; 88 | l->prev->next = l; 89 | l->next->prev = l; 90 | } 91 | 92 | FORCEINLINE void InsertLinkAfter(link_t *l, link_t *after) { 93 | l->next = after->next; 94 | l->prev = after; 95 | l->prev->next = l; 96 | l->next->prev = l; 97 | } 98 | 99 | char *COM_SkipPath(char *pathname); 100 | void COM_StripExtension(char *in, char *out); 101 | void COM_FileBase(char *in, char *out); 102 | void COM_DefaultExtension(char *path, char *extension); 103 | 104 | extern char com_gamedir[MAX_OSPATH]; 105 | extern char com_vastring[MAX_VA_STRING]; 106 | 107 | #define VA(...) ({ sprintf(com_vastring, __VA_ARGS__); com_vastring; }) 108 | 109 | void COM_Init(void); 110 | 111 | extern qboolean com_registered; 112 | -------------------------------------------------------------------------------- /src/r_light.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "game.h" 5 | #include "render.h" 6 | 7 | #define MAX_LIGHTVALUE 400 8 | 9 | u16 r_lightstylevalue[MAX_LIGHTSTYLES + 1]; 10 | 11 | struct { 12 | char map[MAX_STYLESTRING]; 13 | u16 len; 14 | } r_lightstyle[MAX_USERSTYLES]; 15 | 16 | void R_SetLightStyle(const int i, const char *map) { 17 | if (i < MAX_USERSTYLES) { 18 | // proper lightstyle string 19 | strncpy(r_lightstyle[i].map, map, MAX_STYLESTRING - 1); 20 | r_lightstyle[i].len = strlen(map); 21 | for (u16 j = 0; j < r_lightstyle[i].len; ++j) 22 | r_lightstyle[i].map[j] -= 'a'; 23 | } else if (i < MAX_LIGHTSTYLES) { 24 | // toggleable light; set value directly 25 | const u16 k = (u16)(map[0] - 'a') * 22; 26 | r_lightstylevalue[i] = k > MAX_LIGHTVALUE ? MAX_LIGHTVALUE : k; 27 | } 28 | } 29 | 30 | void R_InitLightStyles(void) { 31 | // normal 32 | R_SetLightStyle(0, "m"); 33 | // 1 FLICKER (first variety) 34 | R_SetLightStyle(1, "mmnmmommommnonmmonqnmmo"); 35 | // 2 SLOW STRONG PULSE 36 | R_SetLightStyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); 37 | // 3 CANDLE (first variety) 38 | R_SetLightStyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); 39 | // 4 FAST STROBE 40 | R_SetLightStyle(4, "mamamamamama"); 41 | // 5 GENTLE PULSE 1 42 | R_SetLightStyle(5, "jklmnopqrstuvwxyzyxwvutsrqponmlkj"); 43 | // 6 FLICKER (second variety) 44 | R_SetLightStyle(6, "nmonqnmomnmomomno"); 45 | // 7 CANDLE (second variety) 46 | R_SetLightStyle(7, "mmmaaaabcdefgmmmmaaaammmaamm"); 47 | // 8 CANDLE (third variety) 48 | R_SetLightStyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); 49 | // 9 SLOW STROBE (fourth variety) 50 | R_SetLightStyle(9, "aaaaaaaazzzzzzzz"); 51 | // 10 FLUORESCENT FLICKER 52 | R_SetLightStyle(10, "mmamammmmammamamaaamammma"); 53 | // 11 SLOW PULSE NOT FADE TO BLACK 54 | R_SetLightStyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); 55 | 56 | // dummy style 57 | r_lightstylevalue[MAX_LIGHTSTYLES] = 0; 58 | } 59 | 60 | void R_UpdateLightStyles(const x32 time) { 61 | register const int i = (time * 10) >> FIXSHIFT; 62 | register u16 k; 63 | for (register int j = 0; j < MAX_USERSTYLES; ++j) { 64 | if (!r_lightstyle[j].len) { 65 | r_lightstylevalue[j] = 256; 66 | continue; 67 | } 68 | k = i % r_lightstyle[j].len; 69 | k = r_lightstyle[j].map[k] * 22; 70 | r_lightstylevalue[j] = k > MAX_LIGHTVALUE ? MAX_LIGHTVALUE : k; 71 | } 72 | } 73 | 74 | void R_LightEntity(edict_t *ent) { 75 | if (ent->v.effects & EF_MUZZLEFLASH) { 76 | const u16 boost = ent->v.light + 0x80; 77 | ent->v.light = boost > 0xFF ? 0xFF : boost; 78 | ent->v.effects &= ~EF_MUZZLEFLASH; // FIXME: clear this for all entities 79 | return; 80 | } 81 | 82 | ent->v.light = 0; 83 | 84 | if (!ent->num_leafs) 85 | return; 86 | 87 | u32 avglight = 0; 88 | for (int i = 0; i < ent->num_leafs; ++i) { 89 | const mleaf_t *leaf = gs.worldmodel->leafs + ent->leafnums[i] + 1; 90 | const u32 light = 91 | leaf->lightmap[0] * r_lightstylevalue[leaf->styles[0]] + 92 | leaf->lightmap[1] * r_lightstylevalue[leaf->styles[1]]; 93 | avglight += light >> 8; 94 | } 95 | 96 | avglight /= ent->num_leafs; 97 | ent->v.light = avglight > 0xff ? 0xff : avglight; 98 | } 99 | -------------------------------------------------------------------------------- /src/progs/entclasses.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "entity.h" 4 | 5 | enum entclass_e { 6 | ENT_INVALID_CLASS = 0xff, 7 | ENT_WORLDSPAWN = 0x00, 8 | ENT_PLAYER = 0x01, 9 | ENT_AIR_BUBBLES = 0x02, 10 | ENT_AMBIENT_COMP_HUM = 0x03, 11 | ENT_AMBIENT_DRIP = 0x04, 12 | ENT_AMBIENT_DRONE = 0x05, 13 | ENT_AMBIENT_SUCK_WIND = 0x06, 14 | ENT_AMBIENT_SWAMP1 = 0x07, 15 | ENT_AMBIENT_SWAMP2 = 0x08, 16 | ENT_EVENT_LIGHTNING = 0x09, 17 | ENT_FUNC_BOSSGATE = 0x0a, 18 | ENT_FUNC_BUTTON = 0x0b, 19 | ENT_FUNC_DOOR = 0x0c, 20 | ENT_FUNC_DOOR_SECRET = 0x0d, 21 | ENT_FUNC_EPISODEGATE = 0x0e, 22 | ENT_FUNC_ILLUSIONARY = 0x0f, 23 | ENT_FUNC_PLAT = 0x10, 24 | ENT_FUNC_TRAIN = 0x11, 25 | ENT_FUNC_WALL = 0x12, 26 | ENT_INFO_INTERMISSION = 0x13, 27 | ENT_INFO_NULL = 0x14, 28 | ENT_INFO_PLAYER_COOP = 0x15, 29 | ENT_INFO_PLAYER_DEATHMATCH = 0x16, 30 | ENT_INFO_PLAYER_START = 0x17, 31 | ENT_INFO_PLAYER_START2 = 0x18, 32 | ENT_INFO_TELEPORT_DESTINATION = 0x19, 33 | ENT_ITEM_ARMOR1 = 0x1a, 34 | ENT_ITEM_ARMOR2 = 0x1b, 35 | ENT_ITEM_ARMORINV = 0x1c, 36 | ENT_ITEM_ARTIFACT_ENVIROSUIT = 0x1d, 37 | ENT_ITEM_ARTIFACT_INVISIBILITY = 0x1e, 38 | ENT_ITEM_ARTIFACT_INVULNERABILITY = 0x1f, 39 | ENT_ITEM_ARTIFACT_SUPER_DAMAGE = 0x20, 40 | ENT_ITEM_CELLS = 0x21, 41 | ENT_ITEM_HEALTH = 0x22, 42 | ENT_ITEM_KEY1 = 0x23, 43 | ENT_ITEM_KEY2 = 0x24, 44 | ENT_ITEM_ROCKETS = 0x25, 45 | ENT_ITEM_SHELLS = 0x26, 46 | ENT_ITEM_SIGIL = 0x27, 47 | ENT_ITEM_SPIKES = 0x28, 48 | ENT_ITEM_WEAPON = 0x29, 49 | ENT_LIGHT = 0x2a, 50 | ENT_LIGHT_FLAME_LARGE_YELLOW = 0x2b, 51 | ENT_LIGHT_FLAME_SMALL_WHITE = 0x2c, 52 | ENT_LIGHT_FLAME_SMALL_YELLOW = 0x2d, 53 | ENT_LIGHT_FLUORO = 0x2e, 54 | ENT_LIGHT_FLUOROSPARK = 0x2f, 55 | ENT_LIGHT_GLOBE = 0x30, 56 | ENT_LIGHT_TORCH_SMALL_WALLTORCH = 0x31, 57 | ENT_MISC_EXPLOBOX = 0x32, 58 | ENT_MISC_EXPLOBOX2 = 0x33, 59 | ENT_MISC_FIREBALL = 0x34, 60 | ENT_MISC_TELEPORTTRAIN = 0x35, 61 | ENT_MONSTER_ARMY = 0x36, 62 | ENT_MONSTER_BOSS = 0x37, 63 | ENT_MONSTER_DEMON1 = 0x38, 64 | ENT_MONSTER_DOG = 0x39, 65 | ENT_MONSTER_ENFORCER = 0x3a, 66 | ENT_MONSTER_FISH = 0x3b, 67 | ENT_MONSTER_HELL_KNIGHT = 0x3c, 68 | ENT_MONSTER_KNIGHT = 0x3d, 69 | ENT_MONSTER_OGRE = 0x3e, 70 | ENT_MONSTER_OLDONE = 0x3f, 71 | ENT_MONSTER_SHALRATH = 0x40, 72 | ENT_MONSTER_SHAMBLER = 0x41, 73 | ENT_MONSTER_TARBABY = 0x42, 74 | ENT_MONSTER_WIZARD = 0x43, 75 | ENT_MONSTER_ZOMBIE = 0x44, 76 | ENT_PATH_CORNER = 0x45, 77 | ENT_TRAP_SPIKESHOOTER = 0x46, 78 | ENT_TRIGGER_CHANGELEVEL = 0x47, 79 | ENT_TRIGGER_COUNTER = 0x48, 80 | ENT_TRIGGER_HURT = 0x49, 81 | ENT_TRIGGER_MONSTERJUMP = 0x4a, 82 | ENT_TRIGGER_MULTIPLE = 0x4b, 83 | ENT_TRIGGER_ONCE = 0x4c, 84 | ENT_TRIGGER_ONLYREGISTERED = 0x4d, 85 | ENT_TRIGGER_PUSH = 0x4e, 86 | ENT_TRIGGER_RELAY = 0x4f, 87 | ENT_TRIGGER_SECRET = 0x50, 88 | ENT_TRIGGER_SETSKILL = 0x51, 89 | ENT_TRIGGER_TELEPORT = 0x52, 90 | ENT_WEAPON_GRENADELAUNCHER = 0x53, 91 | ENT_WEAPON_LIGHTNING = 0x54, 92 | ENT_WEAPON_NAILGUN = 0x55, 93 | ENT_WEAPON_ROCKETLAUNCHER = 0x56, 94 | ENT_WEAPON_SUPERNAILGUN = 0x57, 95 | ENT_WEAPON_SUPERSHOTGUN = 0x58, 96 | ENT_TEMP_ENTITY = 0x59, 97 | ENT_SPIKE = 0x5a, 98 | ENT_GRENADE = 0x5b, 99 | ENT_ROCKET = 0x5c, 100 | ENT_DELAYED_USE = 0x5d, 101 | ENT_VIEWTHING = 0x5e, 102 | }; 103 | 104 | extern think_fn_t ent_spawnfuncs[]; 105 | -------------------------------------------------------------------------------- /src/progs/func.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | static void button_done(edict_t *self) { 4 | self->v.door->state = STATE_BOTTOM; 5 | } 6 | 7 | static void button_return(edict_t *self) { 8 | self->v.door->state = STATE_DOWN; 9 | self->v.frame = 0; // use normal textures 10 | utl_calc_move(self, &self->v.door->pos1, self->v.speed, button_done); 11 | if (self->v.health) 12 | self->v.flags |= FL_TAKEDAMAGE; // can be shot again 13 | } 14 | 15 | static void button_wait(edict_t *self) { 16 | self->v.door->state = STATE_TOP; 17 | self->v.nextthink = self->v.ltime + self->v.door->wait; 18 | self->v.think = button_return; 19 | utl_usetargets(self, self->v.door->linked); 20 | self->v.frame = 1; // use alternate textures 21 | } 22 | 23 | static void button_fire(edict_t *self, edict_t *activator) { 24 | if (self->v.door->state == STATE_UP || self->v.door->state == STATE_TOP) 25 | return; 26 | 27 | utl_sound(self, CHAN_VOICE, self->v.noise, SND_MAXVOL, ATTN_NORM); 28 | 29 | self->v.door->linked = activator; 30 | self->v.door->state = STATE_UP; 31 | utl_calc_move(self, &self->v.door->pos2, self->v.speed, button_wait); 32 | } 33 | 34 | static void button_touch(edict_t *self, edict_t *other) { 35 | if (other->v.classname == ENT_PLAYER) 36 | button_fire(self, other); 37 | } 38 | 39 | static void button_killed(edict_t *self, edict_t *killer) { 40 | self->v.health = self->v.max_health; 41 | self->v.flags &= ~FL_TAKEDAMAGE; // will be reset upon return 42 | button_fire(self, killer); 43 | } 44 | 45 | void spawn_func_button(edict_t *self) { 46 | self->v.solid = SOLID_BSP; 47 | self->v.movetype = MOVETYPE_PUSH; 48 | self->v.use = button_fire; 49 | self->v.noise = SFXID_BUTTONS_AIRBUT1; 50 | 51 | utl_set_movedir(self, &self->v.avelocity); 52 | 53 | if (self->v.health) { 54 | self->v.max_health = self->v.health; 55 | self->v.th_die = button_killed; 56 | self->v.flags |= FL_TAKEDAMAGE; 57 | } else { 58 | self->v.touch = button_touch; 59 | } 60 | 61 | if (!self->v.speed) 62 | self->v.speed = 40; 63 | if (!self->v.extra_trigger.wait) 64 | self->v.extra_trigger.wait = ONE; 65 | if (!self->v.count) 66 | self->v.count = 4; // lip 67 | 68 | door_init(self); 69 | 70 | self->v.door->state = STATE_BOTTOM; 71 | 72 | const x32 length = abs(XVecDotSL(&self->v.avelocity, &self->v.size)) - TO_FIX32(self->v.count); 73 | 74 | self->v.door->pos1 = self->v.origin; 75 | self->v.door->pos2.x = self->v.door->pos1.x + xmul32(self->v.avelocity.x, length); 76 | self->v.door->pos2.y = self->v.door->pos1.y + xmul32(self->v.avelocity.y, length); 77 | self->v.door->pos2.z = self->v.door->pos1.z + xmul32(self->v.avelocity.z, length); 78 | } 79 | 80 | void spawn_func_illusionary(edict_t *self) { 81 | self->v.solid = SOLID_NOT; 82 | } 83 | 84 | static void func_wall_use(edict_t *self, edict_t *activator) { 85 | // change to alternate textures 86 | self->v.frame = 1 - self->v.frame; 87 | } 88 | 89 | void spawn_func_wall(edict_t *self) { 90 | self->v.solid = SOLID_BSP; 91 | self->v.movetype = MOVETYPE_PUSH; 92 | self->v.use = func_wall_use; 93 | } 94 | 95 | void spawn_func_bossgate(edict_t *self) { 96 | self->v.solid = SOLID_BSP; 97 | self->v.movetype = MOVETYPE_PUSH; 98 | } 99 | 100 | void spawn_func_episodegate(edict_t *self) { 101 | self->v.solid = SOLID_BSP; 102 | self->v.movetype = MOVETYPE_PUSH; 103 | // TODO 104 | self->v.nextthink = gs.time + PR_FRAMETIME; 105 | self->v.think = utl_remove; 106 | } 107 | -------------------------------------------------------------------------------- /tools/common/libpsxav/libpsxav.h: -------------------------------------------------------------------------------- 1 | /* 2 | libpsxav: MDEC video + SPU/XA-ADPCM audio library 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #ifndef __LIBPSXAV_H__ 25 | #define __LIBPSXAV_H__ 26 | 27 | #include 28 | #include 29 | 30 | // audio.c 31 | 32 | #define PSX_AUDIO_XA_FREQ_SINGLE 18900 33 | #define PSX_AUDIO_XA_FREQ_DOUBLE 37800 34 | 35 | typedef enum { 36 | PSX_AUDIO_XA_FORMAT_XA, // .xa file 37 | PSX_AUDIO_XA_FORMAT_XACD // 2352-byte sector 38 | } psx_audio_xa_format_t; 39 | 40 | typedef struct { 41 | psx_audio_xa_format_t format; 42 | bool stereo; // false or true 43 | int frequency; // 18900 or 37800 Hz 44 | int bits_per_sample; // 4 or 8 45 | int file_number; // 00-FF 46 | int channel_number; // 00-1F 47 | } psx_audio_xa_settings_t; 48 | 49 | typedef struct { 50 | int qerr; // quanitisation error 51 | uint64_t mse; // mean square error 52 | int prev1, prev2; 53 | } psx_audio_encoder_channel_state_t; 54 | 55 | typedef struct { 56 | psx_audio_encoder_channel_state_t left; 57 | psx_audio_encoder_channel_state_t right; 58 | } psx_audio_encoder_state_t; 59 | 60 | #define PSX_AUDIO_SPU_LOOP_END 1 61 | #define PSX_AUDIO_SPU_LOOP_REPEAT 3 62 | #define PSX_AUDIO_SPU_LOOP_START 4 63 | 64 | uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count); 65 | uint32_t psx_audio_spu_get_buffer_size(int sample_count); 66 | uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings); 67 | uint32_t psx_audio_spu_get_buffer_size_per_block(void); 68 | uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings); 69 | uint32_t psx_audio_spu_get_samples_per_block(void); 70 | int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); 71 | int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output); 72 | int psx_audio_spu_encode(psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); 73 | int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start); 74 | int psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length); 75 | void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag); 76 | 77 | // cdrom.c 78 | 79 | #define PSX_CDROM_SECTOR_SIZE 2352 80 | 81 | typedef enum { 82 | PSX_CDROM_SECTOR_TYPE_MODE1, 83 | PSX_CDROM_SECTOR_TYPE_MODE2_FORM1, 84 | PSX_CDROM_SECTOR_TYPE_MODE2_FORM2 85 | } psx_cdrom_sector_type_t; 86 | 87 | void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); 88 | 89 | #endif /* __LIBPSXAV_H__ */ -------------------------------------------------------------------------------- /src/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "system.h" 8 | 9 | #define RAM_END 0x80200000 10 | 11 | #define MEM_ALIGNMENT 4 12 | #define MEM_MAX_ALLOCS 256 13 | 14 | static u8 *mem_base; 15 | static u8 *mem_ptr; 16 | static u8 *mem_lastptr; 17 | static int mem_size; 18 | static int mem_left; 19 | 20 | static u32 mem_allocs[MEM_MAX_ALLOCS]; 21 | static u32 mem_numallocs; 22 | 23 | static struct { 24 | u8 *mem_ptr; 25 | u8 *mem_lastptr; 26 | s32 mem_left; 27 | u32 mem_numallocs; 28 | } mem_mark[MEM_MARK_COUNT]; 29 | 30 | // this should be defined somewhere 31 | extern u8 _end[]; 32 | 33 | void Mem_Init(void) { 34 | // shrink libc heap to acceptable levels; we still probably need it for some things 35 | InitHeap(_end + 4, MALLOC_HEAP_SIZE - 4); 36 | 37 | mem_base = _end + MALLOC_HEAP_SIZE; 38 | mem_base = (u8 *)ALIGN((u32)mem_base, MEM_ALIGNMENT); 39 | mem_size = mem_left = (u8 *)RAM_END - mem_base - STACK_SIZE; 40 | mem_ptr = mem_lastptr = mem_base; 41 | mem_numallocs = 0; 42 | 43 | printf("Mem_Init: mem_base=%08x mem_size=%u\n", (u32)mem_base, mem_size); 44 | } 45 | 46 | void *Mem_Alloc(const u32 size) { 47 | const u32 asize = ALIGN(size, MEM_ALIGNMENT); 48 | if (size == 0) 49 | Sys_Error("Mem_Alloc: size == 0"); 50 | if (mem_left < asize) 51 | Sys_Error("Mem_Alloc: failed to alloc %u bytes - only %d left", size, mem_left); 52 | if (mem_numallocs == MEM_MAX_ALLOCS) 53 | Sys_Error("Mem_Alloc: MAX_ALLOCS reached"); 54 | mem_lastptr = mem_ptr; 55 | mem_ptr += asize; 56 | mem_left -= asize; 57 | mem_allocs[mem_numallocs++] = asize; 58 | return mem_lastptr; 59 | } 60 | 61 | void *Mem_ZeroAlloc(const u32 size) { 62 | void *buf = Mem_Alloc(size); 63 | memset(buf, 0, size); 64 | return buf; 65 | } 66 | 67 | void *Mem_Realloc(void *ptr, const u32 newsize) { 68 | if (!ptr) 69 | return Mem_Alloc(newsize); // just like realloc() 70 | const u32 anewsize = ALIGN(newsize, MEM_ALIGNMENT); 71 | const u32 oldsize = mem_allocs[mem_numallocs - 1]; 72 | if (ptr != mem_lastptr || !mem_numallocs) 73 | Sys_Error("Mem_Realloc: this is a stack allocator you dolt"); 74 | if (mem_left < anewsize) 75 | Sys_Error("Mem_Realloc: failed to realloc %p from %u to %u bytes - only %d left", ptr, oldsize, anewsize, mem_left); 76 | mem_left -= (int)anewsize - (int)oldsize; 77 | mem_ptr = mem_lastptr + anewsize; 78 | mem_allocs[mem_numallocs - 1] = anewsize; 79 | return mem_lastptr; 80 | } 81 | 82 | void Mem_Free(void *ptr) { 83 | if (!ptr) 84 | return; // NULL free is a no-op 85 | if (ptr != mem_lastptr) 86 | Sys_Error("Mem_Free: this is a stack allocator you dolt"); 87 | if (mem_numallocs == 0) 88 | Sys_Error("Mem_Free: nothing to free"); 89 | const u32 size = mem_allocs[--mem_numallocs]; 90 | mem_left += size; 91 | mem_ptr = mem_lastptr; 92 | if (mem_numallocs) 93 | mem_lastptr -= mem_allocs[mem_numallocs - 1]; 94 | else 95 | ASSERT(mem_lastptr == mem_base); 96 | } 97 | 98 | void Mem_SetMark(const int m) { 99 | mem_mark[m].mem_numallocs = mem_numallocs; 100 | mem_mark[m].mem_left = mem_left; 101 | mem_mark[m].mem_lastptr = mem_lastptr; 102 | mem_mark[m].mem_ptr = mem_ptr; 103 | printf("Mem_SetMark: mark %d set at %p/%p, %d left\n", m, mem_lastptr, mem_ptr, mem_left); 104 | } 105 | 106 | void Mem_FreeToMark(const int m) { 107 | mem_numallocs = mem_mark[m].mem_numallocs; 108 | mem_left = mem_mark[m].mem_left; 109 | mem_lastptr = mem_mark[m].mem_lastptr; 110 | mem_ptr = mem_mark[m].mem_ptr; 111 | printf("Mem_FreeToMark: reset to mark %d: %p/%p, %d left\n", m, mem_lastptr, mem_ptr, mem_left); 112 | } 113 | 114 | u32 Mem_GetFreeSpace(void) { 115 | return mem_left; 116 | } 117 | -------------------------------------------------------------------------------- /src/cd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "system.h" 5 | #include "spu.h" 6 | #include "cd.h" 7 | 8 | static struct { 9 | cdmode_t mode; 10 | u32 numtracks; 11 | u32 curtrack; // 0 if data 12 | qboolean paused; 13 | volatile qboolean endoftrack; 14 | } cd; 15 | 16 | s32 cd_volume = 128; 17 | 18 | static void AutoPauseCallback(CdlIntrResult result, u8 *data) { 19 | if (cd.curtrack && cd.mode == CDMODE_AUDIO) { 20 | // signal to CD_Update that we need to restart the CD track 21 | cd.endoftrack = true; 22 | cd.paused = true; 23 | } 24 | } 25 | 26 | void CD_Init(void) { 27 | // see if there was a CD in the drive during init, die if there wasn't 28 | if (!CdInit()) 29 | Sys_Error("CD_Init: bad CD or no CD in drive"); 30 | 31 | // update status 32 | CdControl(CdlNop, 0, 0); 33 | CdStatus(); 34 | 35 | // set hispeed mode 36 | CD_SetMode(CDMODE_DATA); 37 | 38 | // TODO: is this wait necessary? many examples seem to do it 39 | Sys_Wait(3); 40 | 41 | // set the CD mixer to the default settings (L -> L, R -> R) 42 | // the actual volume is set via the Snd_ API 43 | const CdlATV atv = { cd_volume, 0, cd_volume, 0 }; 44 | CdMix(&atv); 45 | 46 | // detect audio tracks, if any 47 | CdlLOC tracks[100]; 48 | cd.numtracks = CdGetToc(tracks); 49 | Sys_Printf("CD_Init: %d tracks\n", cd.numtracks); 50 | } 51 | 52 | u32 CD_GetCurrentTrack(void) { 53 | return cd.curtrack; 54 | } 55 | 56 | qboolean CD_IsPlaybackPaused(void) { 57 | return cd.paused; 58 | } 59 | 60 | void CD_SetAudioVolume(u8 vol) { 61 | cd_volume = vol; 62 | const CdlATV atv = { vol, 0, vol, 0 }; 63 | CdMix(&atv); 64 | } 65 | 66 | void CD_PlayAudio(const u32 track) { 67 | // track 1 is data 68 | if (track < 2 || track > cd.numtracks) { 69 | Sys_Printf("CD_PlayTrack: track %d does not exist\n", track); 70 | return; 71 | } 72 | 73 | CD_SetMode(CDMODE_AUDIO); 74 | 75 | const u32 param = track; 76 | if (CdControl(CdlPlay, ¶m, NULL) != 1) { 77 | Sys_Printf("CD_PlayTrack: CdlPlay failed\n"); 78 | return; 79 | } 80 | 81 | cd.curtrack = track; 82 | cd.paused = false; 83 | } 84 | 85 | void CD_Pause(const qboolean pause) { 86 | if (cd.paused == pause) 87 | return; 88 | 89 | if (cd.mode != CDMODE_AUDIO) 90 | return; 91 | 92 | if (CdControl(pause ? CdlPause : CdlPlay, NULL, NULL) != 1) { 93 | Sys_Printf("CD_Pause: failed to set pause=%d\n", pause); 94 | return; 95 | } 96 | 97 | cd.paused = pause; 98 | } 99 | 100 | void CD_Stop(void) { 101 | // disable audio mode 102 | CD_SetMode(CDMODE_DATA); 103 | // stop the drive motor 104 | CdControl(CdlStop, NULL, NULL); 105 | } 106 | 107 | void CD_SetMode(const cdmode_t mode) { 108 | if (mode == cd.mode) 109 | return; 110 | 111 | // set the CDDA flag if we're going into audio mode 112 | SPU_EnableCDDA(mode == CDMODE_AUDIO); 113 | 114 | // if we were playing something, pause it and reset state 115 | if (cd.mode == CDMODE_AUDIO) { 116 | CdControlB(CdlPause, NULL, NULL); 117 | cd.curtrack = 0; 118 | cd.paused = false; 119 | } 120 | 121 | const u32 param = (mode == CDMODE_AUDIO) ? (CdlModeDA | CdlModeAP) : CdlModeSpeed; 122 | if (CdControlB(CdlSetmode, ¶m, NULL) != 1) { 123 | Sys_Printf("CD_EnableCDDA: CdlSetmode failed\n"); 124 | return; 125 | } 126 | 127 | CdAutoPauseCallback((mode == CDMODE_AUDIO) ? AutoPauseCallback : NULL); 128 | 129 | cd.mode = mode; 130 | } 131 | 132 | void CD_Update(void) { 133 | if (cd.mode != CDMODE_AUDIO) 134 | return; 135 | 136 | if (cd.curtrack && cd.endoftrack) { 137 | if (CdControl(CdlPlay, &cd.curtrack, NULL) != 1) { 138 | Sys_Printf("CD_Update: failed to restart track\n"); 139 | return; 140 | } 141 | cd.endoftrack = false; 142 | cd.paused = false; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/bspfile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "vector.h" 5 | #include "alias.h" 6 | 7 | #define PSXBSPVERSION 0x1D585350 8 | 9 | #define MAX_MAP_HULLS 4 10 | #define MAX_MAP_VERTS 65535 11 | #define MAX_MAP_PLANES 32767 12 | #define MAX_MAP_TEXTURES 255 13 | #define MAX_MAP_NODES 32767 14 | #define MAX_MAP_CLIPNODES 32767 15 | #define MAX_MAP_LEAFS 8192 16 | #define MAX_MAP_FACES 32767 17 | #define MAX_MAP_MODELS 256 18 | #define MAX_MAP_MARKSURF 65535 19 | #define MAX_MAP_VISIBILITY 0x40000 20 | #define MAX_MAP_LIGHTVALS 2 // different light values on a single surface 21 | #define MAX_MAP_STRCHARS 8192 22 | 23 | #pragma pack(push, 1) 24 | 25 | /* XBSP STRUCTURE 26 | * xbsphdr_t ver="PSX\x1D" 27 | * xbsplump_t LUMP_TEXDATA 28 | * xbsplump_t LUMP_SNDDATA 29 | * xbsplump_t LUMP_MDLDATA 30 | * xbsplump_t LUMP_VERTS 31 | * xbsplump_t LUMP_PLANES 32 | * xbsplump_t LUMP_TEXINFO 33 | * xbsplump_t LUMP_FACES 34 | * xbsplump_t LUMP_MARKSURF 35 | * xbsplump_t LUMP_VISILIST 36 | * xbsplump_t LUMP_LEAFS 37 | * xbsplump_t LUMP_NODES 38 | * xbsplump_t LUMP_CLIPNODES 39 | * xbsplump_t LUMP_MODELS 40 | * xbsplump_t LUMP_ENTITIES 41 | * xbsplump_t LUMP_STRINGS 42 | */ 43 | 44 | enum xbsplump_e { 45 | LUMP_INVALID = -1, 46 | 47 | LUMP_TEXDATA, 48 | LUMP_SNDDATA, 49 | LUMP_MDLDATA, 50 | 51 | LUMP_VERTS, 52 | LUMP_PLANES, 53 | LUMP_TEXINFO, 54 | LUMP_FACES, 55 | LUMP_MARKSURF, 56 | LUMP_VISILIST, 57 | LUMP_LEAFS, 58 | LUMP_NODES, 59 | LUMP_CLIPNODES, 60 | LUMP_MODELS, 61 | LUMP_STRINGS, 62 | LUMP_ENTITIES, 63 | 64 | LUMP_COUNT 65 | }; 66 | 67 | typedef struct { 68 | s16vec3_t pos; // positions are always integer 69 | u8vec2_t tex; // 0-255 or 0-size 70 | u8 col[MAX_MAP_LIGHTVALS]; // light values for every lightstyle 71 | } xbspvert_t; 72 | 73 | typedef struct { 74 | s32 type; 75 | s32 size; 76 | u8 data[]; 77 | } xbsplump_t; 78 | 79 | typedef struct { 80 | u32 ver; 81 | } xbsphdr_t; 82 | 83 | typedef struct { 84 | x16vec3_t normal; 85 | x32 dist; 86 | s32 type; 87 | } xbspplane_t; 88 | 89 | typedef struct { 90 | u16 planenum; 91 | s16 children[2]; // negative numbers are -(leafs+1), not nodes 92 | s16vec3_t mins; // for sphere culling 93 | s16vec3_t maxs; 94 | u16 firstface; 95 | u16 numfaces; // counting both sides 96 | } xbspnode_t; 97 | 98 | typedef struct { 99 | s16 planenum; 100 | s16 children[2]; // negative numbers are contents 101 | } xbspclipnode_t; 102 | 103 | typedef struct { 104 | u8vec2_t uv; 105 | s16vec2_t size; 106 | u16 tpage; 107 | u8 flags; 108 | s8 anim_total; 109 | s8 anim_min; 110 | s8 anim_max; 111 | s8 anim_next; 112 | s8 anim_alt; 113 | } xbsptexinfo_t; 114 | 115 | typedef struct { 116 | s16 planenum; 117 | s16 side; 118 | s32 firstvert; 119 | s16 numverts; 120 | s16 texinfo; 121 | u8 styles[MAX_MAP_LIGHTVALS]; 122 | } xbspface_t; 123 | 124 | typedef struct { 125 | s16 contents; 126 | s32 visofs; // -1 = no visibility info 127 | s16vec3_t mins; // for frustum culling 128 | s16vec3_t maxs; 129 | u16 firstmarksurface; 130 | u16 nummarksurfaces; 131 | u8 lightmap[MAX_MAP_LIGHTVALS]; 132 | u8 styles[MAX_MAP_LIGHTVALS]; 133 | } xbspleaf_t; 134 | 135 | typedef struct { 136 | s16vec3_t mins; 137 | s16vec3_t maxs; 138 | s16vec3_t origin; 139 | s16 headnode[MAX_MAP_HULLS]; 140 | s16 visleafs; 141 | u16 firstface; 142 | u16 numfaces; 143 | } xmodel_t; 144 | 145 | typedef struct { 146 | u8 classname; 147 | s8 noise; 148 | u16 spawnflags; 149 | s16 model; // negative = brush models, positive = alias models 150 | s16 health; 151 | s16 dmg; 152 | s16 speed; 153 | s16 count; 154 | s16 height; 155 | u16 target; 156 | u16 killtarget; 157 | u16 targetname; 158 | u16 string; // offset into string lump 159 | x32 wait; 160 | x32 delay; 161 | x16vec3_t angles; 162 | x32vec3_t origin; 163 | } xbspent_t; 164 | 165 | typedef struct { 166 | u32 nummdls; 167 | aliashdr_t mdls[]; 168 | } xmdllump_t; 169 | 170 | typedef struct { 171 | u8 soundid; 172 | u32 spuaddr; 173 | u32 size; 174 | u8 data[]; 175 | } xbspsnd_t; 176 | 177 | #pragma pack(pop) 178 | -------------------------------------------------------------------------------- /tools/common/idbsp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | 5 | #define QBSPVERSION 29 6 | 7 | #define MAX_MAP_HULLS 4 8 | #define MAX_LIGHTMAPS 4 9 | #define MAX_TEXNAME 16 10 | #define NUM_AMBIENTS 4 // automatic ambient sounds 11 | #define NUM_MIPLEVELS 4 12 | 13 | #define MAX_MAP_MODELS 256 14 | #define MAX_MAP_PLANES 32767 15 | #define MAX_MAP_NODES 32767 // because negative shorts are contents 16 | #define MAX_MAP_CLIPNODES 32767 // 17 | #define MAX_MAP_LEAFS 8192 18 | #define MAX_MAP_VERTS 65535 19 | #define MAX_MAP_FACES 65535 20 | #define MAX_MAP_MARKSURFACES 65535 21 | #define MAX_MAP_TEXINFO 4096 22 | #define MAX_MAP_EDGES 256000 23 | #define MAX_MAP_SURFEDGES 512000 24 | #define MAX_MAP_TEXTURES 512 25 | #define MAX_MAP_MIPTEX 0x200000 26 | #define MAX_MAP_LIGHTING 0x100000 27 | #define MAX_MAP_VISIBILITY 0x100000 28 | 29 | #pragma pack(push, 1) 30 | 31 | typedef f32 qvec2_t[2]; 32 | typedef f32 qvec3_t[3]; 33 | typedef f32 qvec4_t[4]; 34 | 35 | typedef struct { 36 | qvec3_t v; 37 | } qvert_t; 38 | 39 | typedef struct { 40 | s32 ofs; 41 | s32 len; 42 | } qlump_t; 43 | 44 | typedef struct { 45 | s32 ver; 46 | qlump_t entities; 47 | qlump_t planes; 48 | qlump_t miptex; 49 | qlump_t vertices; 50 | qlump_t visilist; 51 | qlump_t nodes; 52 | qlump_t texinfo; 53 | qlump_t faces; 54 | qlump_t lightmaps; 55 | qlump_t clipnodes; 56 | qlump_t leafs; 57 | qlump_t marksurfaces; 58 | qlump_t edges; 59 | qlump_t surfedges; 60 | qlump_t models; 61 | } qbsphdr_t; 62 | 63 | typedef struct { 64 | s32 nummiptex; 65 | s32 dataofs[]; // [nummiptex] 66 | } qmiptexlump_t; 67 | 68 | typedef struct { 69 | qvec3_t mins; 70 | qvec3_t maxs; 71 | qvec3_t origin; 72 | s32 headnode[MAX_MAP_HULLS]; 73 | s32 visleafs; 74 | s32 firstface; 75 | s32 numfaces; 76 | } qmodel_t; 77 | 78 | // 0-2 are axial planes 79 | #define PLANE_X 0 80 | #define PLANE_Y 1 81 | #define PLANE_Z 2 82 | // 3-5 are non-axial planes snapped to the nearest 83 | #define PLANE_ANYX 3 84 | #define PLANE_ANYY 4 85 | #define PLANE_ANYZ 5 86 | 87 | typedef struct { 88 | qvec3_t normal; 89 | f32 dist; 90 | s32 type; 91 | } qplane_t; 92 | 93 | #define CONTENTS_EMPTY -1 94 | #define CONTENTS_SOLID -2 95 | #define CONTENTS_WATER -3 96 | #define CONTENTS_SLIME -4 97 | #define CONTENTS_LAVA -5 98 | #define CONTENTS_SKY -6 99 | #define CONTENTS_ORIGIN -7 // removed at csg time 100 | #define CONTENTS_CLIP -8 // changed to contents_solid 101 | #define CONTENTS_CURRENT_0 -9 102 | #define CONTENTS_CURRENT_90 -10 103 | #define CONTENTS_CURRENT_180 -11 104 | #define CONTENTS_CURRENT_270 -12 105 | #define CONTENTS_CURRENT_UP -13 106 | #define CONTENTS_CURRENT_DOWN -14 107 | 108 | typedef struct { 109 | s32 planenum; 110 | s16 children[2]; // negative numbers are -(leafs+1), not nodes 111 | s16 mins[3]; // for sphere culling 112 | s16 maxs[3]; 113 | u16 firstface; 114 | u16 numfaces; // counting both sides 115 | } qnode_t; 116 | 117 | typedef struct { 118 | s32 planenum; 119 | s16 children[2]; // negative numbers are contents 120 | } qclipnode_t; 121 | 122 | typedef struct { 123 | s16 planenum; 124 | s16 side; 125 | s32 firstedge; 126 | s16 numedges; 127 | s16 texinfo; 128 | u8 styles[MAX_LIGHTMAPS]; 129 | s32 lightofs; // start of [numstyles*surfsize] samples 130 | } qface_t; 131 | 132 | typedef struct { 133 | s32 contents; 134 | s32 visofs; // -1 = no visibility info 135 | s16 mins[3]; // for frustum culling 136 | s16 maxs[3]; 137 | u16 firstmarksurface; 138 | u16 nummarksurfaces; 139 | u8 ambientlevel[NUM_AMBIENTS]; 140 | } qleaf_t; 141 | 142 | typedef struct { 143 | u16 v[2]; // vertex numbers 144 | } qedge_t; 145 | 146 | typedef struct { 147 | char name[MAX_TEXNAME]; 148 | u32 width, height; 149 | u32 offsets[NUM_MIPLEVELS]; 150 | } qmiptex_t; 151 | 152 | #define TEXF_SPECIAL 1 153 | #define TEXF_LIQUID 2 // sky or slime, no lightmap or 256 subdivision 154 | #define TEXF_SKY 4 155 | 156 | typedef struct { 157 | qvec4_t vecs[2]; // [s/t][xyzw] 158 | s32 miptex; 159 | s32 flags; 160 | } qtexinfo_t; 161 | 162 | #pragma pack(pop) 163 | -------------------------------------------------------------------------------- /src/entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define MAX_ENT_LEAFS 8 6 | 7 | // edict->solid values 8 | #define SOLID_NOT 0 // no interaction with other objects 9 | #define SOLID_TRIGGER 1 // touch on edge, but not blocking 10 | #define SOLID_BBOX 2 // touch on edge, block 11 | #define SOLID_SLIDEBOX 3 // touch on edge, but not an onground 12 | #define SOLID_BSP 4 // bsp clip, touch on edge, block 13 | 14 | // edict->movetype values 15 | #define MOVETYPE_NONE 0 // never moves 16 | #define MOVETYPE_ANGLENOCLIP 1 17 | #define MOVETYPE_ANGLECLIP 2 18 | #define MOVETYPE_WALK 3 // gravity 19 | #define MOVETYPE_STEP 4 // gravity, special edge handling 20 | #define MOVETYPE_FLY 5 21 | #define MOVETYPE_TOSS 6 // gravity 22 | #define MOVETYPE_PUSH 7 // no clip to world, push and crush 23 | #define MOVETYPE_NOCLIP 8 24 | #define MOVETYPE_FLYMISSILE 9 // extra size to monsters 25 | #define MOVETYPE_BOUNCE 10 26 | 27 | // edict->flags 28 | #define FL_FLY (1 << 0) 29 | #define FL_SWIM (1 << 1) 30 | #define FL_CONVEYOR (1 << 2) 31 | #define FL_CLIENT (1 << 3) 32 | #define FL_INWATER (1 << 4) 33 | #define FL_MONSTER (1 << 5) 34 | #define FL_GODMODE (1 << 6) 35 | #define FL_NOTARGET (1 << 7) 36 | #define FL_ITEM (1 << 8) 37 | #define FL_ONGROUND (1 << 9) 38 | #define FL_PARTIALGROUND (1 << 10) // not all corners are valid 39 | #define FL_WATERJUMP (1 << 11) // player jumping out of water 40 | #define FL_VISIBLE (1 << 12) // was rendered last frame 41 | #define FL_DEAD (1 << 13) 42 | #define FL_TAKEDAMAGE (1 << 14) 43 | #define FL_AUTOAIM (1 << 15) 44 | 45 | // entity effects 46 | #define EF_BRIGHTFIELD 1 47 | #define EF_MUZZLEFLASH 2 48 | #define EF_BRIGHTLIGHT 4 49 | #define EF_DIMLIGHT 8 50 | 51 | // spawnflags 52 | #define SPAWNFLAG_NOT_EASY 256 53 | #define SPAWNFLAG_NOT_MEDIUM 512 54 | #define SPAWNFLAG_NOT_HARD 1024 55 | #define SPAWNFLAG_NOT_DEATHMATCH 2048 56 | #define SPAWNFLAG_SKILL_MASK (SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD) 57 | 58 | #define EDICT_NUM(e) ((e) - gs.edicts) 59 | 60 | typedef struct edict_s edict_t; 61 | typedef struct player_state_s player_state_t; 62 | typedef struct monster_fields_s monster_fields_t; 63 | typedef struct door_fields_s door_fields_t; 64 | 65 | typedef void (*think_fn_t)(edict_t *self); 66 | typedef void (*interact_fn_t)(edict_t *self, edict_t *other); 67 | typedef void (*damage_fn_t)(edict_t *self, edict_t *attacker, s16 damage); 68 | typedef qboolean (*check_fn_t)(edict_t *self); 69 | 70 | typedef struct entvars_s { 71 | u8 classname; 72 | u8 solid; 73 | u8 movetype; 74 | u8 waterlevel; 75 | s8 watertype; 76 | u8 noise; 77 | u8 light; 78 | u8 effects; 79 | u8 frame; 80 | u8 skin; 81 | s16 count; 82 | s16 dmg; 83 | s16 speed; 84 | u16 targetname; 85 | u16 target; 86 | u16 killtarget; 87 | u16 flags; 88 | u16 spawnflags; 89 | s16 modelnum; 90 | s16 health; 91 | s16 max_health; 92 | x32 nextthink; 93 | x32 ltime; 94 | x32 viewheight; 95 | x32 delay; 96 | void *model; // either model_t or aliashdr_t 97 | think_fn_t think; 98 | interact_fn_t touch; 99 | interact_fn_t blocked; 100 | interact_fn_t use; 101 | interact_fn_t th_die; 102 | edict_t *owner; 103 | edict_t *groundentity; 104 | edict_t *chain; 105 | x32vec3_t absmin, mins; 106 | x32vec3_t absmax, maxs; 107 | x32vec3_t size; 108 | x32vec3_t origin; 109 | x32vec3_t oldorigin; 110 | x32vec3_t velocity; 111 | x16vec3_t avelocity; 112 | x16vec3_t angles; 113 | // class-specific extra data 114 | union { 115 | void *extra_ptr; 116 | player_state_t *player; 117 | monster_fields_t *monster; 118 | door_fields_t *door; 119 | struct { 120 | u32 type; 121 | u16 ammotype; 122 | } extra_item; 123 | struct { 124 | x32 wait; 125 | u16 string; 126 | } extra_trigger; 127 | }; 128 | } entvars_t; 129 | 130 | struct edict_s { 131 | qboolean free; 132 | s8 num_leafs; 133 | s16 leafnums[MAX_ENT_LEAFS]; 134 | link_t area; 135 | x32 freetime; 136 | entvars_t v; 137 | }; 138 | 139 | #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l, edict_t, area) 140 | 141 | edict_t *ED_Alloc(void); 142 | void ED_Free(edict_t *ed); 143 | -------------------------------------------------------------------------------- /src/menu.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "input.h" 3 | #include "screen.h" 4 | #include "progs.h" 5 | #include "menu.h" 6 | 7 | static const pic_t *pic_quake; 8 | 9 | static menu_t *menu_current = NULL; 10 | 11 | void Menu_Toggle(void) { 12 | if (menu_current) { 13 | IN_Clear(); 14 | menu_current = NULL; 15 | return; 16 | } 17 | 18 | menu_current = &menu_main; 19 | 20 | pic_quake = Spr_GetPic(PICID_QPLAQUE); 21 | } 22 | 23 | void Menu_Close(void) { 24 | menu_current = NULL; 25 | IN_Clear(); 26 | } 27 | 28 | void Menu_Update(void) { 29 | if (!menu_current) 30 | return; 31 | 32 | if (IN_ButtonPressed(PAD_DOWN)) { 33 | s32 i = menu_current->selection; 34 | do { 35 | i++; 36 | if (i >= menu_current->numoptions) 37 | i = 0; 38 | } while (menu_current->options[i].type == OPT_LABEL); 39 | menu_current->selection = i; 40 | } else if (IN_ButtonPressed(PAD_UP)) { 41 | s32 i = menu_current->selection; 42 | do { 43 | i--; 44 | if (i < 0) 45 | i = menu_current->numoptions - 1; 46 | } while (menu_current->options[i].type == OPT_LABEL); 47 | menu_current->selection = i; 48 | } 49 | 50 | if (IN_ButtonPressed(PAD_CIRCLE)) { 51 | menu_current = menu_current->prev; 52 | return; 53 | } 54 | 55 | menuoption_t *opt = menu_current->options + menu_current->selection; 56 | 57 | const qboolean select = IN_ButtonPressed(PAD_CROSS); 58 | const qboolean left = IN_ButtonPressed(PAD_LEFT); 59 | const qboolean right = IN_ButtonPressed(PAD_RIGHT); 60 | 61 | switch (opt->type) { 62 | case OPT_BUTTON: 63 | if (select) { 64 | if (opt->callback) 65 | opt->callback(opt); 66 | if (opt->button.nextmenu) { 67 | menu_t *prev = menu_current; 68 | menu_current = opt->button.nextmenu; 69 | menu_current->prev = prev; 70 | return; 71 | } 72 | } 73 | break; 74 | 75 | case OPT_CHOICE: 76 | if (select || right) { 77 | *(s32 *)opt->value += 1; 78 | if (*(s32 *)opt->value >= opt->choice.numchoices) 79 | *(s32 *)opt->value = 0; 80 | } else if (left) { 81 | *(s32 *)opt->value -= 1; 82 | if (*(s32 *)opt->value < 0) 83 | *(s32 *)opt->value = opt->choice.numchoices - 1; 84 | } 85 | if (select || right || left) { 86 | if (opt->callback) 87 | opt->callback(opt); 88 | } 89 | break; 90 | 91 | case OPT_SLIDER: 92 | if (select || right) { 93 | *(s32 *)opt->value += opt->slider.step; 94 | if (*(s32 *)opt->value > opt->slider.max) 95 | *(s32 *)opt->value = opt->slider.max; 96 | } else if (left) { 97 | *(s32 *)opt->value -= opt->slider.step; 98 | if (*(s32 *)opt->value < opt->slider.min) 99 | *(s32 *)opt->value = opt->slider.min; 100 | } 101 | if (select || right || left) { 102 | if (opt->callback) 103 | opt->callback(opt); 104 | } 105 | break; 106 | } 107 | } 108 | 109 | static void DrawOption(s32 x, s32 y, const menuoption_t *opt, const qboolean selected) { 110 | if (selected) 111 | Scr_DrawText(x - 10, y, C_WHITE, "\x0d"); 112 | 113 | Scr_DrawTextOffset(opt->type == OPT_LABEL ? 128 : 0, x, y, C_WHITE, opt->title); 114 | 115 | x += 104; 116 | 117 | s32 v = 0; 118 | 119 | switch (opt->type) { 120 | case OPT_CHOICE: 121 | v = *(s32 *)opt->value; 122 | Scr_DrawTextOffset(128, x, y, C_WHITE, opt->choice.choices[v]); 123 | break; 124 | case OPT_SLIDER: 125 | v = *(s32 *)opt->value; 126 | Scr_DrawText(x, y, C_WHITE, "\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82"); 127 | x += 8 + (v / opt->slider.step) * 8; 128 | Scr_DrawText(x, y, C_WHITE, "\x83"); 129 | break; 130 | } 131 | } 132 | 133 | void Menu_Draw(void) { 134 | if (!menu_current) 135 | return; 136 | 137 | Scr_DrawBlendHalf(0, 0, 0); 138 | Scr_DrawPic(40, 32, C_WHITE, pic_quake); 139 | 140 | s32 basex = 40 + 16 + pic_quake->size.u; 141 | s32 basey = 32; 142 | Scr_DrawTextOffset(128, basex, basey, C_WHITE, menu_current->title); 143 | basey += 16; 144 | 145 | for (int i = 0; i < menu_current->numoptions; ++i) { 146 | const menuoption_t *opt = menu_current->options + i; 147 | DrawOption(basex, basey, opt, i == menu_current->selection); 148 | basey += 8; 149 | } 150 | } 151 | 152 | qboolean Menu_IsOpen(void) { 153 | return menu_current != NULL; 154 | } 155 | -------------------------------------------------------------------------------- /tools/bspconvpsx/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../common/psxtypes.h" 7 | #include "../common/idbsp.h" 8 | #include "../common/util.h" 9 | 10 | #define MAX_TOKEN 2048 11 | 12 | #define XBSP_SCALE 1 13 | 14 | void img_palettize(const u8 *src, u8 *dst, const int w, const int h, const u8 *pal, const int palsiz); 15 | void img_unpalettize(const u8 *src, u8 *dst, const int w, const int h, const u8 *pal); 16 | int img_quantize(const u8 *src, u8 *dst, const int w, const int h, u8 *outpal); 17 | 18 | const char *com_parse(const char *data, char *com_token); 19 | 20 | int resmap_parse(const char *fname, char *list, const int max_num, const int entry_len, const int name_len); 21 | 22 | const void *qmemsearch(const void *src, size_t src_len, const void *find, size_t find_len); 23 | 24 | static inline x32 f32_to_x32(const f32 x) { 25 | return (x32)(x * (f32)FIXSCALE); 26 | } 27 | 28 | static inline x16 f32_to_x16deg(const f32 x) { 29 | return (x16)(x * (f32)FIXSCALE / 360.f); 30 | } 31 | 32 | static inline s16vec2_t qvec2_to_s16vec2(const qvec2_t v) { 33 | return (s16vec2_t){ (s16)v[0], (s16)v[1] }; 34 | } 35 | 36 | static inline u8vec2_t qvec2_to_u8vec2(const qvec2_t v) { 37 | return (u8vec2_t){ (u8)v[0], (u8)v[1] }; 38 | } 39 | 40 | static inline s16vec3_t qvec3_to_s16vec3(const qvec3_t v) { 41 | return (s16vec3_t){ (s16)round(v[0]) * XBSP_SCALE, (s16)round(v[1]) * XBSP_SCALE, (s16)round(v[2]) * XBSP_SCALE }; 42 | } 43 | 44 | static inline x16vec3_t qvec3_to_x16vec3(const qvec3_t v) { 45 | const x16 x = f32_to_x32(v[0]); 46 | const x16 y = f32_to_x32(v[1]); 47 | const x16 z = f32_to_x32(v[2]); 48 | return (x16vec3_t){ x, y, z }; 49 | } 50 | 51 | static inline x16vec3_t qvec3_to_x16vec3_angles(const qvec3_t v) { 52 | const x16 x = f32_to_x16deg(v[0]); 53 | const x16 y = f32_to_x16deg(v[1]); 54 | const x16 z = f32_to_x16deg(v[2]); 55 | return (x16vec3_t){ x, y, z }; 56 | } 57 | 58 | static inline x32vec3_t qvec3_to_x32vec3(const qvec3_t v) { 59 | const x32 x = f32_to_x32(v[0]); 60 | const x32 y = f32_to_x32(v[1]); 61 | const x32 z = f32_to_x32(v[2]); 62 | return (x32vec3_t){ x, y, z }; 63 | } 64 | 65 | static inline f32 qdot2(const qvec2_t a, const qvec2_t b) { 66 | return a[0] * b[0] + a[1] * b[1]; 67 | } 68 | 69 | static inline f32 qdot3(const qvec3_t a, const qvec3_t b) { 70 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 71 | } 72 | 73 | static inline f32 *qvec3_sub(qvec3_t a, qvec3_t b, qvec3_t c) { 74 | c[0] = a[0] - b[0]; 75 | c[1] = a[1] - b[1]; 76 | c[2] = a[2] - b[2]; 77 | return c; 78 | } 79 | 80 | static inline f32 *qvec3_add(qvec3_t a, qvec3_t b, qvec3_t c) { 81 | c[0] = a[0] + b[0]; 82 | c[1] = a[1] + b[1]; 83 | c[2] = a[2] + b[2]; 84 | return c; 85 | } 86 | 87 | static inline f32 *qvec2_sub(qvec2_t a, qvec2_t b, qvec2_t c) { 88 | c[0] = a[0] - b[0]; 89 | c[1] = a[1] - b[1]; 90 | return c; 91 | } 92 | 93 | static inline f32 *qvec2_add(qvec2_t a, qvec2_t b, qvec2_t c) { 94 | c[0] = a[0] + b[0]; 95 | c[1] = a[1] + b[1]; 96 | return c; 97 | } 98 | 99 | 100 | static inline f32 qvec3_norm(qvec3_t a) { 101 | // quake uses sqrt here instead of sqrtf, wonder why 102 | const f32 len = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); 103 | if (len) { 104 | const f32 s = 1.f / len; 105 | a[0] *= s; 106 | a[1] *= s; 107 | a[2] *= s; 108 | } 109 | return len; 110 | } 111 | 112 | static inline f32 qvec2_norm(qvec2_t a) { 113 | const f32 len = sqrt(a[0] * a[0] + a[1] * a[1]); 114 | if (len) { 115 | const f32 s = 1.f / len; 116 | a[0] *= s; 117 | a[1] *= s; 118 | } 119 | return len; 120 | } 121 | 122 | static inline f32 *qvec3_scale(qvec3_t src, const f32 s, qvec3_t dst) { 123 | dst[0] = src[0] * s; 124 | dst[1] = src[1] * s; 125 | dst[2] = src[2] * s; 126 | return dst; 127 | } 128 | 129 | static inline f32 *qvec2_scale(qvec2_t src, const f32 s, qvec2_t dst) { 130 | dst[0] = src[0] * s; 131 | dst[1] = src[1] * s; 132 | return dst; 133 | } 134 | static inline f32 *qvec3_copy(qvec3_t src, qvec3_t dst) { 135 | dst[0] = src[0]; 136 | dst[1] = src[1]; 137 | dst[2] = src[2]; 138 | return dst; 139 | } 140 | 141 | static inline f32 *qvec2_copy(qvec2_t src, qvec2_t dst) { 142 | dst[0] = src[0]; 143 | dst[1] = src[1]; 144 | return dst; 145 | } 146 | 147 | static inline f32 ffract(const float x) { 148 | return x - floorf(x); 149 | } 150 | 151 | static inline f32 fsfract(const float x) { 152 | return x - (long)(x); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/exception.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | #include "dbgfont.h" 11 | #include "exception.h" 12 | 13 | #define EX_PRINTF(...) \ 14 | snprintf(com_vastring, sizeof(com_vastring), __VA_ARGS__); \ 15 | Dbg_PrintString(com_vastring); 16 | 17 | static u32 exception_event; 18 | 19 | static inline const char *ExceptionCause(const u32 cr) { 20 | static const char *msg[] = { 21 | "EXT INT", 22 | "?", 23 | "?", 24 | "?", 25 | "READ ERR", 26 | "WRITE ERR", 27 | "CMD BUS ERR", 28 | "DATA BUS ERR", 29 | "SYSCALL", 30 | "BREAK", 31 | "UNK COMMAND", 32 | "NO COP", 33 | "OVERFLOW", 34 | }; 35 | return msg[(cr & 63) >> 2]; 36 | } 37 | 38 | static u32 GetCause(void) { 39 | u32 cr = 0; 40 | __asm__ volatile("mfc0 %0, $13": "=r"(cr) ::); 41 | return cr; 42 | } 43 | 44 | static void ExceptionFunc(void) { 45 | // don't double except 46 | static volatile int inexception = false; 47 | if (inexception) { 48 | printf("DOUBLE FAULT\n"); 49 | while (1); 50 | } 51 | inexception = true; 52 | 53 | // get current thread's TCB and make a copy of the relevant data 54 | const struct ToT *tot = (const struct ToT *)0x100; 55 | const struct TCB *tcb_list = (const struct TCB *)tot[2].head; 56 | const u32 status = tcb_list[0].status; 57 | const u32 cr = GetCause(); 58 | u32 regs[40]; 59 | memcpy(regs, tcb_list[0].reg, sizeof(regs)); 60 | 61 | // spew to tty 62 | printf("UH OH ZONE\nSTATUS=%08x\nCR=%08x\nPC=%08x\nRA=%08x\n", status, cr, regs[R_EPC], regs[R_RA]); 63 | 64 | // setup graphics viewport and clear screen 65 | DISPENV disp; 66 | DRAWENV draw; 67 | ResetGraph(0); 68 | SetDispMask(0); 69 | SetDefDispEnv(&disp, 0, 0, VID_WIDTH, VID_HEIGHT); 70 | SetDefDrawEnv(&draw, 0, 0, VID_WIDTH, VID_HEIGHT); 71 | setRGB0(&draw, 0x40, 0x00, 0x00); draw.isbg = 1; draw.dtd = 1; 72 | PutDispEnv(&disp); 73 | PutDrawEnv(&draw); 74 | 75 | // init debug printer 76 | Dbg_PrintReset(8, 16, 0x40, 0x00, 0x00); 77 | 78 | // the most important info 79 | const char *cause = ExceptionCause(cr); 80 | EX_PRINTF("UH OH ZONE\n\n"); 81 | EX_PRINTF("CAUSE: %s / %08x\nSTATUS: %08x\n\n", cause, cr, status); 82 | EX_PRINTF("PC=%08x RA=%08x\n\n", regs[R_EPC],regs[R_RA]); 83 | 84 | // set display mask right away so that we can at least see the header 85 | SetDispMask(1); 86 | 87 | // GPR dump 88 | EX_PRINTF("AT=%08x K0=%08x K1=%08x\n", regs[R_AT], regs[R_K0], regs[R_K1]); 89 | EX_PRINTF("A0=%08x A1=%08x A2=%08x\n", regs[R_A0], regs[R_A1], regs[R_A2]); 90 | EX_PRINTF("A3=%08x V0=%08x V1=%08x\n", regs[R_A3], regs[R_V0], regs[R_V1]); 91 | EX_PRINTF("T0=%08x T1=%08x T2=%08x\n", regs[R_T0], regs[R_T1], regs[R_T2]); 92 | EX_PRINTF("T3=%08x T4=%08x T5=%08x\n", regs[R_T3], regs[R_T4], regs[R_T5]); 93 | EX_PRINTF("T6=%08x T7=%08x T8=%08x\n", regs[R_T6], regs[R_T7], regs[R_T8]); 94 | EX_PRINTF("T9=%08x S0=%08x S1=%08x\n", regs[R_T9], regs[R_S0], regs[R_S1]); 95 | EX_PRINTF("S2=%08x S3=%08x S4=%08x\n", regs[R_S2], regs[R_S3], regs[R_S4]); 96 | EX_PRINTF("S5=%08x S6=%08x S7=%08x\n", regs[R_S5], regs[R_S6], regs[R_S7]); 97 | EX_PRINTF("GP=%08x FP=%08x SP=%08x\n", regs[R_GP], regs[R_FP], regs[R_SP]); 98 | 99 | // dump stack if possible 100 | u32 sp = regs[R_SP] & ~0x0Fu; 101 | if (sp > 0x80100000u && sp < 0x80200000u) { 102 | EX_PRINTF("\nSTACK FROM %08x\n\n", sp); 103 | for (u32 i = 0; sp < 0x80200000u && i < 15; sp += 16, ++i) { 104 | const u32 *data = (const u32 *)sp; 105 | EX_PRINTF("%02x %08x %08x %08x %08x\n", sp & 0xFF, data[0], data[1], data[2], data[3]); 106 | } 107 | } 108 | 109 | while (1); 110 | } 111 | 112 | void Sys_InstallExceptionHandler(void) { 113 | // HwCPU fires when there's an unhandled CPU exception 114 | EnterCriticalSection(); 115 | exception_event = OpenEvent(HwCPU, EvSpTRAP, EvMdINTR, ExceptionFunc); 116 | EnableEvent(exception_event); 117 | ExitCriticalSection(); 118 | 119 | printf("Sys_InstallExceptionHandler(): opened HwCPU event %08x\n", exception_event); 120 | 121 | /* 122 | // alternatively: 123 | // A(40h) - SystemErrorUnresolvedException() 124 | void **a0tab = (void **)0x200; 125 | const void *old = a0tab[0x40]; 126 | a0tab[0x40] = exception_cb; 127 | printf("ex_install_handler(): hooked exception handler: %p -> %p\n", old, exception_cb); 128 | */ 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | 7 | ## QuakePSX 8 | 9 | This is an experimental port of the original Quake to the Sony PlayStation. 10 | 11 | It is based on the original Quake codebase, with the main idea being to keep it as close to the original as possible, other than the resource formats. 12 | 13 | ### Status 14 | * Runs on both emulators and real hardware at mindblowing framerates of ~15 FPS on average. 15 | * DualShock controllers and the PS Mouse are supported for input. 16 | * CD Audio is supported, if the game is built with CDDA tracks. 17 | * Map conversion is far from perfect and has many texture alignment and lighting issues. 18 | * The entirety of Episode 1 is playable, but many maps from Episode 2 and some from other episodes do not fit in memory. 19 | * There is no multiplayer of any kind. 20 | * There is no save system. 21 | * The renderer is slow and lacks good triangle subdivision/clipping and dynamic lighting support. 22 | * There are some bugs in the movement code that can cause you to get stuck or gain high speed for no reason. 23 | * There might be many other bugs. 24 | 25 | ### Running 26 | 27 | You can get a [prebuilt CD image](https://github.com/fgsfdsfgs/quakepsx/releases/tag/latest-shareware) of QuakePSX with the Quake Shareware data files. 28 | 29 | If you want to run the full version (note that registered episodes mostly don't work right now; refer to the Status section above for details) or add CD music, you will have to build your own CD image. 30 | Refer to the Building section below for instructions. 31 | 32 | Once you have a CD image, you can burn it to a CD-R and run it on a modchipped console, or load it using something like FreePSXBoot. 33 | It will also run on modern emulators, such as DuckStation or PCSX-Redux. I have not tested whether it works with ODEs, but it should. 34 | 35 | ### Building 36 | 37 | #### Prerequisites: 38 | * Linux or WSL, though MSYS might work as well; 39 | * [PSn00bSDK](https://github.com/Lameguy64/PSn00bSDK); 40 | * [mkpsxiso](https://github.com/Lameguy64/mkpsxiso); 41 | * `gcc` for your host platform; 42 | * `mipsel-none-elf-gcc`, preferably versions 12.2 - 13.3; 43 | * `cmake`, `git`, `make`; 44 | * Quake (shareware or full version). 45 | 46 | #### Instructions: 47 | 1. Install the prerequisites and ensure `mipsel-none-elf-gcc` and `mkpsxiso` are in your `PATH` and the PSn00bSDK environment variables are set: 48 | ``` 49 | export PSN00BSDK_LIBS=/path/to/psn00bsdk/lib/libpsn00b 50 | export PATH=/path/to/psn00bsdk/bin:$PATH 51 | ``` 52 | 2. Clone the repo: 53 | ``` 54 | git clone https://github.com/fgsfdsfgs/quakepsx.git && cd quakepsx 55 | ``` 56 | 4. (Optional) You can put the music tracks from Quake into `id1psx/music/`. 57 | Tracks should be in WAV format and named `track02.wav ... track11.wav`. 58 | 5. Build the tools and cook the maps: 59 | ``` 60 | ./tools/scripts/cook_id1.sh /path/to/Quake/id1 61 | ``` 62 | 7. If everything completed with no errors, you can now configure the game: 63 | * If you don't have the music: 64 | ``` 65 | cmake --preset default -DCMAKE_BUILD_TYPE=RelWithDebInfo -Bbuild 66 | ``` 67 | * If you do have the music: 68 | ``` 69 | cmake --preset default -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_CDDA=ON -Bbuild 70 | ``` 71 | 8. Build: 72 | ``` 73 | cmake --build build -j4 74 | ``` 75 | 9. This should produce `quakepsx.bin` and `quakepsx.cue` in the `build` directory. 76 | 77 | ### Credits 78 | * id Software for the [original Quake](https://github.com/id-Software/Quake); 79 | * Lameguy64, spicyjpeg and contributors for [PSn00bSDK](https://github.com/Lameguy64/PSn00bSDK) and [mkpsxiso](https://github.com/Lameguy64/mkpsxiso); 80 | * [PCSX-Redux authors](https://github.com/grumpycoders/pcsx-redux/blob/main/AUTHORS) for PCSX-Redux, which was used for testing and debugging; 81 | * [DuckStation authors](https://github.com/stenzek/duckstation/blob/master/CONTRIBUTORS.md) for DuckStation, which was used for testing and debugging; 82 | * Martin Korth for PSX-SPX and no$psx; 83 | * [psxavenc authors](https://github.com/WonderfulToolchain/psxavenc) for libpsxav; 84 | * Sean Barrett for [stb_image](https://github.com/nothings/stb); 85 | * David Reid for [dr_wav](https://github.com/mackron/dr_libs/blob/master/dr_wav.h); 86 | * PSX.Dev Discord server for help and advice; 87 | * anyone else who helped with this project that I might be forgetting. 88 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qbsp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "qbsp.h" 3 | #include "util.h" 4 | #include "../common/psxtypes.h" 5 | #include "../common/idbsp.h" 6 | 7 | qbsp_t qbsp; 8 | 9 | int qbsp_init(qbsp_t *qbsp, u8 *bsp, const size_t size) { 10 | qbsp->start = bsp; 11 | qbsp->size = size; 12 | 13 | if (qbsp->size < sizeof(*qbsp->header)) 14 | panic("malformed Quake BSP: size < %d", sizeof(*qbsp->header)); 15 | 16 | qbsp->header = (qbsphdr_t *)qbsp->start; 17 | if (qbsp->header->ver != QBSPVERSION) 18 | panic("malformed Quake BSP: ver != %d", QBSPVERSION); 19 | 20 | // set lump pointers 21 | 22 | qbsp->numplanes =qbsp->header->planes.len / sizeof(qplane_t); 23 | qbsp->planes = (qplane_t *)(qbsp->start + qbsp->header->planes.ofs); 24 | 25 | qbsp->numnodes = qbsp->header->nodes.len / sizeof(qnode_t); 26 | qbsp->nodes = (qnode_t *)(qbsp->start + qbsp->header->nodes.ofs); 27 | 28 | qbsp->numcnodes = qbsp->header->clipnodes.len / sizeof (qclipnode_t); 29 | qbsp->cnodes = (qclipnode_t *)(qbsp->start + qbsp->header->clipnodes.ofs); 30 | 31 | qbsp->numleafs = qbsp->header->leafs.len / sizeof (qleaf_t); 32 | qbsp->leafs = (qleaf_t *)(qbsp->start + qbsp->header->leafs.ofs); 33 | 34 | qbsp->nummarksurf = qbsp->header->marksurfaces.len / sizeof(u16); 35 | qbsp->marksurf = (u16 *)(qbsp->start + qbsp->header->marksurfaces.ofs); 36 | 37 | qbsp->numvisdata = qbsp->header->visilist.len; 38 | qbsp->visdata = qbsp->start + qbsp->header->visilist.ofs; 39 | 40 | qbsp->numfaces = qbsp->header->faces.len / sizeof(qface_t); 41 | qbsp->faces = (qface_t *)(qbsp->start + qbsp->header->faces.ofs); 42 | 43 | qbsp->numsurfedges = qbsp->header->surfedges.len / sizeof(s32); 44 | qbsp->surfedges = (s32 *)(qbsp->start + qbsp->header->surfedges.ofs); 45 | 46 | qbsp->numedges = qbsp->header->edges.len / sizeof(qedge_t); 47 | qbsp->edges = (qedge_t *)(qbsp->start + qbsp->header->edges.ofs); 48 | 49 | qbsp->numverts = qbsp->header->vertices.len / sizeof(qvert_t); 50 | qbsp->verts = (qvert_t *)(qbsp->start + qbsp->header->vertices.ofs); 51 | 52 | qbsp->numtexinfos = qbsp->header->texinfo.len / sizeof(qtexinfo_t); 53 | qbsp->texinfos = (qtexinfo_t *)(qbsp->start + qbsp->header->texinfo.ofs); 54 | 55 | qbsp->nummodels = qbsp->header->models.len / sizeof(qmodel_t); 56 | qbsp->models = (qmodel_t *)(qbsp->start + qbsp->header->models.ofs); 57 | 58 | qbsp->miptex = (qmiptexlump_t *)(qbsp->start + qbsp->header->miptex.ofs); 59 | 60 | qbsp->numlightdata = qbsp->header->lightmaps.len; 61 | qbsp->lightdata = qbsp->start + qbsp->header->lightmaps.ofs; 62 | 63 | return 0; 64 | } 65 | 66 | const qmiptex_t *qbsp_get_miptex(const qbsp_t *qbsp, const int i) { 67 | return (qbsp->miptex->dataofs[i] >= 0) ? 68 | (const qmiptex_t *)((const u8 *)qbsp->miptex + qbsp->miptex->dataofs[i]) : 69 | NULL; 70 | } 71 | 72 | u16 qbsp_light_for_vert(const qbsp_t *qbsp, const qface_t *qf, const qvec3_t v, qvec2_t sorg, qvec2_t sext, u16 *out) { 73 | const qtexinfo_t *qti = qbsp->texinfos + qf->texinfo; 74 | const qmiptex_t *qmt = qbsp_get_miptex(qbsp, qti->miptex); 75 | 76 | if (qmt && (qmt->name[0] == '+' || strstr(qmt->name, "*lava") || strstr(qmt->name, "*tele"))) { 77 | // lava, animated texture (presumably button or monitor) or teleport surface => extra bright 78 | out[0] = out[1] = out[2] = out[3] = 0x60; 79 | return 0x60; 80 | } 81 | 82 | if (qti->flags & (TEXF_SPECIAL | TEXF_LIQUID | TEXF_SKY)) { 83 | // this is water or sky or some shit => fullbright 84 | out[0] = out[1] = out[2] = out[3] = 0x40; 85 | return 0x40; 86 | } 87 | 88 | if (qf->lightofs < 0) { 89 | // no lightmap => 100% dark 90 | out[0] = out[1] = out[2] = out[3] = 0; 91 | return 0; 92 | } 93 | 94 | const u8 *samples = qbsp->lightdata + qf->lightofs; 95 | 96 | // don't have to check whether the point is on the surface, since we already know it is 97 | const int s = qdot3(v, qti->vecs[0]) + qti->vecs[0][3]; 98 | const int t = qdot3(v, qti->vecs[1]) + qti->vecs[1][3]; 99 | int ds = s - (int)sorg[0]; 100 | int dt = t - (int)sorg[1]; 101 | 102 | if (ds < 0) ds = 0; 103 | if (ds > sext[0]) ds = sext[0]; 104 | if (dt < 0) dt = 0; 105 | if (dt > sext[1]) dt = sext[1]; 106 | 107 | ds >>= 4; 108 | dt >>= 4; 109 | 110 | u32 r = 0; 111 | u32 scale = 255; // og max light is 264, but for us anything above 128 is overbright 112 | samples += dt * (((int)sext[0] >> 4) + 1) + ds; 113 | for (int m = 0; m < MAX_LIGHTMAPS && qf->styles[m] != 255; m++) { 114 | const u32 v = *samples * scale; 115 | r += v; 116 | out[m] = v >> 8; 117 | samples += (((int)sext[0]>>4)+1) * (((int)sext[1]>>4)+1); 118 | } 119 | return r >> 8; 120 | } 121 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "types.h" 5 | #include "fixed.h" 6 | 7 | #pragma pack(push, 1) 8 | 9 | #define DECLARE_VEC4_T_WORD(ctype, wtype) \ 10 | typedef union ctype ## vec4_u { \ 11 | struct { ctype x, y, z, w; }; \ 12 | struct { ctype u, v, p, q; }; \ 13 | struct { ctype r, g, b, a; }; \ 14 | ctype d[4]; \ 15 | wtype word; \ 16 | } ctype ## vec4_t 17 | 18 | #define DECLARE_VEC3_T(ctype) \ 19 | typedef union ctype ## vec3_u { \ 20 | struct { ctype x, y, z; }; \ 21 | struct { ctype u, v, p; }; \ 22 | struct { ctype r, g, b; }; \ 23 | ctype d[3]; \ 24 | } ctype ## vec3_t 25 | 26 | #define DECLARE_VEC2_T(ctype) \ 27 | typedef union ctype ## vec2_u { \ 28 | struct { ctype x, y; }; \ 29 | struct { ctype u, v; }; \ 30 | ctype d[2]; \ 31 | } ctype ## vec2_t 32 | 33 | #define DECLARE_VEC2_T_WORD(ctype, wtype) \ 34 | typedef union ctype ## vec2_u { \ 35 | struct { ctype x, y; }; \ 36 | struct { ctype u, v; }; \ 37 | ctype d[2]; \ 38 | wtype word; \ 39 | } ctype ## vec2_t 40 | 41 | #pragma pack(pop) 42 | 43 | DECLARE_VEC4_T_WORD(u8, u32); 44 | 45 | DECLARE_VEC3_T(x32); 46 | DECLARE_VEC3_T(s32); 47 | DECLARE_VEC3_T(x16); 48 | DECLARE_VEC3_T(s16); 49 | DECLARE_VEC3_T(u8); 50 | 51 | DECLARE_VEC2_T(x32); 52 | DECLARE_VEC2_T(s32); 53 | DECLARE_VEC2_T_WORD(x16, u32); 54 | DECLARE_VEC2_T_WORD(s16, u32); 55 | DECLARE_VEC2_T_WORD(u8, u16); 56 | DECLARE_VEC2_T_WORD(s8, u16); 57 | 58 | extern x32vec3_t x32vec3_origin; 59 | extern x16vec3_t x16vec3_origin; 60 | 61 | #define XVecAdd(a, b, c) \ 62 | { (c)->x = (a)->x + (b)->x; (c)->y = (a)->y + (b)->y; (c)->z = (a)->z + (b)->z; } 63 | 64 | #define XVecAddInt(a, bx, by, bz, c) \ 65 | { (c)->x = (a)->x + TO_FIX32(bx); (c)->y = (a)->y + TO_FIX32(by); (c)->z = (a)->z + TO_FIX32(bz); } 66 | 67 | #define XVecSub(a, b, c) \ 68 | { (c)->x = (a)->x - (b)->x; (c)->y = (a)->y - (b)->y; (c)->z = (a)->z - (b)->z; } 69 | 70 | #define XVecDotSS(a, b) \ 71 | ((XMUL16((x32)((a)->x), (x32)((b)->x)) + XMUL16((x32)((a)->y), (x32)((b)->y)) + XMUL16((x32)((a)->z), (x32)((b)->z)))) 72 | 73 | #define XVecScaleS(a, b, c) \ 74 | { (c)->x = XMUL16((x32)((a)->x), (x32)(b)); (c)->y = XMUL16((x32)((a)->y), (x32)(b)); (c)->z = XMUL16((x32)((a)->z), (x32)(b)); } 75 | 76 | #define XVecScaleLS(a, b, c) \ 77 | { (c)->x = xmul32((b), (x32)((a)->x)); (c)->y = xmul32((b), (x32)((a)->y)); (c)->z = xmul32((b), (x32)((a)->z)); } 78 | 79 | #define XVecScaleSL(a, b, c) \ 80 | { (c)->x = xmul32((a)->x, (x32)(b)); (c)->y = xmul32((a)->y, (x32)(b)); (c)->z = xmul32((a)->z, (x32)(b)); } 81 | 82 | #define XVecScaleInt(a, b, c) \ 83 | { (c)->x = (a)->x * (x32)(b); (c)->y = (a)->y * (x32)(b); (c)->z = (a)->z * (x32)(b); } 84 | 85 | #define XVecLengthS(v) \ 86 | (SquareRoot12((v)->x * (v)->x + (v)->y * (v)->y + (v)->z * (v)->z)) 87 | 88 | #define XVecLengthL(v) \ 89 | (SquareRoot12(XVecLengthSqrL(v))) 90 | 91 | #define XVecLengthIntL(v) \ 92 | (SquareRoot0(XVecLengthSqrIntL(v))) 93 | 94 | #define XVecZero(v) \ 95 | { (v)->x = 0; (v)->y = 0; (v)->z = 0; } 96 | 97 | #define XVecNegate(a, b) \ 98 | { (b)->x = -(a)->x; (b)->y = -(a)->y; (b)->z = -(a)->z; } 99 | 100 | #define XVecSet(a, ax, ay, az) \ 101 | { (a)->x = (ax); (a)->y = (ay); (a)->z = (az); } 102 | 103 | #define XVecSetInt(a, ax, ay, az) \ 104 | { (a)->x = TO_FIX32(ax); (a)->y = TO_FIX32(ay); (a)->z = TO_FIX32(az); } 105 | 106 | #define XVecEq(a, b) \ 107 | ((a)->x == (b)->x && (a)->y == (b)->y && (a)->z == (b)->z) 108 | 109 | FORCEINLINE void XVecSignLS(const x32vec3_t *in, x16vec3_t *out) { 110 | out->x = xsign32(in->x); 111 | out->y = xsign32(in->y); 112 | out->z = xsign32(in->z); 113 | } 114 | 115 | FORCEINLINE void XVecMulAddLSL(const x32vec3_t *a, const x16 b, const x32vec3_t *c, x32vec3_t *out) { 116 | out->x = a->x + xmul32(b, c->x); 117 | out->y = a->y + xmul32(b, c->y); 118 | out->z = a->z + xmul32(b, c->z); 119 | } 120 | 121 | FORCEINLINE void XVecMulAddS(const x16vec3_t *a, const x16 b, const x16vec3_t *c, x16vec3_t *out) { 122 | out->x = a->x + xmul32(b, c->x); 123 | out->y = a->y + xmul32(b, c->y); 124 | out->z = a->z + xmul32(b, c->z); 125 | } 126 | 127 | FORCEINLINE x32 XVecDotLL(const x32vec3_t *a, const x32vec3_t *b) { 128 | return (xmul32(a->x, b->x) + xmul32(a->y, b->y) + xmul32(a->z, b->z)); 129 | } 130 | 131 | FORCEINLINE x32 XVecDotSL(const x16vec3_t *a, const x32vec3_t *b) { 132 | return (xmul32(a->x, b->x) + xmul32(a->y, b->y) + xmul32(a->z, b->z)); 133 | } 134 | 135 | extern x32 XVecLengthSqrL(const x32vec3_t *v); 136 | extern s32 XVecLengthSqrIntL(const x32vec3_t *v); 137 | extern x32 XVecNormLS(const x32vec3_t *in, x16vec3_t *out, x32 *out_xysqrlen); 138 | extern void XVecCrossSS(const x16vec3_t *a, const x16vec3_t *b, x16vec3_t *out); 139 | -------------------------------------------------------------------------------- /tools/bspconvpsx/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../common/pak.h" 10 | #include "../common/util.h" 11 | #include "util.h" 12 | 13 | void img_unpalettize(const u8 *src, u8 *dst, const int w, const int h, const u8 *pal) { 14 | const u8 *ps = src; 15 | u8 *pd = dst; 16 | for (int i = 0; i < w * h; ++i, ++ps, pd += 3) { 17 | const int c = *ps; 18 | pd[0] = pal[3 * c + 0]; 19 | pd[1] = pal[3 * c + 1]; 20 | pd[2] = pal[3 * c + 2]; 21 | } 22 | } 23 | 24 | u8 img_closest_color(const u8 rgb[3], const u8 *pal, const int palsiz) { 25 | double mindist = 256.0 * 256.0 * 256.0; 26 | int mincol = 0; 27 | for (int i = 0; i < palsiz; ++i) { 28 | const u8 *prgb = pal + i * 3; 29 | if (rgb[0] == prgb[0] && rgb[1] == prgb[1] && rgb[2] == prgb[2]) 30 | return i; 31 | const double dc[3] = { 32 | (double)rgb[0] - (double)prgb[0], 33 | (double)rgb[1] - (double)prgb[1], 34 | (double)rgb[2] - (double)prgb[2], 35 | }; 36 | const double dist = sqrt(dc[0] * dc[0] + dc[1] * dc[1] + dc[2] * dc[2]); 37 | if (dist < mindist) { 38 | mindist = dist; 39 | mincol = i; 40 | } 41 | } 42 | return mincol; 43 | } 44 | 45 | void img_palettize(const u8 *src, u8 *dst, const int w, const int h, const u8 *pal, const int palsiz) { 46 | const u8 *ps = src; 47 | u8 *pd = dst; 48 | for (int i = 0; i < w * h; ++i, ++pd, ps += 3) { 49 | const int c = img_closest_color(ps, pal, palsiz); 50 | *pd = c; 51 | } 52 | } 53 | 54 | int img_quantize(const u8 *src, u8 *dst, const int w, const int h, u8 *outpal) { 55 | const u8 *ps = src; 56 | u8 *pd = dst; 57 | int numc = 0; 58 | for (int i = 0; i < w * h; ++i, ++pd, ps += 3) { 59 | int c; 60 | for (c = 0; c < numc; ++c) 61 | if (outpal[c * 3 + 0] == ps[0] && outpal[c * 3 + 1] == ps[1] && outpal[c * 3 + 2] == ps[2]) 62 | break; 63 | if (c == numc) { 64 | outpal[c * 3 + 0] = ps[0]; 65 | outpal[c * 3 + 1] = ps[1]; 66 | outpal[c * 3 + 2] = ps[2]; 67 | ++numc; 68 | } 69 | } 70 | return numc; 71 | } 72 | 73 | // COM_Parse from Quake 74 | const char *com_parse(const char *data, char *com_token) { 75 | int c; 76 | int len; 77 | 78 | len = 0; 79 | com_token[0] = 0; 80 | 81 | if (!data) 82 | return NULL; 83 | 84 | // skip whitespace 85 | skipwhite: 86 | while ( (c = *data) <= ' ') { 87 | if (c == 0) 88 | return NULL; // end of file; 89 | data++; 90 | } 91 | 92 | // skip // comments 93 | if (c=='/' && data[1] == '/') { 94 | while (*data && *data != '\n') 95 | data++; 96 | goto skipwhite; 97 | } 98 | 99 | 100 | // handle quoted strings specially 101 | if (c == '\"') { 102 | data++; 103 | while (1) { 104 | c = *data++; 105 | if (c=='\"' || !c) { 106 | com_token[len] = 0; 107 | return data; 108 | } 109 | com_token[len] = c; 110 | len++; 111 | } 112 | } 113 | 114 | // parse single characters 115 | if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') { 116 | com_token[len] = c; 117 | len++; 118 | com_token[len] = 0; 119 | return data+1; 120 | } 121 | 122 | // parse a regular word 123 | do { 124 | com_token[len] = c; 125 | data++; 126 | len++; 127 | c = *data; 128 | if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') 129 | break; 130 | } while (c>32); 131 | 132 | com_token[len] = 0; 133 | return data; 134 | } 135 | 136 | int resmap_parse(const char *fname, char *list, const int max_num, const int entry_len, const int name_len) { 137 | int id = 0xFF; 138 | char line[MAX_TOKEN] = { 0 }; 139 | 140 | FILE *f = fopen(fname, "rb"); 141 | if (!f) { 142 | fprintf(stderr, "resmap_parse(): can't find resmap file %s\n", fname); 143 | return -1; 144 | } 145 | 146 | int max_index = -1; 147 | while (fgets(line, sizeof(line), f)) { 148 | char *p = strtok(line, " \t\r\n"); 149 | if (!p || !isalnum(p[0])) 150 | continue; 151 | id = strtol(p, NULL, 16); 152 | if (id >= max_num) 153 | continue; 154 | p = strtok(NULL, " \t\r\n"); 155 | if (p && p[0]) { 156 | if (id > max_index) 157 | max_index = id; 158 | strncpy(list + id * entry_len, p, name_len - 1); 159 | } 160 | } 161 | 162 | fclose(f); 163 | 164 | return max_index + 1; 165 | } 166 | 167 | const void *qmemsearch(const void *src, size_t src_len, const void *find, size_t find_len) { 168 | for (; find_len <= src_len; src = (u8 *)src + 1, --src_len) { 169 | if (!memcmp(src, find, find_len)) 170 | return src; 171 | } 172 | return NULL; 173 | } 174 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qsfx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DR_WAV_IMPLEMENTATION 1 7 | #include "../common/dr_wav.h" 8 | #include "../common/libpsxav/libpsxav.h" 9 | #include "../common/psxtypes.h" 10 | #include "qsfx.h" 11 | #include "util.h" 12 | 13 | static char qsfxmap[MAX_QSFX][MAX_QSFX_NAME]; 14 | static int num_qsfxmap = 0; 15 | 16 | static qsfx_t qsfx[MAX_QSFX]; 17 | 18 | int qsfxmap_init(const char *mapfile) { 19 | num_qsfxmap = resmap_parse(mapfile, (char *)qsfxmap, MAX_QSFX, sizeof(qsfxmap[0]), sizeof(qsfxmap[0])); 20 | printf("qsfxmap_init(): indexed %d sounds from %s\n", num_qsfxmap, mapfile); 21 | return num_qsfxmap; 22 | } 23 | 24 | int qsfxmap_id_for_name(const char *name) { 25 | for (s32 i = 0; i < num_qsfxmap; ++i) { 26 | if (!strcmp(name, qsfxmap[i])) { 27 | return i; 28 | break; 29 | } 30 | } 31 | return 0; 32 | } 33 | 34 | const char *qsfxmap_name_for_id(const int id) { 35 | if (id <= 0 || id >= num_qsfxmap) 36 | return NULL; 37 | return qsfxmap[id]; 38 | } 39 | 40 | qsfx_t *qsfx_add(const int in_id, const char *name, u8 *start, const size_t size) { 41 | const int id = in_id ? in_id : qsfxmap_id_for_name(name); 42 | if (id <= 0) { 43 | fprintf(stderr, "qsfx_add(): unknown sfx '%s'\n", name); 44 | return NULL; 45 | } 46 | 47 | drwav wav; 48 | if (!drwav_init_memory_with_metadata(&wav, start, size, 0, NULL)) { 49 | fprintf(stderr, "qsfx_add(): could not open wav '%s'\n", name); 50 | return NULL; 51 | } 52 | 53 | if (wav.channels != 1) { 54 | fprintf(stderr, "qsfx_add(): '%s' is not mono\n", name); 55 | drwav_uninit(&wav); 56 | return NULL; 57 | } 58 | 59 | drwav_int16 *srcsamples = malloc(wav.totalPCMFrameCount * sizeof(drwav_int16)); 60 | assert(srcsamples); 61 | 62 | const int srate = wav.sampleRate; 63 | const double stime = (double)wav.totalPCMFrameCount / (double)srate; 64 | int numsrcsamples = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, srcsamples); 65 | 66 | // find loop point 67 | int loop = -1; 68 | if (wav.pMetadata && wav.metadataCount) { 69 | for (int i = 0; i < wav.metadataCount; ++i) { 70 | const drwav_metadata *meta = wav.pMetadata + i; 71 | if (meta->type & drwav_metadata_type_cue) { 72 | if (!meta->data.cue.cuePointCount || !meta->data.cue.pCuePoints) 73 | continue; 74 | const drwav_cue_point *cpt = meta->data.cue.pCuePoints; 75 | if (cpt->blockStart) { 76 | fprintf(stderr, "qsfx_add(): warning: skipping cue points in compressed wav\n"); 77 | break; 78 | } 79 | loop = cpt->sampleByteOffset >> (wav.bitsPerSample != 8); 80 | break; 81 | } 82 | } 83 | } 84 | 85 | drwav_uninit(&wav); 86 | 87 | if (numsrcsamples <= 0) { 88 | fprintf(stderr, "qsfx_add(): could not read samples from '%s'\n", name); 89 | return NULL; 90 | } 91 | 92 | if (srate == 22050 && numsrcsamples > 1) { 93 | // downsample to 11025 by throwing out every other sample 94 | numsrcsamples /= 2; 95 | if (loop > 0) 96 | loop /= 2; 97 | drwav_int16 *dst = malloc(numsrcsamples * sizeof(drwav_int16)); 98 | assert(dst); 99 | for (int i = 0, j = 0; j < numsrcsamples; i += 2, ++j) 100 | dst[j] = srcsamples[i]; 101 | free(srcsamples); 102 | srcsamples = dst; 103 | } 104 | 105 | qsfx_t *sfx = &qsfx[id]; 106 | sfx->id = id; 107 | sfx->samplerate = 11025; 108 | sfx->samples = srcsamples; 109 | sfx->numframes = stime * 60.0; 110 | sfx->numsamples = numsrcsamples; 111 | sfx->loopstart = loop; 112 | 113 | return sfx; 114 | } 115 | 116 | qsfx_t *qsfx_find(const int id) { 117 | if (id <= 0 || id >= MAX_QSFX || !qsfx[id].samples) 118 | return NULL; 119 | return &qsfx[id]; 120 | } 121 | 122 | void qsfx_free(qsfx_t *sfx) { 123 | free(sfx->samples); 124 | sfx->samples = NULL; 125 | } 126 | 127 | int qsfx_convert(qsfx_t *sfx, u8 *outptr, const int maxlen) { 128 | // HACK: the lib apparently has no way to set output buffer size, so 129 | // 4MB ought to be enough for everybody 130 | static u8 tmp[4 * 1024 * 1024]; 131 | 132 | const int loop_start = sfx->loopstart; 133 | const int adpcm_len = psx_audio_spu_encode_simple(sfx->samples, sfx->numsamples, tmp, loop_start); 134 | if (adpcm_len <= 0) { 135 | fprintf(stderr, "could not encode sound '%s'\n", qsfxmap[sfx->id]); 136 | return -1; 137 | } 138 | 139 | if (adpcm_len + 16 > maxlen) { 140 | fprintf(stderr, "could not fit sound '%s' (pcm len %d, adpcm len %d)\n", qsfxmap[sfx->id], sfx->numsamples * 2, adpcm_len + 16); 141 | return -2; 142 | } 143 | 144 | // 16 null bytes of lead-in to avoid pops 145 | memcpy(outptr + 16, tmp, adpcm_len); 146 | return ALIGN(adpcm_len + 16, 8); 147 | } 148 | -------------------------------------------------------------------------------- /src/progs/misc.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | #define SF_LIGHT_START_OFF 1 4 | 5 | #define SF_SHOOTER_SUPERSPIKE 1 6 | #define SF_SHOOTER_LASER 2 7 | 8 | static inline void spawn_ambient(edict_t *self, const s16 sfxid, const s16 vol) { 9 | Snd_StaticSoundId(sfxid, &self->v.origin, vol, ATTN_STATIC); 10 | utl_remove_delayed(self); 11 | } 12 | 13 | void spawn_ambient_comp_hum(edict_t *self) { 14 | spawn_ambient(self, SFXID_AMBIENCE_COMP1, SND_MAXVOL); 15 | } 16 | 17 | void spawn_ambient_drip(edict_t *self) { 18 | // spawn_ambient(self, SFXID_AMBIENCE_DRIP1, SND_MAXVOL >> 1); 19 | } 20 | 21 | void spawn_ambient_drone(edict_t *self) { 22 | spawn_ambient(self, SFXID_AMBIENCE_DRONE6, SND_MAXVOL >> 1); 23 | } 24 | 25 | void spawn_ambient_suck_wind(edict_t *self) { 26 | spawn_ambient(self, SFXID_AMBIENCE_SUCK1, SND_MAXVOL); 27 | } 28 | 29 | void spawn_ambient_swamp1(edict_t *self) { 30 | spawn_ambient(self, SFXID_AMBIENCE_SWAMP1, SND_MAXVOL >> 1); 31 | } 32 | 33 | void spawn_ambient_swamp2(edict_t *self) { 34 | // SWAMP2 is basically the same as SWAMP1 35 | spawn_ambient(self, SFXID_AMBIENCE_SWAMP1, SND_MAXVOL >> 1); 36 | } 37 | 38 | static void explobox_explode(edict_t *self, edict_t *killer) { 39 | self->v.flags &= ~(FL_TAKEDAMAGE | FL_AUTOAIM); 40 | utl_sound(self, CHAN_VOICE, SFXID_WEAPONS_R_EXP3, SND_MAXVOL, ATTN_NORM); 41 | utl_radius_damage(self, self, 160, self); 42 | self->v.origin.z += TO_FIX32(32); 43 | utl_become_explosion(self); 44 | } 45 | 46 | void spawn_misc_explobox(edict_t *self) { 47 | G_SetModel(self, MDLID_B_EXPLOB); 48 | self->v.mins = ((amodel_t *)self->v.model)->mins; 49 | self->v.maxs = ((amodel_t *)self->v.model)->maxs; 50 | G_SetSize(self, &self->v.mins, &self->v.maxs); 51 | 52 | self->v.solid = SOLID_BBOX; 53 | self->v.movetype = MOVETYPE_NONE; 54 | self->v.health = 20; 55 | self->v.th_die = explobox_explode; 56 | self->v.flags |= FL_TAKEDAMAGE | FL_AUTOAIM; 57 | 58 | self->v.origin.z += TO_FIX32(2); 59 | G_DropToFloor(self); 60 | } 61 | 62 | void spawn_misc_explobox2(edict_t *self) { 63 | G_SetModel(self, MDLID_B_EXBOX2); 64 | self->v.mins = ((amodel_t *)self->v.model)->mins; 65 | self->v.maxs = ((amodel_t *)self->v.model)->maxs; 66 | G_SetSize(self, &self->v.mins, &self->v.maxs); 67 | 68 | self->v.solid = SOLID_BBOX; 69 | self->v.movetype = MOVETYPE_NONE; 70 | self->v.health = 20; 71 | self->v.th_die = explobox_explode; 72 | self->v.flags |= FL_TAKEDAMAGE | FL_AUTOAIM; 73 | 74 | self->v.origin.z += TO_FIX32(2); 75 | G_DropToFloor(self); 76 | } 77 | 78 | static void light_use(edict_t *self, edict_t *activator) { 79 | if (self->v.spawnflags & SF_LIGHT_START_OFF) { 80 | R_SetLightStyle(self->v.count, "m"); 81 | self->v.spawnflags &= ~SF_LIGHT_START_OFF; 82 | } else { 83 | R_SetLightStyle(self->v.count, "a"); 84 | self->v.spawnflags |= SF_LIGHT_START_OFF; 85 | } 86 | } 87 | 88 | void spawn_light(edict_t *self) { 89 | if (!self->v.targetname) { 90 | // inert light 91 | utl_remove_delayed(self); 92 | return; 93 | } 94 | 95 | // style 96 | if (self->v.count >= 32) { 97 | self->v.use = light_use; 98 | if (self->v.spawnflags & SF_LIGHT_START_OFF) 99 | R_SetLightStyle(self->v.count, "a"); 100 | else 101 | R_SetLightStyle(self->v.count, "m"); 102 | } 103 | } 104 | 105 | void spawn_light_fluoro(edict_t *self) { 106 | spawn_light(self); 107 | // TODO: hum? 108 | } 109 | 110 | void spawn_light_fluorospark(edict_t *self) { 111 | if (!self->v.count) 112 | self->v.count = 10; 113 | // TODO: buzz? 114 | } 115 | 116 | void spawn_light_globe(edict_t *self) { 117 | /* unused */ 118 | utl_remove_delayed(self); 119 | } 120 | 121 | void spawn_light_flame_large_yellow(edict_t *self) { 122 | // TODO 123 | utl_remove_delayed(self); 124 | } 125 | 126 | void spawn_light_flame_small_white(edict_t *self) { 127 | // TODO 128 | utl_remove_delayed(self); 129 | } 130 | 131 | void spawn_light_flame_small_yellow(edict_t *self) { 132 | // TODO 133 | utl_remove_delayed(self); 134 | } 135 | 136 | void spawn_light_torch_small_walltorch(edict_t *self) { 137 | // TODO 138 | utl_remove_delayed(self); 139 | } 140 | 141 | static void spikeshooter_use(edict_t *self, edict_t *activator) { 142 | if (self->v.spawnflags & SF_SHOOTER_LASER) { 143 | utl_sound(self, CHAN_VOICE, SFXID_ENFORCER_ENFIRE, SND_MAXVOL, ATTN_NORM); 144 | // TODO: utl_launch_laser 145 | } else { 146 | utl_sound(self, CHAN_VOICE, SFXID_WEAPONS_SPIKE2, SND_MAXVOL, ATTN_NORM); 147 | edict_t *spike = utl_launch_spike(self, &self->v.origin, &self->v.angles, &self->v.avelocity, 500); 148 | if (self->v.spawnflags & SF_SHOOTER_SUPERSPIKE) 149 | spike->v.dmg = 18; 150 | } 151 | } 152 | 153 | void spawn_trap_spikeshooter(edict_t *self) { 154 | self->v.movetype = MOVETYPE_NONE; 155 | self->v.use = spikeshooter_use; 156 | self->v.solid = SOLID_NOT; 157 | utl_set_movedir(self, &self->v.avelocity); 158 | } 159 | -------------------------------------------------------------------------------- /src/mathlib.c: -------------------------------------------------------------------------------- 1 | #include "mathlib.h" 2 | #include "types.h" 3 | #include "fixed.h" 4 | #include "vector.h" 5 | #include "model.h" 6 | 7 | x32vec3_t x32vec3_origin = { 0 }; 8 | x16vec3_t x16vec3_origin = { 0 }; 9 | 10 | // atan2 for an integer XY, returns in the same angle format as everything else (4096 = 360 degrees) 11 | // method spied in Soul Reaver (soul-re) 12 | x16 qatan2(s32 y, s32 x) { 13 | if (x == 0) 14 | x = 1; 15 | 16 | if (y == 0) 17 | return (x < 1) * TO_DEG16(180); 18 | 19 | const s32 ax = abs(x); 20 | const s32 ay = abs(y); 21 | 22 | if (x > 0) { 23 | if (y > 0) { 24 | if (ax < ay) 25 | return (1024 - ((ax * 512) / ay)); 26 | else 27 | return ((ay * 512) / ax); 28 | } else { 29 | if (ay < ax) 30 | return (4096 - ((ay * 512) / ax)); 31 | else 32 | return (((ax * 512) / ay) + 3072); 33 | } 34 | } 35 | 36 | if (y > 0) { 37 | if (ax < ay) 38 | return (((ax * 512) / ay) + 1024); 39 | else 40 | return (2048 - ((ay * 512) / ax)); 41 | } 42 | 43 | if (ay < ax) 44 | return (((ay * 512) / ax) + 2048); 45 | else 46 | return (3072 - ((ax * 512) / ay)); 47 | } 48 | 49 | void AngleVectors(const x16vec3_t *angles, x16vec3_t *forward, x16vec3_t *right, x16vec3_t *up) { 50 | register x16 angle; 51 | x32 sr, sp, sy, cr, cp, cy; 52 | 53 | angle = angles->d[YAW]; 54 | sy = csin(angle); 55 | cy = ccos(angle); 56 | angle = angles->d[PITCH]; 57 | sp = csin(angle); 58 | cp = ccos(angle); 59 | angle = angles->d[ROLL]; 60 | sr = csin(angle); 61 | cr = ccos(angle); 62 | 63 | forward->d[0] = XMUL16(cp, cy); 64 | forward->d[1] = XMUL16(cp, sy); 65 | forward->d[2] = -sp; 66 | right->d[0] = (XMUL16(XMUL16(-sr, sp), cy) + XMUL16(-cr, -sy)); 67 | right->d[1] = (XMUL16(XMUL16(-sr, sp), sy) + XMUL16(-cr, cy)); 68 | right->d[2] = XMUL16(-sr, cp); 69 | up->d[0] = (XMUL16(XMUL16(cr, sp), cy) + XMUL16(-sr, -sy)); 70 | up->d[1] = (XMUL16(XMUL16(cr, sp), sy) + XMUL16(-sr, cy)); 71 | up->d[2] = XMUL16(cr, cp); 72 | } 73 | 74 | int BoxOnPlaneSide(const x32vec3_t *emins, const x32vec3_t *emaxs, const mplane_t *p) { 75 | x32 dist1, dist2; 76 | x32vec3_t *v = PSX_SCRATCH; 77 | int sides; 78 | 79 | // fast axial cases 80 | if (p->type < 3) { 81 | if (p->dist <= emins->d[p->type]) 82 | return 1; 83 | if (p->dist >= emaxs->d[p->type]) 84 | return 2; 85 | return 3; 86 | } 87 | 88 | // general case 89 | switch (p->signbits) { 90 | case 0: 91 | v[0] = *emaxs; 92 | v[1] = *emins; 93 | break; 94 | case 1: 95 | v[0] = (x32vec3_t){{ emins->x, emaxs->y, emaxs->z }}; 96 | v[1] = (x32vec3_t){{ emaxs->x, emins->y, emins->z }}; 97 | break; 98 | case 2: 99 | v[0] = (x32vec3_t){{ emaxs->x, emins->y, emaxs->z }}; 100 | v[1] = (x32vec3_t){{ emins->x, emaxs->y, emins->z }}; 101 | break; 102 | case 3: 103 | v[0] = (x32vec3_t){{ emins->x, emins->y, emaxs->z }}; 104 | v[1] = (x32vec3_t){{ emaxs->x, emaxs->y, emins->z }}; 105 | break; 106 | case 4: 107 | v[0] = (x32vec3_t){{ emaxs->x, emaxs->y, emins->z }}; 108 | v[1] = (x32vec3_t){{ emins->x, emins->y, emaxs->z }}; 109 | break; 110 | case 5: 111 | v[0] = (x32vec3_t){{ emins->x, emaxs->y, emins->z }}; 112 | v[1] = (x32vec3_t){{ emaxs->x, emins->y, emaxs->z }}; 113 | break; 114 | case 6: 115 | v[0] = (x32vec3_t){{ emaxs->x, emins->y, emins->z }}; 116 | v[1] = (x32vec3_t){{ emins->x, emaxs->y, emaxs->z }}; 117 | break; 118 | case 7: 119 | v[0] = *emins; 120 | v[1] = *emaxs; 121 | break; 122 | default: 123 | // never happens 124 | v[0].x = v[0].y = v[0].z = 0; 125 | v[1].x = v[1].y = v[1].z = 0; 126 | break; 127 | } 128 | 129 | dist1 = XVecDotSL(&p->normal, &v[0]); 130 | dist2 = XVecDotSL(&p->normal, &v[1]); 131 | 132 | sides = 0; 133 | if (dist1 >= p->dist) 134 | sides = 1; 135 | if (dist2 < p->dist) 136 | sides |= 2; 137 | 138 | return sides; 139 | } 140 | 141 | x16 VecToYaw(const x32vec3_t *vec) { 142 | const s32 ix = vec->x >> FIXSHIFT; 143 | const s32 iy = vec->y >> FIXSHIFT; 144 | if (ix == 0 && iy == 0) 145 | return 0; 146 | return qatan2(iy, ix); 147 | } 148 | 149 | MATRIX *RotMatrixZY(SVECTOR *r, MATRIX *m) { 150 | short s[3], c[3]; 151 | MATRIX tm[2]; 152 | 153 | s[1] = isin(r->vy); s[2] = isin(r->vz); 154 | c[1] = icos(r->vy); c[2] = icos(r->vz); 155 | 156 | // mZ 157 | tm[1].m[0][0] = c[2]; tm[1].m[0][1] = -s[2]; tm[1].m[0][2] = 0; 158 | tm[1].m[1][0] = s[2]; tm[1].m[1][1] = c[2]; tm[1].m[1][2] = 0; 159 | tm[1].m[2][0] = 0; tm[1].m[2][1] = 0; tm[1].m[2][2] = ONE; 160 | 161 | // mY 162 | tm[0].m[0][0] = c[1]; tm[0].m[0][1] = 0; tm[0].m[0][2] = s[1]; 163 | tm[0].m[1][0] = 0; tm[0].m[1][1] = ONE; tm[0].m[1][2] = 0; 164 | tm[0].m[2][0] = -s[1]; tm[0].m[2][1] = 0; tm[0].m[2][2] = c[1]; 165 | 166 | MulMatrix0(&tm[1], &tm[0], m); 167 | 168 | return m; 169 | } 170 | -------------------------------------------------------------------------------- /src/model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "vector.h" 5 | #include "bspfile.h" 6 | #include "alias.h" 7 | 8 | // 0-2 are axial planes 9 | #define PLANE_X 0 10 | #define PLANE_Y 1 11 | #define PLANE_Z 2 12 | // 3-5 are non-axial planes snapped to the nearest 13 | #define PLANE_ANYX 3 14 | #define PLANE_ANYY 4 15 | #define PLANE_ANYZ 5 16 | 17 | #define SIDE_FRONT 0 18 | #define SIDE_BACK 1 19 | #define SIDE_ON 2 20 | 21 | #define CONTENTS_EMPTY -1 22 | #define CONTENTS_SOLID -2 23 | #define CONTENTS_WATER -3 24 | #define CONTENTS_SLIME -4 25 | #define CONTENTS_LAVA -5 26 | #define CONTENTS_SKY -6 27 | #define CONTENTS_ORIGIN -7 // removed at csg time 28 | #define CONTENTS_CLIP -8 // changed to contents_solid 29 | #define CONTENTS_CURRENT_0 -9 30 | #define CONTENTS_CURRENT_90 -10 31 | #define CONTENTS_CURRENT_180 -11 32 | #define CONTENTS_CURRENT_270 -12 33 | #define CONTENTS_CURRENT_UP -13 34 | #define CONTENTS_CURRENT_DOWN -14 35 | 36 | #define SURF_PLANEBACK 1 // 2 in og quake 37 | #define SURF_DRAWSKY 4 38 | #define SURF_DRAWSPRITE 8 39 | #define SURF_DRAWTURB 0x10 40 | #define SURF_DRAWTILED 0x20 41 | #define SURF_DRAWBACKGROUND 0x40 42 | #define SURF_UNDERWATER 0x80 43 | 44 | // in memory representations of bspfile.h structs 45 | 46 | /* ALIAS MODELS */ 47 | 48 | typedef aliashdr_t amodel_t; 49 | 50 | /* BSP MODELS */ 51 | 52 | typedef xbspvert_t mvert_t; 53 | 54 | typedef xbspclipnode_t mclipnode_t; 55 | 56 | typedef struct { 57 | x16vec3_t normal; 58 | u8 type; 59 | u8 signbits; // signx + signy<<1 + signz<<1 60 | x32 dist; 61 | } mplane_t; 62 | 63 | typedef struct msurface_s msurface_t; 64 | 65 | typedef struct mtexture_s { 66 | struct mtexture_s *anim_next; 67 | struct mtexture_s *anim_alt; 68 | s16 width; 69 | s16 height; 70 | u16 vram_page; 71 | u8 vram_u; 72 | u8 vram_v; 73 | s8 anim_total; 74 | s8 anim_min; 75 | s8 anim_max; 76 | u8 flags; 77 | } mtexture_t; 78 | 79 | struct msurface_s { 80 | mplane_t *plane; 81 | mtexture_t *texture; 82 | struct msurface_s *vischain; 83 | u32 visframe; 84 | u8 styles[MAX_MAP_LIGHTVALS]; 85 | u16 firstvert; 86 | u16 numverts; 87 | u8 backface; 88 | }; 89 | 90 | typedef struct mnode_s { 91 | s8 contents; 92 | u32 visframe; 93 | x32vec3_t mins; 94 | x32vec3_t maxs; 95 | struct mnode_s *parent; 96 | mplane_t *plane; 97 | struct mnode_s *children[2]; 98 | u16 firstsurf; 99 | u16 numsurf; 100 | } mnode_t; 101 | 102 | typedef struct mleaf_s { 103 | s8 contents; 104 | u32 visframe; 105 | x32vec3_t mins; 106 | x32vec3_t maxs; 107 | struct mnode_s *parent; 108 | u8 *compressed_vis; 109 | msurface_t **firstmarksurf; 110 | u16 nummarksurf; 111 | u8 lightmap[MAX_MAP_LIGHTVALS]; 112 | u8 styles[MAX_MAP_LIGHTVALS]; 113 | } mleaf_t; 114 | 115 | typedef struct { 116 | xbspclipnode_t *clipnodes; 117 | mplane_t *planes; 118 | u16 firstclipnode; 119 | u16 lastclipnode; 120 | x32vec3_t mins; 121 | x32vec3_t maxs; 122 | } hull_t; 123 | 124 | /* WHOLE MODEL */ 125 | 126 | typedef enum { mod_brush, mod_sprite, mod_alias } modtype_t; 127 | 128 | #define EF_ROCKET 1 // leave a trail 129 | #define EF_GRENADE 2 // leave a trail 130 | #define EF_GIB 4 // leave a trail 131 | #define EF_ROTATE 8 // rotate (bonus items) 132 | #define EF_TRACER 16 // green split trail 133 | #define EF_ZOMGIB 32 // small blood trail 134 | #define EF_TRACER2 64 // orange split trail + rotate 135 | #define EF_TRACER3 128 // purple trail 136 | 137 | // somewhat confusingly our brush model struct also contains alias models 138 | 139 | typedef struct bmodel_s { 140 | u8 type; 141 | u8 flags; 142 | s16 id; // <0 == brush models 143 | 144 | x32vec3_t mins, maxs; 145 | x32 radius; 146 | 147 | int firstmodelsurface; 148 | int nummodelsurfaces; 149 | 150 | int numsubmodels; 151 | xmodel_t *submodels; 152 | 153 | int numplanes; 154 | mplane_t *planes; 155 | 156 | int numleafs; // number of visible leafs, not counting 0 157 | mleaf_t *leafs; 158 | 159 | int numverts; 160 | mvert_t *verts; 161 | 162 | int numnodes; 163 | mnode_t *nodes; 164 | 165 | int numsurfaces; 166 | msurface_t *surfaces; 167 | 168 | int numclipnodes; 169 | xbspclipnode_t *clipnodes; 170 | 171 | int nummarksurfaces; 172 | msurface_t **marksurfaces; 173 | 174 | hull_t hulls[MAX_MAP_HULLS]; 175 | 176 | int numtextures; 177 | mtexture_t *textures; 178 | 179 | int nummapents; 180 | xbspent_t *mapents; 181 | 182 | int numamodels; 183 | amodel_t *amodels; 184 | 185 | int stringslen; 186 | char *strings; 187 | 188 | struct bmodel_s *bmodels; 189 | struct bmodel_s **bmodelptrs; 190 | 191 | u8 *visdata; 192 | } bmodel_t; 193 | 194 | bmodel_t *Mod_LoadXBSP(const char *name); 195 | mleaf_t *Mod_PointInLeaf(const x32vec3_t *p, bmodel_t *mod); 196 | const u8 *Mod_LeafPVS(const mleaf_t *leaf, const bmodel_t *model); 197 | -------------------------------------------------------------------------------- /src/render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | #include "entity.h" 9 | #include "model.h" 10 | #include "sprite.h" 11 | 12 | // some GTE macro variations that use registers instead of pointers 13 | #define gte_stotz_m(r0) __asm__ volatile( "mfc2 %0, $7;" : "=r"( r0 ) : ) 14 | #define gte_stmac0_m(r0) __asm__ volatile( "mfc2 %0, $24;" : "=r"( r0 ) : ) 15 | #define gte_stsxy0_m(r0) __asm__ volatile( "mfc2 %0, $12;" : "=r"( r0 ) : ) 16 | #define gte_stsz1_m(r0) __asm__ volatile( "mfc2 %0, $17;" : "=r"( r0 ) : ) 17 | #define gte_stsz_m(r0) __asm__ volatile( "mfc2 %0, $19;" : "=r"( r0 ) : ) 18 | #define gte_stopz_m(r0) __asm__ volatile( "mfc2 %0, $24;" : "=r"( r0 ) : ) 19 | #define gte_ldsxy1_m(r0) __asm__ volatile( "mtc2 %0, $13;" : : "r"( r0 ) ) 20 | #define gte_ldsz1_m(r0) __asm__ volatile( "mtc2 %0, $17;" : : "r"( r0 ) ) 21 | #define gte_ldsz2_m(r0) __asm__ volatile( "mtc2 %0, $18;" : : "r"( r0 ) ) 22 | #define gte_ldzsf3_m(r0) __asm__ volatile( "mtc2 %0, $29;" : : "r"( r0 ) ) 23 | #define gte_ldzsf4_m(r0) __asm__ volatile( "mtc2 %0, $30;" : : "r"( r0 ) ) 24 | 25 | #define gte_stsz01(r1,r2) { \ 26 | __asm__ volatile ("move $12,%0": :"r"(r1):"$12","$13","memory"); \ 27 | __asm__ volatile ("move $13,%0": :"r"(r2):"$12","$13","memory"); \ 28 | __asm__ volatile ("swc2 $17,($12)": : :"$12","$13","memory"); \ 29 | __asm__ volatile ("swc2 $18,($13)": : :"$12","$13","memory"); \ 30 | } 31 | 32 | #define BACKFACE_EPSILON 41 // F1.19.12 33 | #define GPU_BUFSIZE 0x30000 34 | #define GPU_OTDEPTH 2048 35 | 36 | #define GPU_SUBDIV_DIST_1 136 37 | #define GPU_SUBDIV_DIST_2 60 38 | 39 | #define VMODEL_SCALE 3 40 | 41 | #define MAX_PARTICLES 512 42 | #define MAX_BEAMS 4 43 | 44 | enum parttype_e { 45 | PT_STATIC, 46 | PT_GRAV, 47 | PT_FIRE, 48 | PT_EXPLODE, 49 | PT_EXPLODE2 50 | }; 51 | 52 | typedef struct { 53 | s16vec3_t org; 54 | u8 type; 55 | u8 color; 56 | s16vec3_t vel; 57 | u8 ramp; 58 | s8 die; 59 | } particle_t; 60 | 61 | typedef struct { 62 | s16vec3_t src; 63 | s16vec3_t dst; 64 | u32 color; 65 | s16 die; 66 | } beam_t; 67 | 68 | typedef struct render_state_s { 69 | particle_t particles[MAX_PARTICLES]; 70 | beam_t beams[MAX_BEAMS]; 71 | RECT clip; 72 | MATRIX matrix; 73 | MATRIX entmatrix; 74 | edict_t *cur_entity; 75 | mtexture_t *cur_texture; 76 | x32vec3_t origin; 77 | x32vec3_t modelorg; 78 | x32vec3_t vieworg; 79 | x16vec3_t vforward; 80 | x16vec3_t vright; 81 | x16vec3_t vup; 82 | x16vec3_t viewangles; 83 | mplane_t frustum[4]; 84 | mleaf_t *oldviewleaf; 85 | mleaf_t *viewleaf; 86 | msurface_t *vischain; 87 | x32 frametime; 88 | u32 frame; 89 | u32 visframe; 90 | u32 debug; 91 | s32 gamma; // 0 - 5 92 | u16 num_particles; 93 | u16 last_beam; 94 | u16 clut; 95 | } render_state_t; 96 | 97 | extern render_state_t rs; 98 | 99 | extern u16 r_lightstylevalue[MAX_LIGHTSTYLES + 1]; 100 | extern u32 r_palette[VID_NUM_COLORS]; // used for particles, etc 101 | 102 | extern int c_mark_leaves; 103 | extern int c_draw_polys; 104 | 105 | extern u32 *gpu_ot; 106 | extern u8 *gpu_buf; 107 | extern u8 *gpu_ptr; 108 | 109 | static inline void *R_AllocPrim(const u32 size) { 110 | void *ret = gpu_ptr; 111 | gpu_ptr += size; 112 | return ret; 113 | } 114 | 115 | void R_Init(void); 116 | void R_UploadCluts(const u16 *clut); 117 | void R_UploadTexture(const u8 *data, int x, int y, const int w, const int h); 118 | void R_ApplyGamma(void); 119 | qboolean R_CullBox(const x32vec3_t *mins, const x32vec3_t *maxs); 120 | void R_SetFrustum(void); 121 | void R_AddScreenPrim(const u32 primsize); 122 | void R_RenderScene(void); 123 | void R_DrawWorld(void); 124 | void R_NewMap(void); 125 | void R_DrawAliasModel(const amodel_t *model, const int frame, const int skin, const u32 tint); 126 | void R_DrawAliasViewModel(const amodel_t *model, const int frame, const u32 tint); 127 | void R_DrawBrushModel(bmodel_t *model); 128 | void R_DrawBBox(edict_t *ent); 129 | void R_DrawVisChain(void); 130 | void R_DrawBlitSync(const pic_t *pic, int x, const int y); 131 | void R_DrawParticles(void); 132 | void R_DrawBeams(void); 133 | void R_RenderView(void); 134 | void R_Flip(void); 135 | 136 | const mtexture_t *R_TextureAnimation(const mtexture_t *base); 137 | 138 | void R_InitLightStyles(void); 139 | void R_UpdateLightStyles(const x32 time); 140 | void R_SetLightStyle(const int i, const char *map); 141 | void R_LightEntity(edict_t *ent); 142 | 143 | void R_SpawnParticleEffect(const u8 type, const x32vec3_t *org, const x32vec3_t *vel, const u8 color, s16 count); 144 | void R_SpawnParticleExplosion(const x32vec3_t *org); 145 | void R_SpawnParticleLavaSplash(const x32vec3_t *org); 146 | void R_SpawnParticleTeleport(const x32vec3_t *org); 147 | void R_SpawnParticleTrail(const x32vec3_t *org, const x32vec3_t *oldorg, const u8 type); 148 | void R_SpawnBeam(const x32vec3_t *src, const x32vec3_t *dst, const u32 rgb24, const s16 frames, s16 index); 149 | 150 | void R_UpdateParticles(void); 151 | -------------------------------------------------------------------------------- /tools/common/psxbsp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "psxtypes.h" 4 | #include "psxmdl.h" 5 | 6 | #define PSXBSPVERSION 0x1D585350 7 | 8 | #define MAX_XMAP_HULLS 4 9 | #define MAX_XMAP_VERTS 65535 10 | #define MAX_XMAP_PLANES 32767 11 | #define MAX_XMAP_TEXTURES 255 12 | #define MAX_XMAP_NODES 32767 13 | #define MAX_XMAP_CLIPNODES 32767 14 | #define MAX_XMAP_LEAFS 8192 15 | #define MAX_XMAP_FACES 32767 16 | #define MAX_XMAP_MODELS 256 17 | #define MAX_XMAP_MARKSURF 65535 18 | #define MAX_XMAP_ENTITIES 512 19 | #define MAX_XMAP_ENTMODELS 128 20 | #define MAX_XMAP_VISIBILITY 0x40000 21 | #define MAX_XMAP_LIGHTVALS 2 22 | #define MAX_XMAP_STRINGS 8192 23 | 24 | #define MAX_ENTITIES 1024 25 | #define MAX_SOUNDS 255 26 | #define MAX_LIGHTSTYLES 64 27 | 28 | #define MAX_TEX_WIDTH 64 29 | #define MAX_TEX_HEIGHT 64 30 | 31 | #define VRAM_XSTART 320 32 | #define VRAM_NUM_PAGES 2 33 | #define VRAM_PAGE_WIDTH (1024 - 64 - VRAM_XSTART) 34 | #define VRAM_PAGE_HEIGHT 256 35 | #define VRAM_TEXPAGE_WIDTH 64 36 | #define VRAM_TEXPAGE_HEIGHT 256 37 | #define VRAM_TOTAL_WIDTH (VRAM_PAGE_WIDTH) 38 | #define VRAM_TOTAL_HEIGHT (VRAM_PAGE_HEIGHT * VRAM_NUM_PAGES) 39 | 40 | #define SPURAM_BASE 0x1100 41 | #define SPURAM_SIZE (0x80000 - SPURAM_BASE) 42 | 43 | #define NUM_CLUT_COLORS 256 44 | #define NUM_GAMMA_LEVELS 6 45 | 46 | #pragma pack(push, 1) 47 | 48 | /* XBSP STRUCTURE 49 | * xbsphdr_t ver="PSX\x1D" 50 | * xbsplump_t XLMP_TEXDATA 51 | * xbsplump_t XLMP_SNDDATA 52 | * xbsplump_t XLMP_MDLDATA 53 | * xbsplump_t XLMP_VERTS 54 | * xbsplump_t XLMP_PLANES 55 | * xbsplump_t XLMP_TEXINFO 56 | * xbsplump_t XLMP_FACES 57 | * xbsplump_t XLMP_MARKSURF 58 | * xbsplump_t XLMP_VISILIST 59 | * xbsplump_t XLMP_LEAFS 60 | * xbsplump_t XLMP_NODES 61 | * xbsplump_t XLMP_CLIPNODES 62 | * xbsplump_t XLMP_MODELS 63 | * xbsplump_t XLMP_STRINGS 64 | * xbsplump_t XLMP_ENTITIES 65 | */ 66 | 67 | enum xbsplump_e { 68 | XLMP_INVALID = -1, 69 | 70 | XLMP_TEXDATA, 71 | XLMP_SNDDATA, 72 | XLMP_MDLDATA, 73 | 74 | XLMP_VERTS, 75 | XLMP_PLANES, 76 | XLMP_TEXINFO, 77 | XLMP_FACES, 78 | XLMP_MARKSURF, 79 | XLMP_VISILIST, 80 | XLMP_LEAFS, 81 | XLMP_NODES, 82 | XLMP_CLIPNODES, 83 | XLMP_MODELS, 84 | XLMP_STRINGS, 85 | XLMP_ENTITIES, 86 | 87 | XLMP_COUNT 88 | }; 89 | 90 | typedef struct { 91 | s16vec3_t pos; // positions are always integer 92 | u8vec2_t tex; // 0-255 or 0-size 93 | u8 col[MAX_XMAP_LIGHTVALS]; // light values for every used lightstyle 94 | } xvert_t; 95 | 96 | typedef struct { 97 | s32 type; 98 | s32 size; 99 | u8 data[]; 100 | } xlump_t; 101 | 102 | typedef struct { 103 | u32 ver; 104 | } xbsphdr_t; 105 | 106 | typedef struct { 107 | x16vec3_t normal; 108 | x32 dist; 109 | s32 type; 110 | } xplane_t; 111 | 112 | typedef struct { 113 | u16 planenum; 114 | s16 children[2]; // negative numbers are -(leafs+1), not nodes 115 | s16vec3_t mins; // for sphere culling 116 | s16vec3_t maxs; 117 | u16 firstface; 118 | u16 numfaces; // counting both sides 119 | } xnode_t; 120 | 121 | typedef struct { 122 | s16 planenum; 123 | s16 children[2]; // negative numbers are contents 124 | } xclipnode_t; 125 | 126 | typedef struct { 127 | u8vec2_t uv; 128 | s16vec2_t size; 129 | u16 tpage; 130 | u8 flags; 131 | s8 anim_total; 132 | s8 anim_min; 133 | s8 anim_max; 134 | s8 anim_next; 135 | s8 anim_alt; 136 | } xtexinfo_t; 137 | 138 | #define XTEX_SPECIAL 1 139 | #define XTEX_LIQUID 2 140 | #define XTEX_SKY 4 141 | #define XTEX_INVISIBLE 8 142 | #define XTEX_ANIMATED 16 143 | #define XTEX_LARGE 32 144 | #define XTEX_NULL 0x80 145 | 146 | typedef struct { 147 | s16 planenum; 148 | s16 side; 149 | s32 firstvert; 150 | s16 numverts; 151 | s16 texinfo; 152 | u8 styles[MAX_XMAP_LIGHTVALS]; 153 | } xface_t; 154 | 155 | typedef struct { 156 | s16 contents; 157 | s32 visofs; // -1 = no visibility info 158 | s16vec3_t mins; // for frustum culling 159 | s16vec3_t maxs; 160 | u16 firstmarksurface; 161 | u16 nummarksurfaces; 162 | u8 lightmap[MAX_XMAP_LIGHTVALS]; 163 | u8 styles[MAX_XMAP_LIGHTVALS]; 164 | } xleaf_t; 165 | 166 | typedef struct { 167 | s16vec3_t mins; 168 | s16vec3_t maxs; 169 | s16vec3_t origin; 170 | s16 headnode[MAX_XMAP_HULLS]; 171 | s16 visleafs; 172 | u16 firstface; 173 | u16 numfaces; 174 | } xmodel_t; 175 | 176 | typedef struct { 177 | u8 classname; 178 | s8 noise; 179 | u16 spawnflags; 180 | s16 model; // negative = brush models, positive = alias models 181 | s16 health; 182 | s16 dmg; 183 | s16 speed; 184 | s16 count; 185 | s16 height; 186 | u16 target; 187 | u16 killtarget; 188 | u16 targetname; 189 | u16 string; // offset into string lump 190 | x32 wait; 191 | x32 delay; 192 | x16vec3_t angles; 193 | x32vec3_t origin; 194 | } xmapent_t; 195 | 196 | typedef struct { 197 | s16 soundid; 198 | u16 frames; 199 | u32 spuaddr; 200 | } xmapsnd_t; 201 | 202 | typedef struct { 203 | u32 nummdls; 204 | xaliashdr_t mdls[]; 205 | } xmdllump_t; 206 | 207 | typedef struct { 208 | u32 numsfx; 209 | xmapsnd_t sfx[]; 210 | } xsndlump_t; 211 | 212 | typedef struct { 213 | u16 tpage; 214 | u8vec2_t uv; 215 | u8vec2_t size; 216 | } xpic_t; 217 | 218 | #pragma pack(pop) 219 | -------------------------------------------------------------------------------- /src/progs/sbar.c: -------------------------------------------------------------------------------- 1 | #include "prcommon.h" 2 | 3 | #define ICON_W 24 4 | #define ICON_H 24 5 | #define KEY_W 16 6 | #define KEY_H 16 7 | #define RUNE_W 8 8 | #define RUNE_H 8 9 | 10 | #define PAD_SIZE 4 11 | #define LEFT_X (PAD_SIZE) 12 | #define RIGHT_X (VID_WIDTH - PAD_SIZE) 13 | #define ROW_HEIGHT (PAD_SIZE + ICON_H) 14 | #define BOTTOM_ROW_Y (VID_HEIGHT - 1 * ROW_HEIGHT) 15 | #define TOP_ROW_Y (VID_HEIGHT - 2 * ROW_HEIGHT) 16 | #define POWER_SIZE 16 17 | #define POWER_ROW_Y (TOP_ROW_Y - PAD_SIZE - POWER_SIZE) 18 | 19 | static const pic_t *pic_faces = NULL; 20 | static const pic_t *pic_faces_pain = NULL; 21 | static const pic_t *pic_faces_power = NULL; 22 | static const pic_t *pic_armor = NULL; 23 | static const pic_t *pic_ammo = NULL; 24 | static const pic_t *pic_keys = NULL; 25 | static const pic_t *pic_runes = NULL; 26 | static const pic_t *pic_powers = NULL; 27 | 28 | static x32 face_pain_time = 0; 29 | 30 | u32 sbar_xhair_color = C_WHITE; 31 | s32 sbar_xhair = true; 32 | 33 | static inline void DrawCrosshair(void) { 34 | // just draw a little dotted + with 4 rects for now 35 | // top and bottom 36 | Scr_DrawRect(VID_CENTER_X - 1, VID_CENTER_Y - 2, 2, 1, sbar_xhair_color, true); 37 | Scr_DrawRect(VID_CENTER_X - 1, VID_CENTER_Y + 1, 2, 1, sbar_xhair_color, true); 38 | // left and right 39 | Scr_DrawRect(VID_CENTER_X - 2, VID_CENTER_Y - 1, 1, 2, sbar_xhair_color, true); 40 | Scr_DrawRect(VID_CENTER_X + 1, VID_CENTER_Y - 1, 1, 2, sbar_xhair_color, true); 41 | } 42 | 43 | static inline const pic_t *PickFace(const s16 health, const u32 items) { 44 | const pic_t *pic; 45 | 46 | if ((items & (IT_INVISIBILITY | IT_INVULNERABILITY)) == (IT_INVISIBILITY | IT_INVULNERABILITY)) { 47 | // special face both invulnerability and invisiblity 48 | pic = pic_faces_power + 3; 49 | } else if (items & IT_QUAD) { 50 | pic = pic_faces_power + 0; 51 | } else if (items & IT_INVISIBILITY) { 52 | pic = pic_faces_power + 1; 53 | } else if (items & IT_INVULNERABILITY) { 54 | pic = pic_faces_power + 2; 55 | } else { 56 | const s16 index = (health >= PLAYER_HEALTH) ? 4 : (health / (PLAYER_HEALTH / 4)); 57 | pic = ((face_pain_time > rs.frametime) ? pic_faces_pain : pic_faces) + index; 58 | } 59 | 60 | return pic; 61 | } 62 | 63 | static inline void DrawCounter(s16 x, s16 y, const pic_t *pic, const s16 val, const s16 redval, const qboolean ralign) { 64 | const u32 color = val <= redval ? C_RED : C_WHITE; 65 | if (ralign) { 66 | Scr_DrawPic(x, y, C_WHITE, pic); x -= FNT_BIG_W * 3 + 2; 67 | Scr_DrawDigits(x, y,color, VA("%3d", val)); 68 | } else { 69 | Scr_DrawPic(x, y, C_WHITE, pic); x += ICON_W + 2; 70 | Scr_DrawDigits(x, y,color, VA("%d", val)); 71 | } 72 | } 73 | 74 | void Sbar_Init(void) { 75 | pic_faces = Spr_GetPic(PICID_FACE5); 76 | pic_faces_pain = Spr_GetPic(PICID_FACE_P5); 77 | pic_faces_power = Spr_GetPic(PICID_FACE_QUAD); 78 | pic_armor = Spr_GetPic(PICID_SB_ARMOR1); 79 | pic_ammo = Spr_GetPic(PICID_SB_SHELLS); 80 | pic_keys = Spr_GetPic(PICID_SB_KEY1); 81 | pic_runes = Spr_GetPic(PICID_SB_SIGIL1); 82 | pic_powers = Spr_GetPic(PICID_SBA1_INVIS); 83 | } 84 | 85 | void Sbar_DrawIntermission(const player_state_t *p) { 86 | const s32 min = (pr.completion_time >> FIXSHIFT) / 60; 87 | const s32 sec = (pr.completion_time >> FIXSHIFT) % 60; 88 | 89 | s16 y = VID_CENTER_Y - 24; 90 | Scr_DrawTextOffset(128, VID_CENTER_X - 40, y, C_WHITE, "\x10""COMPLETED""\x11"); y += 10; 91 | Scr_DrawText(VID_CENTER_X - 52, y, C_WHITE, VA("Time: %02d:%02d", min, sec)); y += 8; 92 | Scr_DrawText(VID_CENTER_X - 52, y, C_WHITE, VA("Secrets: %d/%d", pr.found_secrets, pr.total_secrets)); y += 8; 93 | Scr_DrawText(VID_CENTER_X - 52, y, C_WHITE, VA("Kills: %d/%d", pr.killed_monsters, pr.total_monsters)); y += 8; 94 | } 95 | 96 | void Sbar_Draw(const player_state_t *p) { 97 | const pic_t *pic; 98 | 99 | if (pr.intermission_state > 0) { 100 | if (pr.intermission_state == 1) { 101 | // draw level completion screen 102 | Sbar_DrawIntermission(p); 103 | } 104 | return; 105 | } 106 | 107 | if (p->ent->v.health < 0) 108 | return; // we're fucking dead 109 | 110 | // bottom row 111 | DrawCounter(LEFT_X, BOTTOM_ROW_Y, PickFace(p->ent->v.health, p->stats.items), p->ent->v.health, 25, false); 112 | if (p->stats.weaponnum && p->stats.weaponnum != IT_AXE) { 113 | pic = pic_ammo + p->stats.ammonum; 114 | DrawCounter(RIGHT_X - ICON_W, BOTTOM_ROW_Y, pic, p->stats.ammo[p->stats.ammonum], 10, true); 115 | } 116 | 117 | // top row 118 | if (p->stats.items & IT_ARMOR3) 119 | pic = pic_armor + 2; 120 | else if (p->stats.items & IT_ARMOR2) 121 | pic = pic_armor + 1; 122 | else 123 | pic = pic_armor + 0; 124 | DrawCounter(LEFT_X, TOP_ROW_Y, pic, p->stats.armor, 25, false); 125 | 126 | // keys and shit 127 | s16 x = RIGHT_X - KEY_W; 128 | if (p->stats.items & IT_KEY1) { 129 | Scr_DrawPic(x, BOTTOM_ROW_Y - PAD_SIZE - KEY_H, C_WHITE, pic_keys + 0); x-= KEY_W; 130 | } 131 | if (p->stats.items & IT_KEY2) { 132 | Scr_DrawPic(x, BOTTOM_ROW_Y - PAD_SIZE - KEY_H, C_WHITE, pic_keys + 1); x -= KEY_W; 133 | } 134 | 135 | // powerups 136 | s16 y = POWER_ROW_Y; 137 | for (int i = POWER_INVIS; i <= POWER_QUAD; ++i) { 138 | if (p->power_time[i] > gs.time) { 139 | Scr_DrawPic(LEFT_X, y, C_WHITE, pic_powers + i - 1); 140 | Scr_DrawTextOffset(128, LEFT_X + POWER_SIZE + 2, y + 4, C_WHITE, VA("%d", (p->power_time[i] - gs.time) >> FIXSHIFT)); 141 | y -= POWER_SIZE; 142 | } 143 | } 144 | 145 | if (sbar_xhair) 146 | DrawCrosshair(); 147 | } 148 | 149 | void Sbar_IndicateDamage(const s16 damage) { 150 | face_pain_time = rs.frametime + HALF; 151 | Scr_SetBlend(C_RED, SCR_FLASH_TIME); 152 | } 153 | -------------------------------------------------------------------------------- /src/progs/prcommon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "prcommon.h" 3 | 4 | pr_globals_t pr; 5 | 6 | void null_think(edict_t *self) { 7 | 8 | } 9 | 10 | void null_touch(edict_t *self, edict_t *other) { 11 | 12 | } 13 | 14 | const trace_t *utl_traceline(const x32vec3_t *v1, const x32vec3_t *v2, const qboolean nomonsters, edict_t *ent) { 15 | pr.trace = G_Move(v1, &x32vec3_origin, &x32vec3_origin, v2, nomonsters, ent); 16 | return pr.trace; 17 | } 18 | 19 | void utl_makevectors(const x16vec3_t *angles) { 20 | AngleVectors(angles, &pr.v_forward, &pr.v_right, &pr.v_up); 21 | } 22 | 23 | void utl_vectoangles(const x16vec3_t *dir) { 24 | x16 yaw, pitch; 25 | x32 forward; 26 | 27 | if (dir->x == 0 && dir->y == 0) { 28 | yaw = 0; 29 | if (dir->z > 0) 30 | pitch = TO_DEG16(90); 31 | else 32 | pitch = TO_DEG16(270); 33 | } else { 34 | yaw = qatan2(dir->y, dir->x); 35 | forward = SquareRoot12(XMUL16(dir->x, dir->x) + XMUL16(dir->y, dir->y)); 36 | pitch = ONE - qatan2(dir->z, forward); 37 | } 38 | 39 | pr.v_angles.x = pitch; 40 | pr.v_angles.y = yaw; 41 | pr.v_angles.z = 0; 42 | } 43 | 44 | void utl_remove(edict_t *self) { 45 | ED_Free(self); 46 | } 47 | 48 | void utl_remove_delayed(edict_t *self) { 49 | self->v.think = utl_remove; 50 | self->v.nextthink = gs.time + 1; 51 | } 52 | 53 | void utl_set_movedir(edict_t *self, x16vec3_t *movedir) { 54 | if (self->v.angles.x == 0 && self->v.angles.z == 0) { 55 | if (self->v.angles.y == -1) { 56 | // up 57 | movedir->x = 0; 58 | movedir->y = 0; 59 | movedir->z = ONE; 60 | return; 61 | } else if (self->v.angles.y == -2) { 62 | // down 63 | movedir->x = 0; 64 | movedir->y = 0; 65 | movedir->z = -ONE; 66 | return; 67 | } 68 | } 69 | 70 | utl_makevectors(&self->v.angles); 71 | *movedir = pr.v_forward; 72 | } 73 | 74 | static void calc_move_done(edict_t *self) { 75 | self->v.origin = self->v.door->dest; 76 | XVecZero(&self->v.velocity); 77 | G_LinkEdict(self, false); 78 | 79 | self->v.nextthink = -1; 80 | if (self->v.door->reached) 81 | self->v.door->reached(self); 82 | } 83 | 84 | void utl_calc_move(edict_t *self, const x32vec3_t *tdest, const s16 tspeed, think_fn_t func) { 85 | self->v.door->reached = func; 86 | self->v.door->dest = *tdest; 87 | self->v.think = calc_move_done; 88 | 89 | if (tdest->x == self->v.origin.x && tdest->y == self->v.origin.y && tdest->z == self->v.origin.z) { 90 | XVecZero(&self->v.velocity); 91 | self->v.nextthink = self->v.ltime + PR_FRAMETIME; 92 | return; 93 | } 94 | 95 | // divide delta length by speed to get time to reach dest 96 | x32vec3_t vdestdelta; 97 | XVecSub(tdest, &self->v.origin, &vdestdelta); 98 | const s32 len = XVecLengthIntL(&vdestdelta); 99 | const s32 traveltime = xdiv32(len, tspeed); 100 | if (traveltime < PR_FRAMETIME) { 101 | XVecZero(&self->v.velocity); 102 | self->v.nextthink = self->v.ltime + PR_FRAMETIME; 103 | return; 104 | } 105 | 106 | // set nextthink to trigger a think when dest is reached 107 | self->v.nextthink = self->v.ltime + traveltime; 108 | 109 | // scale the destdelta vector by the time spent traveling to get velocity 110 | const x32 recip = xdiv32(ONE, traveltime); 111 | self->v.velocity.x = xmul32(recip, vdestdelta.x); 112 | self->v.velocity.y = xmul32(recip, vdestdelta.y); 113 | self->v.velocity.z = xmul32(recip, vdestdelta.z); 114 | } 115 | 116 | static void delay_think(edict_t *self) { 117 | utl_usetargets(self, self->v.extra_ptr); 118 | utl_remove(self); 119 | } 120 | 121 | void utl_usetargets(edict_t *self, edict_t *activator) { 122 | // check for a delay 123 | if (self->v.delay) { 124 | // create a temp object to fire at a later time 125 | edict_t *t = ED_Alloc(); 126 | t->v.classname = ENT_DELAYED_USE; 127 | t->v.nextthink = gs.time + self->v.delay; 128 | t->v.think = delay_think; 129 | t->v.owner = self; 130 | t->v.target = self->v.target; 131 | t->v.killtarget = self->v.killtarget; 132 | t->v.extra_trigger.string = self->v.extra_trigger.string; 133 | t->v.extra_ptr = activator; 134 | return; 135 | } 136 | 137 | // print the string 138 | const u16 msg = self->v.extra_trigger.string; 139 | if (activator->v.classname == ENT_PLAYER && (msg || self->v.classname == ENT_TRIGGER_SECRET)) { 140 | Scr_SetCenterMsg(msg ? (gs.worldmodel->strings + msg) : "You found a secret area!"); 141 | if (!self->v.noise) 142 | utl_sound(activator, CHAN_VOICE, SFXID_MISC_TALK, SND_MAXVOL, ATTN_NORM); 143 | } 144 | 145 | // kill the killtargets 146 | if (self->v.killtarget) { 147 | edict_t *t = G_FindByTargetname(gs.edicts, self->v.killtarget); 148 | while (t != gs.edicts) { 149 | utl_remove(t); 150 | t = G_FindByTargetname(t, self->v.killtarget); 151 | } 152 | } 153 | 154 | // fire targets 155 | if (self->v.target) { 156 | edict_t *t = G_FindByTargetname(gs.edicts, self->v.target); 157 | while (t != gs.edicts) { 158 | if (t->v.use) 159 | t->v.use(t, activator); 160 | t = G_FindByTargetname(t, self->v.target); 161 | } 162 | } 163 | } 164 | 165 | void utl_sound(edict_t *self, const s16 chan, const s16 sndid, const u8 vol, const x32 attn) { 166 | x32vec3_t org; 167 | org.x = self->v.origin.x + ((self->v.mins.x + self->v.maxs.x) >> 1); 168 | org.y = self->v.origin.y + ((self->v.mins.y + self->v.maxs.y) >> 1); 169 | org.z = self->v.origin.z + ((self->v.mins.z + self->v.maxs.z) >> 1); 170 | Snd_StartSoundId(EDICT_NUM(self), chan, sndid, &org, vol, attn); 171 | } 172 | 173 | // called on map change 174 | void Progs_NewMap(void) { 175 | memset(&pr, 0, sizeof(pr)); 176 | 177 | // HACK: same way as this is done in the og progs 178 | if (strstr(g_map, "E1M8")) 179 | gs.gravity = 100; 180 | } 181 | -------------------------------------------------------------------------------- /src/vector_a.s: -------------------------------------------------------------------------------- 1 | .set noreorder 2 | .set noat 3 | 4 | .include "gtereg.inc" 5 | .include "inline_s.inc" 6 | 7 | .set C2_D1, $0 8 | .set C2_D2, $2 9 | .set C2_D3, $4 10 | 11 | .section .text 12 | 13 | # calculates square of length of F1.19.12 vector pointed to by a0, in F1.19.12 14 | .global XVecLengthSqrL 15 | .type XVecLengthSqrL, @function 16 | XVecLengthSqrL: 17 | # a0 - pointer to input vector 18 | # v0 - return 19 | lwc2 C2_IR1, 0($a0) 20 | lwc2 C2_IR2, 4($a0) 21 | lwc2 C2_IR3, 8($a0) 22 | nSQR(1) 23 | mfc2 $t0, C2_MAC1 24 | mfc2 $t1, C2_MAC2 25 | mfc2 $t2, C2_MAC3 26 | move $v0, $t0 27 | addu $v0, $t1 28 | jr $ra 29 | addu $v0, $t2 30 | 31 | # calculates square of length of F1.19.12 2D vector pointed to by a0, in F1.19.12 32 | .global XVec2LengthSqrL 33 | .type XVec2LengthSqrL, @function 34 | XVec2LengthSqrL: 35 | # a0 - pointer to input vector 36 | # v0 - return 37 | lwc2 C2_IR1, 0($a0) 38 | lwc2 C2_IR2, 4($a0) 39 | ctc2 $0, C2_IR3 40 | nSQR(1) 41 | mfc2 $t0, C2_MAC1 42 | mfc2 $t1, C2_MAC2 43 | move $v0, $0 44 | addu $v0, $t0 45 | jr $ra 46 | addu $v0, $t1 47 | 48 | # takes a F1.19.12 vector, converts it to a F1.31.0 vector and returns the square of its length 49 | .global XVecLengthSqrIntL 50 | .type XVecLengthSqrIntL, @function 51 | XVecLengthSqrIntL: 52 | # a0 - pointer to input vector 53 | # v0 - return 54 | lw $t0, 0($a0) 55 | lw $t1, 4($a0) 56 | sra $t0, 12 57 | lw $t2, 8($a0) 58 | sra $t1, 12 59 | mtc2 $t0, C2_IR1 60 | sra $t2, 12 61 | mtc2 $t1, C2_IR2 62 | mtc2 $t2, C2_IR3 63 | nSQR(0) 64 | mfc2 $t0, C2_MAC1 65 | mfc2 $t1, C2_MAC2 66 | move $v0, $t0 67 | mfc2 $t2, C2_MAC3 68 | addu $v0, $t1 69 | jr $ra 70 | addu $v0, $t2 71 | 72 | # calculates cross product of two x16vec3s pointed to by a0 and a1 and puts it in a2 73 | .global XVecCrossSS 74 | .type XVecCrossSS, @function 75 | XVecCrossSS: 76 | # a0 - pointer to x16vec3_t 77 | # a1 - pointer to x16vec3_t 78 | # a2 - pointer to output x16vec3_t 79 | lh $t0, 0($a0) 80 | lh $t1, 2($a0) 81 | ctc2 $t0, C2_D1 82 | lh $t2, 4($a0) 83 | ctc2 $t1, C2_D2 84 | ctc2 $t2, C2_D3 85 | lh $t3, 0($a1) 86 | lh $t4, 2($a1) 87 | ctc2 $t3, C2_IR1 88 | lh $t5, 4($a1) 89 | ctc2 $t4, C2_IR2 90 | ctc2 $t5, C2_IR3 91 | nop 92 | nop 93 | cop2 0x0178000C 94 | mfc2 $t0, C2_IR1 95 | mfc2 $t1, C2_IR2 96 | mfc2 $t2, C2_IR3 97 | sh $t0, 0($a2) 98 | sh $t1, 2($a2) 99 | jr $ra 100 | sh $t2, 4($a2) 101 | 102 | .global XVecNormLS 103 | .type XVecNormLS, @function 104 | XVecNormLS: 105 | lw $t0, 0($a0) 106 | lw $t1, 4($a0) 107 | srl $t0, 12 108 | lw $t2, 8($a0) 109 | srl $t1, 12 110 | mtc2 $t0, C2_IR1 111 | srl $t2, 12 112 | mtc2 $t1, C2_IR2 113 | mtc2 $t2, C2_IR3 114 | 115 | nSQR(0) 116 | 117 | mfc2 $t3, C2_MAC1 118 | mfc2 $t4, C2_MAC2 119 | mfc2 $t5, C2_MAC3 120 | 121 | add $t3, $t4 122 | add $v0, $t3, $t5 123 | mtc2 $v0, C2_LZCS 124 | sw $t3, 0($a2) 125 | nop 126 | mfc2 $v1, C2_LZCR 127 | 128 | addiu $at, $0 , -2 129 | and $v1, $at 130 | 131 | addiu $t6, $0 , 0x1f 132 | sub $t6, $v1 133 | sra $t6, 1 134 | addiu $t3, $v1, -24 135 | 136 | bltz $t3, .Lvalue_neg 137 | nop 138 | b .Lvalue_pos 139 | sllv $t4, $v0, $t3 140 | .Lvalue_neg: 141 | addiu $t3, $0 , 24 142 | sub $t3, $v1 143 | srav $t4, $v0, $t3 144 | .Lvalue_pos: 145 | addi $t4, -64 146 | sll $t4, 1 147 | 148 | la $t5, _norm_table 149 | addu $t5, $t4 150 | lh $t5, 0($t5) 151 | 152 | mtc2 $t0, C2_IR1 153 | mtc2 $t1, C2_IR2 154 | mtc2 $t2, C2_IR3 155 | mtc2 $t5, C2_IR0 156 | 157 | nGPF(0) 158 | 159 | mfc2 $t0, C2_MAC1 160 | mfc2 $t1, C2_MAC2 161 | mfc2 $t2, C2_MAC3 162 | 163 | sra $t0, $t6 164 | sra $t1, $t6 165 | sra $t2, $t6 166 | 167 | sh $t0, 0($a1) 168 | sh $t1, 2($a1) 169 | jr $ra 170 | sh $t2, 4($a1) 171 | 172 | .type _norm_table, @object 173 | _norm_table: 174 | .hword 0x1000, 0x0fe0, 0x0fc1, 0x0fa3, 0x0f85, 0x0f68, 0x0f4c, 0x0f30 175 | .hword 0x0f15, 0x0efb, 0x0ee1, 0x0ec7, 0x0eae, 0x0e96, 0x0e7e, 0x0e66 176 | .hword 0x0e4f, 0x0e38, 0x0e22, 0x0e0c, 0x0df7, 0x0de2, 0x0dcd, 0x0db9 177 | .hword 0x0da5, 0x0d91, 0x0d7e, 0x0d6b, 0x0d58, 0x0d45, 0x0d33, 0x0d21 178 | .hword 0x0d10, 0x0cff, 0x0cee, 0x0cdd, 0x0ccc, 0x0cbc, 0x0cac, 0x0c9c 179 | .hword 0x0c8d, 0x0c7d, 0x0c6e, 0x0c5f, 0x0c51, 0x0c42, 0x0c34, 0x0c26 180 | .hword 0x0c18, 0x0c0a, 0x0bfd, 0x0bef, 0x0be2, 0x0bd5, 0x0bc8, 0x0bbb 181 | .hword 0x0baf, 0x0ba2, 0x0b96, 0x0b8a, 0x0b7e, 0x0b72, 0x0b67, 0x0b5b 182 | .hword 0x0b50, 0x0b45, 0x0b39, 0x0b2e, 0x0b24, 0x0b19, 0x0b0e, 0x0b04 183 | .hword 0x0af9, 0x0aef, 0x0ae5, 0x0adb, 0x0ad1, 0x0ac7, 0x0abd, 0x0ab4 184 | .hword 0x0aaa, 0x0aa1, 0x0a97, 0x0a8e, 0x0a85, 0x0a7c, 0x0a73, 0x0a6a 185 | .hword 0x0a61, 0x0a59, 0x0a50, 0x0a47, 0x0a3f, 0x0a37, 0x0a2e, 0x0a26 186 | .hword 0x0a1e, 0x0a16, 0x0a0e, 0x0a06, 0x09fe, 0x09f6, 0x09ef, 0x09e7 187 | .hword 0x09e0, 0x09d8, 0x09d1, 0x09c9, 0x09c2, 0x09bb, 0x09b4, 0x09ad 188 | .hword 0x09a5, 0x099e, 0x0998, 0x0991, 0x098a, 0x0983, 0x097c, 0x0976 189 | .hword 0x096f, 0x0969, 0x0962, 0x095c, 0x0955, 0x094f, 0x0949, 0x0943 190 | .hword 0x093c, 0x0936, 0x0930, 0x092a, 0x0924, 0x091e, 0x0918, 0x0912 191 | .hword 0x090d, 0x0907, 0x0901, 0x08fb, 0x08f6, 0x08f0, 0x08eb, 0x08e5 192 | .hword 0x08e0, 0x08da, 0x08d5, 0x08cf, 0x08ca, 0x08c5, 0x08bf, 0x08ba 193 | .hword 0x08b5, 0x08b0, 0x08ab, 0x08a6, 0x08a1, 0x089c, 0x0897, 0x0892 194 | .hword 0x088d, 0x0888, 0x0883, 0x087e, 0x087a, 0x0875, 0x0870, 0x086b 195 | .hword 0x0867, 0x0862, 0x085e, 0x0859, 0x0855, 0x0850, 0x084c, 0x0847 196 | .hword 0x0843, 0x083e, 0x083a, 0x0836, 0x0831, 0x082d, 0x0829, 0x0824 197 | .hword 0x0820, 0x081c, 0x0818, 0x0814, 0x0810, 0x080c, 0x0808, 0x0804 198 | -------------------------------------------------------------------------------- /tools/bspconvpsx/qent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "util.h" 7 | #include "qent.h" 8 | #include "qmdl.h" 9 | #include "qsfx.h" 10 | 11 | qentmap_t qentmap[MAX_ENT_CLASSES]; 12 | int num_qentmap = 0; 13 | 14 | qent_t qents[MAX_ENTITIES]; 15 | int num_qents = 0; 16 | 17 | qentmap_t *qentmap_find(const char *classname) { 18 | for (int i = 0; i < num_qentmap; ++i) { 19 | if (!strcmp(qentmap[i].classname, classname)) 20 | return &qentmap[i]; 21 | } 22 | return NULL; 23 | } 24 | 25 | int qentmap_init(const char *mapfile) { 26 | num_qentmap = resmap_parse(mapfile, (char *)qentmap, MAX_ENT_CLASSES, sizeof(qentmap[0]), MAX_ENT_CLASSNAME); 27 | for (int i = 0; i < num_qentmap; ++i) { 28 | qentmap[i].classnum = i; 29 | } 30 | printf("qentmap_init(): indexed %d entclasses from %s\n", num_qentmap, mapfile); 31 | return num_qentmap; 32 | } 33 | 34 | int qentmap_link(const char *resfile) { 35 | FILE *f = fopen(resfile, "rb"); 36 | if (!f) { 37 | fprintf(stderr, "qentmap_link(): can't open reslist file %s\n", resfile); 38 | return -1; 39 | } 40 | 41 | char line[MAX_TOKEN] = { 0 }; 42 | char classname[MAX_ENT_CLASSNAME] = { 0 }; 43 | qentmap_t *entclass = NULL; 44 | int id; 45 | int ret = 0; 46 | while (fgets(line, sizeof(line), f)) { 47 | char *key = strtok(line, " \t\r\n"); 48 | if (!key || key[0] == '#') 49 | continue; 50 | 51 | char *val = strtok(NULL, " \t\r\n"); 52 | if (!key || !val || !key[0] || !val[0]) 53 | continue; 54 | 55 | if (!strcmp(key, "ent")) { 56 | strncpy(classname, val, sizeof(classname) - 1); 57 | entclass = qentmap_find(classname); 58 | if (!entclass) { 59 | fprintf(stderr, "classname '%s' is not in entmap\n", classname); 60 | ret = -2; 61 | break; 62 | } 63 | } else { 64 | if (!entclass) { 65 | fprintf(stderr, "encountered '%s %s' before any 'ent' directives\n", key, val); 66 | ret = -3; 67 | break; 68 | } 69 | if (!strcmp(key, "mdl")) { 70 | assert(entclass->num_mdlnums < MAX_ENT_MDLS); 71 | const int idx = entclass->num_mdlnums++; 72 | for (int i = 0; i < MAX_ALIASES; ++i) { 73 | id = qmdlmap_id_for_name(val); 74 | if (id <= 0) { 75 | fprintf(stderr, "model '%s' not in mdlmap\n", val); 76 | break; 77 | } 78 | entclass->mdlnums[idx][i] = id; 79 | val = strtok(NULL, " \t\r\n"); 80 | if (!val || !val[0]) 81 | break; 82 | } 83 | } else if (!strcmp(key, "sfx")) { 84 | assert(entclass->num_sfxnums < MAX_ENT_SFX); 85 | const int idx = entclass->num_sfxnums++; 86 | for (int i = 0; i < MAX_ALIASES; ++i) { 87 | id = qsfxmap_id_for_name(val); 88 | if (id <= 0) { 89 | fprintf(stderr, "sound '%s' not in sfxmap\n", val); 90 | break; 91 | } 92 | entclass->sfxnums[idx][i] = id; 93 | val = strtok(NULL, " \t\r\n"); 94 | if (!val || !val[0]) 95 | break; 96 | } 97 | } 98 | } 99 | } 100 | 101 | fclose(f); 102 | return ret; 103 | } 104 | 105 | const char *qent_parse(qent_t *ent, const char *data) { 106 | char key[MAX_TOKEN] = { 0 }; 107 | char value[MAX_TOKEN] = { 0 }; 108 | 109 | ent->numfields = 0; 110 | 111 | while (1) { 112 | // parse key 113 | data = com_parse(data, key); 114 | if (key[0] == '}') 115 | break; 116 | 117 | assert(data); 118 | 119 | // parse value 120 | data = com_parse(data, value); 121 | assert(value[0] != '}'); 122 | assert(data); 123 | 124 | // save keyvalue 125 | assert(ent->numfields < MAX_ENT_FIELDS); 126 | const int keylen = strlen(key); 127 | const int valuelen = strlen(value); 128 | qentfield_t *field = calloc(1, sizeof(qentfield_t) + keylen + valuelen + 2); 129 | assert(field); 130 | field->keylen = keylen; 131 | field->valuelen = valuelen; 132 | field->key = field->strdata; 133 | field->value = field->strdata + keylen + 1; 134 | memcpy(field->key, key, keylen); 135 | memcpy(field->value, value, valuelen); 136 | ent->fields[ent->numfields++] = field; 137 | 138 | // save pointer to classname if that's what it is 139 | if (!strcmp(key, "classname")) 140 | ent->classname = field->value; 141 | } 142 | 143 | assert(ent->classname); 144 | 145 | ent->info = qentmap_find(ent->classname); 146 | 147 | assert(ent->info); 148 | 149 | return data; 150 | } 151 | 152 | void qent_load(const char *data, int datalen) { 153 | char token[MAX_TOKEN] = { 0 }; 154 | const char *end = data + datalen; 155 | qent_t *ent = NULL; 156 | 157 | while (1) { 158 | // parse opening bracket 159 | data = com_parse(data, token); 160 | if (!data || data >= end) 161 | break; 162 | assert(token[0] == '{'); 163 | 164 | // parse entity block 165 | assert(num_qents < MAX_ENTITIES); 166 | ent = &qents[num_qents++]; 167 | data = qent_parse(ent, data); 168 | } 169 | } 170 | 171 | const char *qent_get_string(qent_t *ent, const char *key) { 172 | for (int i = 0; i < ent->numfields; ++i) { 173 | if (!strcmp(ent->fields[i]->key, key)) 174 | return ent->fields[i]->value; 175 | } 176 | return NULL; 177 | } 178 | 179 | const char *qent_get_int(qent_t *ent, const char *key, int *out) { 180 | const char *value = qent_get_string(ent, key); 181 | if (!value) return NULL; 182 | *out = atoi(value); 183 | return value; 184 | } 185 | 186 | const char *qent_get_float(qent_t *ent, const char *key, float *out) { 187 | const char *value = qent_get_string(ent, key); 188 | if (!value) return NULL; 189 | *out = atof(value); 190 | return value; 191 | } 192 | 193 | const char *qent_get_vector(qent_t *ent, const char *key, qvec3_t out) { 194 | const char *value = qent_get_string(ent, key); 195 | if (!value) return NULL; 196 | sscanf(value, "%f %f %f", &out[0], &out[1], &out[2]); 197 | return value; 198 | } 199 | -------------------------------------------------------------------------------- /src/sound.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "system.h" 5 | #include "spu.h" 6 | #include "sound.h" 7 | 8 | static const sfx_t *snd_sfx = NULL; 9 | static int snd_num_sfx = 0; 10 | static int snd_num_statics = 0; 11 | static x32vec3_t *listener_origin = NULL; 12 | static x16vec3_t *listener_right = NULL; 13 | 14 | static struct sndchan { 15 | const sfx_t *sfx; 16 | s16 looping; 17 | s16 entchannel; 18 | s16 entnum; 19 | s16 vol; // 0 - 255 20 | x16 attn; 21 | s32 endframe; 22 | x32vec3_t origin; 23 | } snd_chan[SND_NUM_CH]; 24 | 25 | s32 snd_volume = 128; 26 | 27 | void Snd_Init(void) { 28 | Sys_Printf("Snd_Init()\n"); 29 | SPU_Init(); 30 | } 31 | 32 | void Snd_NewMap(void) { 33 | memset(snd_chan, 0, sizeof(snd_chan)); 34 | listener_origin = NULL; 35 | listener_right = NULL; 36 | snd_num_statics = 0; 37 | SPU_ClearAllVoices(); 38 | } 39 | 40 | void Snd_SetBank(const sfx_t *sfx, const int num_sfx, const u8 *spudata, const u32 spudata_size) { 41 | snd_sfx = sfx; 42 | snd_num_sfx = num_sfx; 43 | 44 | SPU_StartUpload(SPU_RAM_BASE, spudata, spudata_size); 45 | SPU_WaitForTransfer(); 46 | 47 | Sys_Printf("new sound bank loaded: %d sfx, %u bytes\n", num_sfx, spudata_size); 48 | } 49 | 50 | void Snd_ResetBank(void) { 51 | snd_sfx = NULL; 52 | snd_num_sfx = 0; 53 | } 54 | 55 | static struct sndchan *Snd_PickChannel(const s16 entnum, const s16 entch) { 56 | s16 ch_idx = 0; 57 | s16 first_to_die = -1; 58 | s32 life_left = 0x7fffffff; 59 | const s32 frame_now = Sys_Frames(); 60 | 61 | for (ch_idx = SND_CH_DYNAMIC0; ch_idx < SND_NUM_CH; ++ch_idx) { 62 | if (entch && snd_chan[ch_idx].entnum == entnum && (entch == -1 || snd_chan[ch_idx].entchannel == entch)) { 63 | // always override sound from same entity 64 | first_to_die = ch_idx; 65 | break; 66 | } 67 | 68 | // don't let monster sounds override player sounds 69 | if (snd_chan[ch_idx].entnum == 1 && entnum != 1 && snd_chan[ch_idx].sfx) 70 | continue; 71 | 72 | const s32 dt = snd_chan[ch_idx].endframe - frame_now; 73 | if (dt < life_left) { 74 | life_left = dt; 75 | first_to_die = ch_idx; 76 | } 77 | } 78 | 79 | if (first_to_die == -1) 80 | return NULL; 81 | 82 | if (snd_chan[first_to_die].sfx) 83 | snd_chan[first_to_die].sfx = NULL; 84 | 85 | return &snd_chan[first_to_die]; 86 | } 87 | 88 | static qboolean Snd_Spatialize(struct sndchan* ch) { 89 | const s16 voice = ch - snd_chan; 90 | s32 lvol = 0; 91 | s32 rvol = 0; 92 | 93 | if (snd_volume) { 94 | if (ch->entnum == 1) { 95 | // anything coming from the view entity will always be full volume 96 | lvol = ch->vol; 97 | rvol = ch->vol; 98 | } else { 99 | x32vec3_t source_vec; 100 | x16vec3_t dir_vec; 101 | x32 dist = 0; 102 | XVecSub(&ch->origin, listener_origin, &source_vec); 103 | XVecNormLS(&source_vec, &dir_vec, &dist); // this returns squared distance 104 | // if out of clip distance, don't bother 105 | if (dist < SND_CLIPDIST * SND_CLIPDIST) { 106 | dist = ONE - SquareRoot0(dist) * ch->attn; 107 | const x16 dot = XVecDotSS(listener_right, &dir_vec); 108 | const x32 rscale = xmul32(dist, ONE + dot); 109 | const x32 lscale = xmul32(dist, ONE - dot); 110 | rvol = (rscale <= 0) ? 0 : xmul32(rscale, ch->vol); 111 | if (rvol > SND_MAXVOL) rvol = SND_MAXVOL; 112 | lvol = (lscale <= 0) ? 0 : xmul32(lscale, ch->vol); 113 | if (lvol > SND_MAXVOL) lvol = SND_MAXVOL; 114 | } 115 | } 116 | rvol = ((rvol * snd_volume) >> 7) << 6; 117 | lvol = ((lvol * snd_volume) >> 7) << 6; 118 | } 119 | 120 | SPU_SetVoiceVolume(voice, lvol, rvol); 121 | 122 | return (rvol || lvol); 123 | } 124 | 125 | const sfx_t *Snd_FindSound(const s16 id) { 126 | if (snd_sfx) { 127 | for (s32 i = 0; i < snd_num_sfx; ++i) { 128 | if (snd_sfx[i].id == id) 129 | return &snd_sfx[i]; 130 | } 131 | } 132 | 133 | Sys_Printf("Snd_FindSound: sound %02x not found\n", id); 134 | 135 | return NULL; 136 | } 137 | 138 | void Snd_StartSound(const s16 entnum, const s16 entch, const sfx_t *sfx, const x32vec3_t *origin, s16 vol, x32 attn) { 139 | if (!sfx) 140 | return; 141 | 142 | // pick a channel to play on 143 | struct sndchan *target_chan = Snd_PickChannel(entnum, entch); 144 | if (!target_chan) 145 | return; 146 | 147 | // spatialize 148 | target_chan->sfx = NULL; 149 | target_chan->endframe = 0; 150 | target_chan->origin = *origin; 151 | target_chan->entnum = entnum; 152 | target_chan->entchannel = entch; 153 | target_chan->vol = vol; 154 | target_chan->attn = XMUL16(attn, SND_INV_CLIPDIST); 155 | if (!Snd_Spatialize(target_chan)) 156 | return; // sound is inaudible 157 | 158 | target_chan->sfx = sfx; 159 | target_chan->endframe = Sys_Frames() + sfx->frames; 160 | 161 | const s16 voice = target_chan - snd_chan; 162 | SPU_PlaySample(voice, sfx->spuaddr, SND_FREQ); 163 | } 164 | 165 | void Snd_StaticSound(const sfx_t *sfx, const x32vec3_t *origin, s16 vol, x32 attn) { 166 | if (!sfx) 167 | return; 168 | 169 | if (snd_num_statics == SND_NUM_CH_AMBIENT) { 170 | Sys_Printf("Snd_StaticSound: too many statics (max %d)\n", SND_NUM_CH_AMBIENT); 171 | return; 172 | } 173 | 174 | struct sndchan* ch = &snd_chan[SND_CH_AMBIENT0 + snd_num_statics++]; 175 | ch->sfx = sfx; 176 | ch->origin = *origin; 177 | ch->vol = vol; 178 | ch->attn = XMUL16(attn, SND_INV_CLIPDIST); 179 | ch->endframe = Sys_Frames() + sfx->frames; 180 | 181 | Snd_Spatialize(ch); 182 | 183 | const s16 voice = ch - snd_chan; 184 | SPU_PlaySample(voice, sfx->spuaddr, SND_FREQ); 185 | } 186 | 187 | void Snd_Update(x32vec3_t *lorigin, x16vec3_t *lright) { 188 | listener_origin = lorigin; 189 | listener_right = lright; 190 | 191 | struct sndchan *ch = &snd_chan[SND_CH_AMBIENT0]; 192 | for (int i = SND_CH_AMBIENT0; i < SND_NUM_CH; ++i, ++ch) { 193 | if (!ch->sfx) 194 | continue; 195 | if (!Snd_Spatialize(ch)) 196 | continue; 197 | } 198 | } 199 | 200 | void Snd_SetVolume(const s32 vol) { 201 | snd_volume = vol; 202 | // recalculate volumes 203 | Snd_Update(listener_origin, listener_right); 204 | } 205 | -------------------------------------------------------------------------------- /tools/scripts/redux/profiler.lua: -------------------------------------------------------------------------------- 1 | -- Dumb profiler script for PCSX-Redux. Relies on the game calling 2 | -- certain PCSX exec slots whenever a frame begins or ends or 3 | -- a function is entered or left. See quakepsx/src/profile.c for an example. 4 | -- Don't forget to feed your ELF into Redux for the symbols to appear. 5 | 6 | -- MIT License 7 | -- 8 | -- Copyright (c) 2025 fgsfds 9 | -- 10 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 11 | -- of this software and associated documentation files (the "Software"), to deal 12 | -- in the Software without restriction, including without limitation the rights 13 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | -- copies of the Software, and to permit persons to whom the Software is 15 | -- furnished to do so, subject to the following conditions: 16 | -- 17 | -- The above copyright notice and this permission notice shall be included in all 18 | -- copies or substantial portions of the Software. 19 | -- 20 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | -- SOFTWARE. 27 | 28 | local bit = require("bit") 29 | 30 | -- tree type -- 31 | 32 | local Tree = {} 33 | Tree.__index = Tree 34 | 35 | -- make a new tree node 36 | function Tree:new(count, value, parent) 37 | local tree = setmetatable({ }, Tree) 38 | tree.count = count -- number of children this node has 39 | tree.value = value -- data held in this node 40 | tree.parent = parent -- parent node for this node 41 | tree.child = { } -- array containing this node's children 42 | return tree 43 | end 44 | 45 | -- add a new child node to self 46 | function Tree:addChild(value) 47 | self.count = self.count + 1 48 | self.child[self.count] = Tree:new(0, value, self) 49 | return self.child[self.count] 50 | end 51 | 52 | -- profiler internals -- 53 | 54 | gCallTree = nil 55 | gCallNode = nil 56 | 57 | gProfShowStats = false 58 | gProfAutoPause = true 59 | 60 | function addrToName(addr) 61 | if addr == 0 then 62 | return "This frame" 63 | end 64 | for k, v in PCSX.iterateSymbols() do 65 | if k == addr then 66 | return v 67 | end 68 | end 69 | return string.format("0x%08x", addr) 70 | end 71 | 72 | function onReset(hard) 73 | gCallTree = nil 74 | gCallNode = nil 75 | end 76 | 77 | function onPause(exception) 78 | gProfShowStats = true 79 | end 80 | 81 | function onRun() 82 | gProfShowStats = false 83 | end 84 | 85 | gEvReset = PCSX.Events.createEventListener("ExecutionFlow::Reset", onReset) 86 | gEvPause = PCSX.Events.createEventListener("ExecutionFlow::Pause", onPause) 87 | gEvRun = PCSX.Events.createEventListener("ExecutionFlow::Run", onRun) 88 | 89 | -- Prf_EndFrame, called at the end of a frame in the game's main loop 90 | PCSX.execSlots[252] = function() 91 | if gProfAutoPause then 92 | PCSX.pauseEmulator() 93 | end 94 | 95 | if gCallTree then 96 | gCallTree.value.timeTotal = 0 97 | for k, v in ipairs(gCallTree.child) do 98 | gCallTree.value.timeTotal = gCallTree.value.timeTotal + v.value.timeTotal 99 | end 100 | end 101 | end 102 | 103 | -- Prf_StartFrame, called at the start of a frame in the game's main loop 104 | PCSX.execSlots[253] = function() 105 | -- call node for main function (or top scope or wherever you called this from) 106 | gCallTree = Tree:new(0, { 107 | addr = 0, 108 | timeStart = 0, 109 | timeTotal = 0, 110 | calls = 1 111 | }) 112 | gCallNode = gCallTree 113 | end 114 | 115 | -- __cyg_profile_func_enter, called by gcc instrumentation whenever a function is entered 116 | PCSX.execSlots[254] = function() 117 | local enterTime = tonumber(PCSX.getCPUCycles()) 118 | local regs = PCSX.getRegisters().GPR.n 119 | local thisFunc = regs.a0 120 | local callSite = regs.a1 121 | 122 | if not gCallTree then 123 | return 124 | end 125 | 126 | local frame = { 127 | addr = thisFunc, 128 | timeStart = enterTime, 129 | timeTotal = 0, 130 | calls = 1 131 | } 132 | 133 | if not gCallNode then 134 | -- top node 135 | gCallNode = gCallTree 136 | else 137 | -- check if the current node already has a child for this call 138 | local found = nil 139 | for k, v in ipairs(gCallNode.child) do 140 | if v.value.addr == thisFunc then 141 | found = v 142 | break 143 | end 144 | end 145 | if found then 146 | -- yes: use this node and increment its call count 147 | found.value.timeStart = enterTime 148 | found.value.calls = found.value.calls + 1 149 | gCallNode = found 150 | else 151 | -- no: add a new child call 152 | gCallNode = gCallNode:addChild(frame) 153 | end 154 | end 155 | end 156 | 157 | -- __cyg_profile_func_exit, called by gcc instrumentation whenever a function is exited 158 | PCSX.execSlots[255] = function() 159 | local exitTime = tonumber(PCSX.getCPUCycles()) 160 | local regs = PCSX.getRegisters().GPR.n 161 | local thisFunc = regs.a0 162 | local callSite = regs.a1 163 | 164 | if not gCallTree then 165 | return 166 | end 167 | 168 | assert(gCallNode) 169 | assert(gCallNode.parent) 170 | assert(gCallNode.value.addr == thisFunc) 171 | 172 | gCallNode.value.timeTotal = gCallNode.value.timeTotal + (exitTime - gCallNode.value.timeStart) 173 | gCallNode = gCallNode.parent 174 | end 175 | 176 | -- visualization -- 177 | 178 | function nodeCmp(na, nb) 179 | return na.value.timeTotal > nb.value.timeTotal 180 | end 181 | 182 | function funcNode(node) 183 | local name = addrToName(node.value.addr) 184 | local text = string.format( 185 | "%s | calls: %d | time: %d", 186 | name, node.value.calls, node.value.timeTotal 187 | ) 188 | 189 | if imgui.TreeNode(text) then 190 | table.sort(node.child, nodeCmp) 191 | for k, v in ipairs(node.child) do 192 | funcNode(v) 193 | end 194 | imgui.TreePop() 195 | end 196 | end 197 | 198 | function profilerWindow() 199 | if imgui.Button("Resume") then 200 | PCSX.resumeEmulator() 201 | end 202 | 203 | imgui.SameLine() 204 | 205 | local changed 206 | changed, gProfAutoPause = imgui.Checkbox("Pause on EndFrame", gProfAutoPause) 207 | 208 | if not gProfShowStats then 209 | return 210 | end 211 | 212 | if not gCallTree then 213 | return 214 | end 215 | 216 | funcNode(gCallTree) 217 | end 218 | 219 | function DrawImguiFrame() 220 | imgui.safe.Begin("Profiler", profilerWindow); 221 | end 222 | --------------------------------------------------------------------------------