commit 8bdf6bf2d2daf0f38f26d06aa183b0694fcb93f0 Author: Quinn <99677023+thepigeongenerator@users.noreply.github.com> Date: Mon Dec 2 10:37:52 2024 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2575ab0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +build diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0258d4f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + "version": "2.0.0", + "configurations": [ + { + "name": "(gdb) C Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/linux-86_64/${workspaceFolderBasename}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "miDebuggerPath": "/usr/bin/gdb", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "build linux86_64" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..01d44c5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "tasks": [ + { + "type": "shell", + "label": "build linux86_64", + "command": "./build.sh", + "args": [ + "linux86_64" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "builds the program for linux x86_64" + }, + { + "type": "shell", + "label": "build win86_64", + "command": "./build.sh", + "args": [ + "win86_64" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "detail": "builds the program for windows x86_64" + }, + { + "type": "shell", + "label": "build web", + "command": "./build.sh", + "args": [ + "web" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "detail": "builds the program for web using emscripten" + } + ], + "version": "2.0.0" +} diff --git a/assets/uwu b/assets/uwu new file mode 100644 index 0000000..e108e50 --- /dev/null +++ b/assets/uwu @@ -0,0 +1 @@ +uwu diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..756d52f --- /dev/null +++ b/build.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# define variables +PROJECT_NAME=${PWD##*/} +BUILD_DIR="./build" +INCLUDE_IN_BUILD="./assets" + +# compilation targets +args_linux86_64() +{ + ARCHITECTURE="linux-86_64" + COMPILER="/bin/gcc" + ARGS="-lSDL2" + FILE_EXSTENSION="" +} + +args_win86-64() +{ + ARCHITECTURE="win-86_64" + COMPILER="/usr/bin/x86_64-w64-mingw32-gcc" + ARGS="-lmingw32 -lSDL2main -lSDL2 -mwindows" + INCLUDE_IN_BUILD="$INCLUDE_IN_BUILD /usr/x86_64-w64-mingw32/bin/SDL2.dll" + FILE_EXSTENSION=".exe" +} + +args_emscripten() +{ + ARCHITECTURE="web" + COMPILER="emcc" # just make sure it's in $PATH (it's a pain to install) + ARGS="-s USE_SDL=2" + FILE_EXSTENSION=".html" +} + +# handle arguments +if [ "$1" = "linux86_64" ]; then args_linux86_64 +elif [ "$1" = "win86_64" ]; then args_win86-64 +elif [ "$1" = "web" ]; then args_emscripten +else echo -e "\033[91mdidn't include any arguments! D:\033[0m" && exit 1 +fi + +# check whether $BUILD_DIR or $ARCHITECTURE isn't set +if [[ -z $BUILD_DIR ]] || [[ -z "$ARCHITECTURE" ]]; then + echo -e "\033[91mBUILD_DIR or ARCHITECTURE not set D:\033[0m" + exit 1 +fi + + +# make (and clear) the build directory +mkdir -p "$BUILD_DIR/$ARCHITECTURE" +rm -rf "${BUILD_DIR:?}/$ARCHITECTURE/*" + +# copy included files or directories to the build directory +if [[ -n $INCLUDE_IN_BUILD ]]; then + cp -r "$INCLUDE_IN_BUILD" "$BUILD_DIR/$ARCHITECTURE" +fi + +# get the executable path +EXE_PATH=$BUILD_DIR/$ARCHITECTURE/$PROJECT_NAME$FILE_EXSTENSION +echo "building at: $EXE_PATH" + +# check whether the compiler can actually be executed +if [ ! -x "$COMPILER" ] && ! command -v "$COMPILER" > /dev/null; then + echo -e "\033[91mCouldn't find an executable at path: \033[0m $COMPILER" + exit 1 +fi + +# compile the code +COMMAND="$COMPILER $(find ./src -name "*.c") -o $EXE_PATH -Wall -g -lm $ARGS" +echo "using command: $COMMAND" +$COMMAND +exit $? diff --git a/src/errors.c b/src/errors.c new file mode 100644 index 0000000..fa7d860 --- /dev/null +++ b/src/errors.c @@ -0,0 +1,32 @@ +#include "errors.h" + +#include +#include +#include + +#define MAX_STR_LEN 128 + +void error(const ErrorCode error_code, const char* format, ...) { + char buffer[MAX_STR_LEN] = {0}; // contains the buffer of the final string + + va_list args = {0}; + va_start(args, format); + vsnprintf(buffer, MAX_STR_LEN, format, args); + va_end(args); + + printf("\033[91mE\033[0m: %s\n", buffer); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "something went wrong! :O", buffer, NULL); + + exit(error_code); +} + +void warn(const char* format, ...) { + char buffer[MAX_STR_LEN] = {0}; // contains the buffer of the final string + + va_list args = {0}; + va_start(args, format); + vsnprintf(buffer, MAX_STR_LEN, format, args); + va_end(args); + + printf("\033[93mW\033[0m: %s\n", buffer); +} diff --git a/src/errors.h b/src/errors.h new file mode 100644 index 0000000..07f1613 --- /dev/null +++ b/src/errors.h @@ -0,0 +1,25 @@ +#pragma once + +typedef unsigned char ErrorCode; +enum { + ERROR_MISC = -1, + + SUCCESS = 0, + ERROR_INIT = 1, + + // SDL errors + ERROR_SDL = 2, + ERROR_SDL_INIT = ERROR_SDL | ERROR_INIT, + + // renderer errors + ERROR_SDL_RENDERER = ERROR_SDL | 4, + ERROR_SDL_RENDERER_INIT = ERROR_SDL_RENDERER | ERROR_INIT, + + // audio errors + ERROR_SDL_AUDIO = ERROR_SDL | 8, + ERROR_SDL_AUDIO_INIT = ERROR_SDL_AUDIO | ERROR_INIT, +}; + +// call when a fatal error has occurred, the program will immediately terminate when called +void error(const ErrorCode error_code, const char* format, ...); +void warn(const char* format, ...); diff --git a/src/game/game.c b/src/game/game.c new file mode 100644 index 0000000..7770ffd --- /dev/null +++ b/src/game/game.c @@ -0,0 +1,11 @@ +#include "game.h" + +#include "../main.h" + + +// called every time the game's state is updated +void game_update(GameData game_data, const Uint8* keys) { + if (keys[SDL_SCANCODE_ESCAPE]) { + stop(); + } +} diff --git a/src/game/game.h b/src/game/game.h new file mode 100644 index 0000000..0689c8b --- /dev/null +++ b/src/game/game.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// stores the data used in the game +typedef struct { +} GameData; + +// updates the game's state +void game_update(GameData game_data, const Uint8* keys); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..6262a5e --- /dev/null +++ b/src/main.c @@ -0,0 +1,78 @@ +#include "main.h" + +#include +#include +#include +#include + +#include "errors.h" +#include "game/game.h" +#include "window/audio.h" +#include "window/renderer.h" + +#ifdef __EMSCRIPTEN__ // for web builds +#include +#endif + + +bool playing = true; + +SDL_Window* window = NULL; +SDL_Renderer* renderer = NULL; + +// handles game application initialisation +static void init(void) { + // initialize SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + error(ERROR_SDL_INIT, "SDL could not initialize! SDL Error: %s", SDL_GetError()); + return; + } + + // initialize the renderer + if (renderer_init(&window, &renderer) < 0) { + error(ERROR_SDL_RENDERER_INIT, SDL_GetError()); + return; + } + + // initialize audio + AudioDevice* audio_device = audio_device_init(32000, AUDIO_S16, 1, 4096); + //AudioData audio1 = audio_load_wav(audio_device, "FILE MANE"); +} + +// handles game application updating +static void update(void) { + // update the input + { + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_QUIT: + exit(SUCCESS); + break; + } + } + } + + // preform updates + game_update((GameData){}, SDL_GetKeyboardState(NULL)); + renderer_update(&(RenderData){ + window, renderer}); +} + +// handles game application quitting +void stop(void) { + playing = false; +} + +// entry point of the application +int main(int argc, char** argv) { + init(); + + while (playing) + update(); + + // cleanup of resources + SDL_Quit(); + + return 0; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..1369892 --- /dev/null +++ b/src/main.h @@ -0,0 +1,4 @@ +#pragma once + +// stops execution of the game +void stop(void); diff --git a/src/window/audio.c b/src/window/audio.c new file mode 100644 index 0000000..9791118 --- /dev/null +++ b/src/window/audio.c @@ -0,0 +1,112 @@ +#include "audio.h" + +#include + +#include "../errors.h" + +// the maximum amount of sounds that can play at once +#define MAX_SOUNDS 64 + +typedef struct { + AudioData* playing_audio; + AudioDevice audio_device; +} AudioCallbackData; + +// audio callback from SDL_AudioSpec; called when the audio device needs more data +static void audio_mixer(void* userdata, Uint8* stream, int len) { + memset(stream, 0, len); // clear the playing audio + AudioDevice* device = userdata; // get the callback data + AudioData* audio = device->playing_audio; + + for (int i = 0; i < MAX_SOUNDS; i++) { + // skip if the audio doesn't conain any further data + if (audio[i].length <= 0) { + continue; + } + + // get the length of which we shall be mixing + Uint32 mix_length = SDL_min(audio[i].length, len); + + // mix the audio with the stream + SDL_MixAudioFormat(stream, audio[i].buffer, device->format, mix_length, SDL_MIX_MAXVOLUME); + audio[i].buffer += mix_length; // move the pointer up a a bit + audio[i].length -= mix_length; // move up the mixed amount + } +} + +// converts the audio to the format of the audio device +static void convert_audio(const AudioDevice* audio_device, const SDL_AudioSpec wav_spec, Uint8** wav_buffer, Uint32* wav_length) { + // build the audio converter with the audio given + SDL_AudioCVT cvt = {0}; + SDL_BuildAudioCVT(&cvt, wav_spec.format, wav_spec.channels, wav_spec.freq, audio_device->format, audio_device->channels, audio_device->freq); + + cvt.len = (*wav_length) * wav_spec.channels; // the buffer length + cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult); // allocate size for the new buffer + memcpy(cvt.buf, *wav_buffer, *wav_length); // copy wav data to cvt buffer; + + // convert + SDL_ConvertAudio(&cvt); + + // output + *wav_length = cvt.len_cvt; // set the length to the new length + memcpy(*wav_buffer, cvt.buf, cvt.len_cvt); // copy converted cvt buffer back to wav buffer + + free(cvt.buf); // free the memory allocated to the cvt buffer +} + +// loads a WAV file and returns the relevant information +AudioData audio_load_wav(const AudioDevice* audio_device, const char* file_path) { + SDL_AudioSpec wav_spec = {0}; + AudioData audio = {0}; + + SDL_LoadWAV(file_path, &wav_spec, &audio.buffer, &audio.length); + convert_audio(audio_device, wav_spec, &audio.buffer, &audio.length); + audio.mixed_amount = audio.length; + + return audio; +} + +// initializes the audio device +AudioDevice* audio_device_init(const int freq, const SDL_AudioFormat format, const Uint8 channels, const Uint16 samples) { + // allocate memory for the audio device + AudioDevice* audio_device = malloc(sizeof(AudioDevice)); + + // define the audio specification + SDL_AudioSpec spec = {freq, format, channels, samples}; + spec.callback = audio_mixer; + spec.userdata = audio_device; + + // create the audio device + *audio_device = (AudioDevice){ + SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0), + freq, + format, + channels, + calloc(MAX_SOUNDS, sizeof(AudioData)), // allocate memory on the heap for the playing audio array + }; + + // if the audio device isn't set, cause an error + if (audio_device->id < 1) { + error(ERROR_SDL_AUDIO_INIT, "AudioDivice failed to open! SDL Error: %s", SDL_GetError()); + return NULL; + } + + // default state of the device is paused, so we unpause it here + SDL_PauseAudioDevice(audio_device->id, 0); + return audio_device; +} + +// plays the audio +void audio_play(const AudioDevice* audio_device, const AudioData audio) { + AudioData* playing_audio = audio_device->playing_audio; + + for (int i = 0; i < MAX_SOUNDS; i++) { + // overrite audio that has been deallocated + if (playing_audio[i].length <= 0) { + + // override the audio + playing_audio[i] = audio; + break; // don't continue. :3 + } + } +} diff --git a/src/window/audio.h b/src/window/audio.h new file mode 100644 index 0000000..bd54743 --- /dev/null +++ b/src/window/audio.h @@ -0,0 +1,21 @@ +#pragma once +#include + +typedef struct { + Uint32 length; + Uint32 mixed_amount; + Uint8* buffer; +} AudioData; + +typedef struct { + SDL_AudioDeviceID id; + int freq; + SDL_AudioFormat format; + Uint8 channels; + AudioData* playing_audio; +} AudioDevice; + + +AudioData audio_load_wav(const AudioDevice* audio_device, const char* file_path); +AudioDevice* audio_device_init(const int freq, const SDL_AudioFormat format, const Uint8 channels, const Uint16 samples); +void audio_play(const AudioDevice* audio_device, const AudioData audio); diff --git a/src/window/renderer.c b/src/window/renderer.c new file mode 100644 index 0000000..8ba1333 --- /dev/null +++ b/src/window/renderer.c @@ -0,0 +1,44 @@ +// initializes the window and renderer +#include "renderer.h" + +#include +#include + +#include "../errors.h" +#include "../main.h" + + +int renderer_init(SDL_Window** window, SDL_Renderer** renderer) { + // create a new window + *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 100, 100, SDL_WINDOW_SHOWN); + if (*window == NULL) { + error(ERROR_SDL_RENDERER_INIT, "Window failed to be created! SDL Error: %s", SDL_GetError()); + return -1; + } + + // create a renderer + *renderer = SDL_CreateRenderer(*window, -1, SDL_RENDERER_PRESENTVSYNC); + if (*renderer == NULL) { + error(ERROR_SDL_RENDERER_INIT, "Renderer failed to be created! SDL Error: %s", SDL_GetError()); + return -1; + } + + return 0; +} + +void renderer_update(const RenderData* render_data) { + SDL_Renderer* renderer = render_data->renderer; + + int success = 0; // if an error occurs, this value is <0 + + // clear render + success |= SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x50); + success |= SDL_RenderClear(renderer); + + if (success < 0) { + warn("\033[93mW\033[0m: something went wrong whilst renderering! SDL Error: %s\n", SDL_GetError()); + return; + } + + SDL_RenderPresent(renderer); +} diff --git a/src/window/renderer.h b/src/window/renderer.h new file mode 100644 index 0000000..60a636b --- /dev/null +++ b/src/window/renderer.h @@ -0,0 +1,10 @@ +#pragma once +#include + +typedef struct { + SDL_Window* window; + SDL_Renderer* renderer; +} RenderData; + +int renderer_init(SDL_Window** window, SDL_Renderer** renderer); +void renderer_update(const RenderData* render_data);