├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json └── settings.json ├── CMakeLists.txt ├── Doxyfile ├── Doxyfile.bak ├── LICENSE ├── README.md ├── include ├── Audio.h ├── GraphicsContext.h ├── Input.h ├── Primitive.h ├── QuickGame.h ├── QuickGame.hpp ├── Sprite.h ├── Texture.h ├── Tilemap.h ├── Timer.h └── Types.h ├── interpreter ├── audio.c ├── audio.h ├── graphics.c ├── graphics.h ├── input.c ├── input.h ├── main.c ├── sprite.c └── sprite.h ├── samples ├── audio │ ├── CMakeLists.txt │ └── main.cpp ├── basic-contained │ ├── CMakeLists.txt │ └── main.c ├── basic-cpp │ ├── CMakeLists.txt │ └── main.cpp ├── basic │ ├── CMakeLists.txt │ └── main.c ├── camera-input │ ├── CMakeLists.txt │ └── main.c ├── collision-basic │ ├── CMakeLists.txt │ └── main.c ├── lua │ ├── audio.lua │ ├── basic.lua │ ├── blank.lua │ ├── camera.lua │ ├── input.lua │ ├── primitive.lua │ ├── print.lua │ └── timer.lua ├── primitive │ ├── CMakeLists.txt │ └── main.c └── tilemap │ ├── CMakeLists.txt │ └── main.c ├── src ├── Audio.c ├── GraphicsContext.c ├── Input.c ├── Primitive.c ├── QuickGame.c ├── Sprite.c ├── Texture.c ├── Tilemap.c ├── Timer.c └── osl_sound │ ├── VirtualFile.c │ ├── VirtualFile.h │ ├── audio.c │ ├── audio.h │ ├── bgm.c │ ├── bgm.h │ ├── media.c │ ├── oslib.h │ ├── pgeWav.c │ ├── pgeWav.h │ ├── readwav.h │ └── vfsFile.c └── stbi ├── stb_image.h └── stbi.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | build 55 | build-example 56 | build-cpp 57 | docs -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gu2gl"] 2 | path = gu2gl 3 | url = https://github.com/IridescentRose/gu2gl 4 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/clang", 10 | "cStandard": "c17", 11 | "cppStandard": "c++14", 12 | "intelliSenseMode": "linux-clang-x64" 13 | }, 14 | { 15 | "name": "PSP", 16 | "includePath": [ 17 | "${workspaceFolder}/**", 18 | "/usr/local/pspdev/psp/sdk/include/**", 19 | "/usr/local/pspdev/psp/include/**" 20 | ], 21 | "compilerPath": "/usr/local/pspdev/bin/psp-gcc", 22 | "cStandard": "c11", 23 | "intelliSenseMode": "linux-clang-x64", 24 | "cppStandard": "c++17" 25 | } 26 | ], 27 | "version": 4 28 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "quickgame.h": "c", 4 | "gu2gl.h": "c", 5 | "graphicscontext.h": "c", 6 | "stddef.h": "c", 7 | "types.h": "c", 8 | "sprite.h": "c", 9 | "input.h": "c", 10 | "texture.h": "c", 11 | "pspkernel.h": "c", 12 | "audio.h": "c", 13 | "tilemap.h": "c", 14 | "timer.h": "c", 15 | "lualib.h": "c", 16 | "lauxlib.h": "c", 17 | "lua.h": "c", 18 | "graphics.h": "c" 19 | } 20 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(QuickGame) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | file(GLOB_RECURSE SRC_FILES src/*.c) 8 | file(GLOB_RECURSE INC_FILES include/*.h) 9 | 10 | add_library(QuickGame STATIC ${SRC_FILES} ${INC_FILES}) 11 | add_library(STBI STATIC stbi/stb_image.h stbi/stbi.c) 12 | 13 | target_link_libraries(QuickGame PUBLIC pspgum pspgu pspge psputility pspdisplay pspctrl pspnet pspnet_apctl psppower pspaudio STBI) 14 | 15 | target_include_directories(QuickGame PUBLIC gu2gl/) 16 | target_include_directories(QuickGame PUBLIC stbi/) 17 | target_include_directories(QuickGame PUBLIC include/) 18 | 19 | target_compile_options(QuickGame PRIVATE -Wall -Werror -Wno-unused) 20 | 21 | 22 | add_executable(interpreter ${INC_FILES} interpreter/main.c interpreter/graphics.c interpreter/input.c interpreter/audio.c interpreter/sprite.c) 23 | 24 | target_link_libraries(interpreter PUBLIC QuickGame pspdebug pspgum pspgu pspge psputility pspdisplay pspctrl pspnet pspnet_apctl psppower pspaudio STBI lua) 25 | target_include_directories(interpreter PUBLIC gu2gl/) 26 | target_include_directories(interpreter PUBLIC stbi/) 27 | target_include_directories(interpreter PUBLIC include/) 28 | 29 | target_compile_options(interpreter PRIVATE -Wall -Werror -Wno-unused) 30 | 31 | create_pbp_file( 32 | TARGET interpreter 33 | TITLE QuickGameLua 34 | BUILD_PRX ON 35 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nathan Bourgeois 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuickGame 2 | Simple PSP Graphics & Games Library 3 | 4 | ## Sample 5 | 6 | ```c 7 | #include 8 | 9 | int main(int argc, char** argv){ 10 | if(QuickGame_Init() < 0) 11 | return 1; 12 | 13 | QGTexture_t tex = QuickGame_Texture_Load("circle.png", 0, 0); 14 | QGSprite_t sprite = QuickGame_Sprite_Create_Alt(240, 136, 256, 256, tex); 15 | 16 | QuickGame_Graphics_Set2D(); 17 | 18 | while(QuickGame_Running()){ 19 | QuickGame_Graphics_Start_Frame(); 20 | 21 | QuickGame_Graphics_Clear(); 22 | 23 | QuickGame_Sprite_Draw(sprite); 24 | 25 | QuickGame_Graphics_End_Frame(true); 26 | } 27 | 28 | QuickGame_Sprite_Destroy(&sprite); 29 | QuickGame_Texture_Destroy(&tex); 30 | 31 | QuickGame_Terminate(); 32 | return 0; 33 | } 34 | ``` 35 | 36 | ## Usage 37 | QuickGame has a C, C++, and Lua-based API. These APIs all have the same features, and this includes audio, graphics, collisions, and inputs. 38 | The goal of the API is to make PSP programming so simple you don't need to know much about the underlying system to use it. 39 | 40 | The interpreter is available for download and the C/C++ API can be used via CMake 41 | 42 | ## Samples 43 | Beyond just the included samples, there's also a [Pong Demo](https://youtu.be/J3xVZsjFDhw) and [Flappy Bird Demo](https://youtu.be/T5x3K4aWLMs) 44 | 45 | ## Documentation 46 | Documentation can be found here: https://iridescentrose.github.io/QuickGame/ 47 | -------------------------------------------------------------------------------- /include/Audio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Audio.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-10-01 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _AUDIO_INCLUDED_H_ 13 | #define _AUDIO_INCLUDED_H_ 14 | 15 | #include 16 | 17 | #if __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * @brief Initializes the audio subsystem 23 | * 24 | */ 25 | void QuickGame_Audio_Init(); 26 | 27 | /** 28 | * @brief Terminates the audio subsystem 29 | * 30 | */ 31 | void QuickGame_Audio_Terminate(); 32 | 33 | /** 34 | * @brief Loads an audio clip 35 | * 36 | * @param filename File name 37 | * @param looping Whether or not the audio is looping 38 | * @param streaming Whether or not we should stream the audio from disk 39 | * @return QGAudioClip_t Result audio clip or NULL on failure 40 | */ 41 | QGAudioClip_t QuickGame_Audio_Load(const char* filename, bool looping, bool streaming); 42 | 43 | /** 44 | * @brief Audio Destroy 45 | * 46 | * @param clip Destroys an audio clip 47 | */ 48 | void QuickGame_Audio_Destroy(QGAudioClip_t* clip); 49 | 50 | /** 51 | * @brief Sets the clip's looping mode 52 | * 53 | * @param clip Clip to set 54 | * @param looping Whether or not the clip should play in a loop 55 | */ 56 | void QuickGame_Audio_Set_Looping(QGAudioClip_t clip, bool looping); 57 | 58 | /** 59 | * @brief Sets the clip's volume 60 | * 61 | * @param clip Clip to set 62 | * @param volume Volume of the clip [0, 1] 63 | */ 64 | void QuickGame_Audio_Set_Volume(QGAudioClip_t clip, f32 volume); 65 | 66 | /** 67 | * @brief Sets the clips' panning 68 | * 69 | * @param clip Clip to set 70 | * @param pan Panning from [-1, 1] 71 | */ 72 | void QuickGame_Audio_Set_Pan(QGAudioClip_t clip, f32 pan); 73 | 74 | /** 75 | * @brief Plays an audio clip on a channel. There are channels 0-8 76 | * 77 | * @param clip Clip to play 78 | * @param channel Clip channel 79 | */ 80 | void QuickGame_Audio_Play(QGAudioClip_t clip, u8 channel); 81 | 82 | /** 83 | * @brief Pauses an audio clip (this toggles if you call pause on a paused clip) 84 | * 85 | * @param clip Clip to pause 86 | */ 87 | void QuickGame_Audio_Pause(QGAudioClip_t clip); 88 | 89 | /** 90 | * @brief Stops an audio clip 91 | * 92 | * @param clip Clip to stop 93 | */ 94 | void QuickGame_Audio_Stop(QGAudioClip_t clip); 95 | 96 | #if __cplusplus 97 | }; 98 | #endif 99 | 100 | #endif -------------------------------------------------------------------------------- /include/GraphicsContext.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file GraphicsContext.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-10 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _GRAPHICS_CONTEXT_INCLUDED_H_ 13 | #define _GRAPHICS_CONTEXT_INCLUDED_H_ 14 | 15 | #include 16 | 17 | #if __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * @brief Initializes the graphics context 23 | * 24 | */ 25 | void QuickGame_Graphics_Init(); 26 | 27 | /** 28 | * @brief Terminates the graphics context 29 | * 30 | */ 31 | void QuickGame_Graphics_Terminate(); 32 | 33 | /** 34 | * @brief Sets a dialog mode 35 | * 36 | * @param mode Are we inside a dialog mode? 37 | */ 38 | void QuickGame_Graphics_Set_Dialog_Mode(bool mode); 39 | 40 | /** 41 | * @brief Sets wireframe mode 42 | * 43 | * @param mode Draw in wireframe mode? 44 | */ 45 | void QuickGame_Graphics_Set_Wireframe_Mode(bool mode); 46 | 47 | /** 48 | * @brief Starts a new frame 49 | * 50 | */ 51 | void QuickGame_Graphics_Start_Frame(); 52 | 53 | /** 54 | * @brief Ends the frame and draws to screen 55 | * 56 | * @param vsync Whether or not to VSync? 57 | */ 58 | void QuickGame_Graphics_End_Frame(bool vsync); 59 | 60 | /** 61 | * @brief Sets the clear color of the screen 62 | * 63 | * @param color Background color 64 | */ 65 | void QuickGame_Graphics_Set_Clear_Color(QGColor color); 66 | 67 | /** 68 | * @brief Clears the screen 69 | * 70 | */ 71 | void QuickGame_Graphics_Clear(); 72 | 73 | /** 74 | * @brief Sets rendering for 2D -- must be called inside of StartFrame() EndFrame() 75 | * 76 | */ 77 | void QuickGame_Graphics_Set2D(); 78 | 79 | /** 80 | * @brief Creates a mesh with the select mesh type and number of elements 81 | * 82 | * @param type Type of Mesh 83 | * @param vcount Vertex Count 84 | * @param icount Index Count 85 | * @return QGVMesh* Result Mesh or NULL on failure. 86 | */ 87 | QGVMesh_t QuickGame_Graphics_Create_Mesh(const u8 type, const usize vcount, const usize icount); 88 | 89 | /** 90 | * @brief Sets a camera to track 91 | * 92 | * @param camera Camera to track 93 | */ 94 | void QuickGame_Graphics_Set_Camera(QGCamera2D* camera); 95 | 96 | /** 97 | * @brief Stops tracking the previously set camera. 98 | * 99 | */ 100 | void QuickGame_Graphics_Unset_Camera(); 101 | 102 | /** 103 | * @brief Destroys a Graphics Mesh and sets pointer to NULL. 104 | * 105 | * @param mesh Pointer to Mesh to destroy 106 | */ 107 | void QuickGame_Graphics_Destroy_Mesh(QGVMesh_t* mesh); 108 | 109 | /** 110 | * @brief Draws a Graphics Mesh 111 | * 112 | * @param mesh Mesh to draw 113 | */ 114 | void QuickGame_Graphics_Draw_Mesh(QGVMesh_t mesh); 115 | 116 | #if __cplusplus 117 | }; 118 | #endif 119 | 120 | #endif -------------------------------------------------------------------------------- /include/Input.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Input.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-27 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | 13 | #ifndef _QUICKGAME_INPUT_INCLUDED_H_ 14 | #define _QUICKGAME_INPUT_INCLUDED_H_ 15 | 16 | #include 17 | 18 | #if __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * @brief Initialize the input system -- does not need termination 24 | * 25 | */ 26 | void QuickGame_Input_Init(); 27 | 28 | /** 29 | * @brief Update the input system 30 | * 31 | */ 32 | void QuickGame_Input_Update(); 33 | 34 | /** 35 | * @brief Is Button Pressed? This will only return true on the very FIRST time the button (combination) is pressed until it is released 36 | * 37 | * @param buttons Button or button combination from PSP CTRL 38 | * @return true Button is pressed 39 | * @return false Button is not pressed 40 | */ 41 | bool QuickGame_Button_Pressed(u32 buttons); 42 | 43 | /** 44 | * @brief Is Button Held? This will return true on the subsequent times the button (combination) is held until it is released 45 | * 46 | * @param buttons Button or button combination from PSP CTRL 47 | * @return true Button is held 48 | * @return false Button is not held 49 | */ 50 | bool QuickGame_Button_Held(u32 buttons); 51 | 52 | /** 53 | * @brief Is Button Released? Only returns true if this is the EXACT moment of release. This only trigger when a button (combination) stops being pressed. 54 | * 55 | * @param buttons Button or button combination from PSP CTRL 56 | * @return true 57 | * @return false 58 | */ 59 | bool QuickGame_Button_Released(u32 buttons); 60 | 61 | /** 62 | * @brief Gets the current analog X position, scaled to [-1, 1] 63 | * 64 | * @return f32 X position 65 | */ 66 | f32 QuickGame_Analog_X(); 67 | 68 | /** 69 | * @brief Gets the current analog Y position, scaled to [-1, 1] 70 | * 71 | * @return f32 Y position 72 | */ 73 | f32 QuickGame_Analog_Y(); 74 | 75 | #if __cplusplus 76 | }; 77 | #endif 78 | 79 | #endif -------------------------------------------------------------------------------- /include/Primitive.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Primitive.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-10-03 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | 13 | #ifndef _QG_PRIMITIVE_INCLUDED_H_ 14 | #define _QG_PRIMITIVE_INCLUDED_H_ 15 | 16 | #include 17 | 18 | 19 | #if __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | /** 24 | * @brief Initialize Primitive Drawings 25 | * 26 | * returns int < 0 on failure. 27 | */ 28 | int QuickGame_Primitive_Init(); 29 | 30 | /** 31 | * @brief Terminate Primitives 32 | * 33 | */ 34 | void QuickGame_Primitive_Terminate(); 35 | 36 | /** 37 | * @brief Draw a rectangle with a given transform and color 38 | * 39 | * @param transform Position, Rotation, Size 40 | * @param color Color to draw with 41 | */ 42 | void QuickGame_Primitive_Draw_Rectangle(QGTransform2D transform, QGColor color); 43 | 44 | /** 45 | * @brief Draw a triangle with a given transform and color 46 | * 47 | * @param transform Position, Rotation, Size 48 | * @param color Color to draw with 49 | */ 50 | void QuickGame_Primitive_Draw_Triangle(QGTransform2D transform, QGColor color); 51 | 52 | /** 53 | * @brief Draw a circle with a given transform and color 54 | * 55 | * @param transform Position, Rotation, Size 56 | * @param color Color to draw with 57 | */ 58 | void QuickGame_Primitive_Draw_Circle(QGTransform2D transform, QGColor color); 59 | 60 | #if __cplusplus 61 | }; 62 | #endif 63 | 64 | #endif -------------------------------------------------------------------------------- /include/QuickGame.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file QuickGame.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-10 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _QUICKGAME_INCLUDED_H_ 13 | #define _QUICKGAME_INCLUDED_H_ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #if __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | /** 30 | * @brief Initializes the game engine 31 | * @return < 0 on failure, 0 on success 32 | * 33 | */ 34 | i32 QuickGame_Init(); 35 | 36 | /** 37 | * @brief 38 | * 39 | */ 40 | bool QuickGame_Running(); 41 | 42 | /** 43 | * @brief Terminates the game engine 44 | * 45 | */ 46 | void QuickGame_Terminate(); 47 | 48 | /** 49 | * @brief Requests an exit from the game engine 50 | * 51 | */ 52 | void QuickGame_Request_Exit(); 53 | 54 | /** 55 | * MEMORY ALLOCATION 56 | * TO OVERRIDE THESE FUNCTIONS 57 | * #define QUICKGAME_CUSTOM_ALLOCATOR 58 | */ 59 | 60 | /** 61 | * @brief Allocates memory of size n 62 | * 63 | * @param n Size in bytes to allocate 64 | * @return anyopaque* Result or NULL if failed 65 | */ 66 | anyopaque* QuickGame_Allocate(usize n); 67 | 68 | /** 69 | * @brief Allocates memory of size n and aligned to a bytes 70 | * 71 | * @param a 72 | * @param n 73 | * @return anyopaque* 74 | */ 75 | anyopaque* QuickGame_Allocate_Aligned(usize a, usize n); 76 | 77 | /** 78 | * @brief Destroyes memory allocation 79 | * 80 | * @param src Memory allocation to destroy 81 | */ 82 | void QuickGame_Destroy(anyopaque* src); 83 | 84 | #if __cplusplus 85 | }; 86 | #endif 87 | 88 | #endif -------------------------------------------------------------------------------- /include/QuickGame.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file QuickGame.hpp 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-09-29 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace QuickGame { 17 | /** 18 | * @brief Initializes the game engine 19 | * @throw Throws a runtime error exception on failure 20 | */ 21 | inline auto init() -> void { 22 | if(QuickGame_Init() < 0) 23 | throw std::runtime_error("Failed to initialize!"); 24 | } 25 | 26 | /** 27 | * @brief Tells whether the game is running 28 | * 29 | */ 30 | inline auto running() noexcept -> bool { 31 | return QuickGame_Running(); 32 | } 33 | 34 | /** 35 | * @brief Terminates the game engine 36 | * 37 | */ 38 | inline auto terminate() noexcept -> void { 39 | QuickGame_Terminate(); 40 | } 41 | 42 | /** 43 | * @brief Requests an exit from the game engine 44 | * 45 | */ 46 | inline auto request_exit() noexcept -> void { 47 | QuickGame_Request_Exit(); 48 | } 49 | 50 | /** 51 | * MEMORY ALLOCATION 52 | * TO OVERRIDE THESE FUNCTIONS 53 | * #define QUICKGAME_CUSTOM_ALLOCATOR 54 | */ 55 | 56 | /** 57 | * @brief Allocates memory of size n 58 | * 59 | * @param n Size in bytes to allocate 60 | * @return anyopaque* Result or NULL if failed 61 | */ 62 | inline auto allocate(usize n) -> anyopaque* { 63 | return QuickGame_Allocate(n); 64 | } 65 | 66 | /** 67 | * @brief Allocates memory of size n and aligned to a bytes 68 | * 69 | * @param a 70 | * @param n 71 | * @return anyopaque* 72 | */ 73 | inline auto allocate_aligned(usize a, usize n) -> anyopaque* { 74 | return QuickGame_Allocate_Aligned(a, n); 75 | } 76 | 77 | /** 78 | * @brief Destroyes memory allocation 79 | * 80 | * @param src Memory allocation to destroy 81 | */ 82 | inline auto destroy(anyopaque* src) -> void { 83 | return QuickGame_Destroy(src); 84 | } 85 | 86 | namespace Graphics{ 87 | /** 88 | * @brief Initializes the graphics context 89 | * 90 | */ 91 | inline auto init() -> void { 92 | QuickGame_Graphics_Init(); 93 | } 94 | 95 | /** 96 | * @brief Terminates the graphics context 97 | * 98 | */ 99 | inline auto terminate() noexcept -> void { 100 | QuickGame_Graphics_Terminate(); 101 | } 102 | 103 | /** 104 | * @brief Sets a dialog mode 105 | * 106 | * @param mode Are we inside a dialog mode? 107 | */ 108 | inline auto set_dialog_mode(const bool&& mode) noexcept -> void { 109 | QuickGame_Graphics_Set_Dialog_Mode(mode); 110 | } 111 | 112 | 113 | /** 114 | * @brief Sets wireframe mode 115 | * 116 | * @param mode Draw in wireframe mode? 117 | */ 118 | inline auto set_wireframe_mode(const bool&& mode) noexcept -> void { 119 | QuickGame_Graphics_Set_Wireframe_Mode(mode); 120 | } 121 | 122 | /** 123 | * @brief Starts a new frame 124 | * 125 | */ 126 | inline auto start_frame() noexcept -> void { 127 | QuickGame_Graphics_Start_Frame(); 128 | } 129 | 130 | /** 131 | * @brief Ends the frame and draws to screen 132 | * 133 | * @param vsync Whether or not to VSync? 134 | */ 135 | inline auto end_frame(const bool&& vsync) noexcept -> void { 136 | QuickGame_Graphics_End_Frame(vsync); 137 | } 138 | 139 | /** 140 | * @brief Sets the clear color of the screen 141 | * 142 | * @param color Background color 143 | */ 144 | inline auto set_clear_color(const QGColor&& color) noexcept -> void { 145 | QuickGame_Graphics_Set_Clear_Color(color); 146 | } 147 | 148 | /** 149 | * @brief Clears the screen 150 | * 151 | */ 152 | inline auto clear() noexcept -> void { 153 | QuickGame_Graphics_Clear(); 154 | } 155 | 156 | /** 157 | * @brief Sets rendering for 2D -- must be called inside of StartFrame() EndFrame() 158 | * 159 | */ 160 | inline auto set2D() noexcept -> void { 161 | QuickGame_Graphics_Set2D(); 162 | } 163 | 164 | /** 165 | * @brief Sets a camera to track 166 | * 167 | * @param camera Camera to track 168 | */ 169 | inline auto set_camera(QGCamera2D& camera) { 170 | QuickGame_Graphics_Set_Camera(&camera); 171 | } 172 | 173 | /** 174 | * @brief Stops tracking the previously set camera. 175 | * 176 | */ 177 | inline auto unset_camera() { 178 | QuickGame_Graphics_Unset_Camera(); 179 | } 180 | 181 | class Mesh { 182 | public: 183 | Mesh() : ir(NULL) {} 184 | 185 | /** 186 | * @brief Construct a new Mesh object 187 | * 188 | * @param type Vertex Type 189 | * @param vcount Vertex Count 190 | * @param icount Index Count 191 | */ 192 | Mesh(const u8 type, const usize vcount, const usize icount) : ir(NULL) { 193 | create_mesh(type, vcount, icount); 194 | } 195 | 196 | virtual ~Mesh() { 197 | delete_data(); 198 | } 199 | 200 | /** 201 | * @brief Create a mesh 202 | * 203 | * @param type Vertex Type 204 | * @param vcount Vertex Count 205 | * @param icount Index Count 206 | */ 207 | inline auto create_mesh(const u8 type, const usize vcount, const usize icount) -> void { 208 | if(ir != NULL) 209 | delete_data(); 210 | else 211 | ir = QuickGame_Graphics_Create_Mesh(type, vcount, icount); 212 | 213 | if(ir == NULL) 214 | throw std::runtime_error("Mesh creation failed!"); 215 | } 216 | 217 | /** 218 | * @brief Add Data - this copies the data and does not take ownership! 219 | * 220 | * @param verts Vertex data 221 | * @param vcount Vertex Count 222 | * @param indices Index data 223 | * @param icount Index Count 224 | */ 225 | inline auto add_data(const void* verts, size_t vcount, const u16* indices, size_t icount) -> void { 226 | if(verts == nullptr || indices == nullptr) 227 | throw std::runtime_error("Mesh data null!"); 228 | 229 | int size = 0; 230 | switch(ir->type) { 231 | case QG_VERTEX_TYPE_COLORED: 232 | size = sizeof(QGColoredVertex); 233 | break; 234 | case QG_VERTEX_TYPE_FULL: 235 | size = sizeof(QGFullVertex); 236 | break; 237 | case QG_VERTEX_TYPE_TEXTURED: 238 | size = sizeof(QGTexturedVertex); 239 | break; 240 | case QG_VERTEX_TYPE_SIMPLE: 241 | size = sizeof(QGSimpleVertex); 242 | break; 243 | } 244 | 245 | memcpy(ir->data, verts, vcount * size); 246 | memcpy(ir->indices, indices, icount * sizeof(u16)); 247 | } 248 | 249 | /** 250 | * @brief Draws the mesh 251 | * 252 | */ 253 | inline auto draw() noexcept -> void { 254 | QuickGame_Graphics_Draw_Mesh(ir); 255 | } 256 | 257 | /** 258 | * @brief Deletes data -- you need to call create after this to reuse 259 | * 260 | */ 261 | inline auto delete_data() noexcept -> void { 262 | QuickGame_Graphics_Destroy_Mesh(&ir); 263 | } 264 | 265 | protected: 266 | QGVMesh_t ir; 267 | }; 268 | 269 | class Texture { 270 | public: 271 | 272 | /** 273 | * @brief Load a texture from disk 274 | * 275 | * @param filename File to load 276 | * @param vram Whether the texture should be stored in VRAM 277 | * @return QGTexture_t Texture loaded or NULL if failed 278 | */ 279 | Texture(const char* filename, const bool flip, const bool vram) : ir(NULL) { 280 | ir = QuickGame_Texture_Load(filename, flip, vram); 281 | if(ir == nullptr) 282 | throw std::runtime_error("Could not load texture!"); 283 | } 284 | 285 | /** 286 | * @brief Load a texture from disk 287 | * 288 | * @param tex_info Texture information about loading 289 | * @return QGTexture_t Texture loaded of NULL if failed 290 | */ 291 | Texture(const QGTexInfo tex_info) { 292 | ir = QuickGame_Texture_Load_Alt(tex_info); 293 | if(ir == nullptr) 294 | throw std::runtime_error("Could not load texture!"); 295 | } 296 | 297 | /** 298 | * @brief Texture to bind to the graphics engine 299 | * 300 | * @param texture Texture to bind 301 | */ 302 | inline auto bind() noexcept -> void { 303 | QuickGame_Texture_Bind(ir); 304 | } 305 | 306 | /** 307 | * @brief Texture to unbind from the graphics engine 308 | * 309 | */ 310 | inline auto unbind() noexcept -> void { 311 | QuickGame_Texture_Unbind(); 312 | } 313 | 314 | virtual ~Texture() { 315 | QuickGame_Texture_Destroy(&ir); 316 | } 317 | 318 | friend class Sprite; 319 | 320 | protected: 321 | QGTexture_t ir; 322 | }; 323 | 324 | class Tilemap { 325 | public: 326 | 327 | /** 328 | * @brief Create a tilemap 329 | * 330 | * @param texture_atlas Texture Atlas size 331 | * @param texture Texture to use 332 | * @param size Size of tile map 333 | * @return QGTilemap_t Created tilemap or NULL on failure 334 | */ 335 | Tilemap(QGTextureAtlas texture_atlas, QGTexture_t texture, QGVector2 size) { 336 | ir = QuickGame_Tilemap_Create(texture_atlas, texture, size); 337 | } 338 | ~Tilemap() { 339 | QuickGame_Tilemap_Destroy(&ir); 340 | } 341 | 342 | inline auto intersects(QGTransform2D transform) noexcept -> bool { 343 | return QuickGame_Tilemap_Intersects(ir, transform); 344 | } 345 | 346 | inline auto draw_string(std::string str, QGVector2 position) noexcept -> void { 347 | return QuickGame_Tilemap_Draw_String(ir, str.c_str(), position); 348 | } 349 | 350 | inline auto build() noexcept -> void { 351 | QuickGame_Tilemap_Build(ir); 352 | } 353 | inline auto draw() noexcept -> void { 354 | QuickGame_Tilemap_Draw(ir); 355 | } 356 | 357 | protected: 358 | QGTilemap_t ir; 359 | }; 360 | 361 | class Sprite { 362 | public: 363 | QGTransform2D transform; 364 | i32 layer; 365 | QGColor color; 366 | 367 | /** 368 | * @brief Creates a sprite 369 | * 370 | * @param position Position for sprite 371 | * @param size Size of sprite 372 | * @param texture Texture to use 373 | * @return Sprite Sprite result or NULL if failed 374 | */ 375 | Sprite(const QGVector2 position, const QGVector2 size, Texture* texture) { 376 | transform = {position, 0, size}; 377 | layer = 0; 378 | color.color = 0xFFFFFFFF; 379 | 380 | ir = QuickGame_Sprite_Create(position, size, texture->ir); 381 | 382 | if(ir == NULL) 383 | throw std::runtime_error("Could not make sprite!"); 384 | } 385 | 386 | /** 387 | * @brief Creates a sprite and loads a texture. This texture is owned by the sprite and destroyed on sprite destroy. 388 | * 389 | * @param position Position for sprite 390 | * @param size Size of sprite 391 | * @param tex_info Information about the texture to load 392 | * @return QGSprite_t Sprite result or NULL if failed 393 | */ 394 | Sprite(const QGVector2&& position, const QGVector2&& size, const QGTexInfo tex_info) { 395 | transform = {position, 0, size}; 396 | layer = 0; 397 | color.color = 0xFFFFFFFF; 398 | 399 | ir = QuickGame_Sprite_Create_Contained(position.x, position.y, size.x, size.y, tex_info); 400 | 401 | if(ir == NULL) 402 | throw std::runtime_error("Could not make sprite!"); 403 | } 404 | 405 | virtual ~Sprite() { 406 | QuickGame_Sprite_Destroy(&ir); 407 | } 408 | 409 | inline auto draw() noexcept -> void{ 410 | if(ir == nullptr) 411 | return; 412 | 413 | ir->transform = transform; 414 | ir->layer = layer; 415 | 416 | QuickGame_Sprite_Draw(ir); 417 | } 418 | 419 | inline auto draw_flipped(uint8_t flip) noexcept -> void { 420 | if(ir == nullptr) 421 | return; 422 | 423 | ir->transform = transform; 424 | ir->layer = layer; 425 | 426 | QuickGame_Sprite_Draw_Flipped(ir, flip); 427 | } 428 | 429 | inline auto intersects(Sprite& other) noexcept -> bool { 430 | return QuickGame_Sprite_Intersects(ir, other.ir); 431 | } 432 | 433 | 434 | 435 | inline auto intersection(Sprite& other) noexcept -> int { 436 | return QuickGame_Sprite_Intersect_Direction(ir, other.ir); 437 | } 438 | 439 | private: 440 | QGSprite_t ir; 441 | }; 442 | 443 | 444 | /** 445 | * @brief Intersection Detection 446 | * 447 | * @param a Transform A 448 | * @param b Transform B 449 | * @return true Transforms intersect 450 | * @return false Transforms do not intersect 451 | */ 452 | inline auto intersect_transform(QGTransform2D a, QGTransform2D b) noexcept -> bool { 453 | return QuickGame_Intersect_Transform(a, b); 454 | } 455 | 456 | } // Graphics 457 | 458 | class Timer { 459 | public: 460 | Timer() { 461 | QuickGame_Timer_Start(&t); 462 | } 463 | virtual ~Timer() { 464 | reset(); 465 | } 466 | 467 | inline auto deltaTime() noexcept -> double { 468 | return QuickGame_Timer_Delta(&t); 469 | } 470 | 471 | inline auto elapsed() noexcept -> double { 472 | return QuickGame_Timer_Elapsed(&t); 473 | } 474 | 475 | inline auto reset() noexcept -> void { 476 | QuickGame_Timer_Reset(&t); 477 | } 478 | 479 | private: 480 | QGTimer t; 481 | }; 482 | 483 | namespace Audio { 484 | /** 485 | * @brief Initializes the audio subsystem 486 | * 487 | */ 488 | inline auto init() noexcept -> void{ 489 | QuickGame_Audio_Init(); 490 | } 491 | 492 | /** 493 | * @brief Terminates the audio subsystem 494 | * 495 | */ 496 | inline auto terminate() noexcept -> void{ 497 | QuickGame_Audio_Terminate(); 498 | } 499 | 500 | class Clip{ 501 | public: 502 | 503 | /** 504 | * @brief Loads an audio clip 505 | * 506 | * @param filename File name 507 | * @param looping Whether or not the audio is looping 508 | * @param streaming Whether or not we should stream the audio from disk 509 | * @return QGAudioClip_t Result audio clip or NULL on failure 510 | */ 511 | Clip(const char* filename, bool looping, bool streaming) { 512 | ir = QuickGame_Audio_Load(filename, looping, streaming); 513 | } 514 | 515 | ~Clip() { 516 | QuickGame_Audio_Destroy(&ir); 517 | } 518 | /** 519 | * @brief Sets the clip's looping mode 520 | * 521 | * @param looping Whether or not the clip should play in a loop 522 | */ 523 | inline auto set_looping(bool looping) noexcept -> void { 524 | QuickGame_Audio_Set_Looping(ir, looping); 525 | } 526 | 527 | /** 528 | * @brief Sets the clip's volume 529 | * 530 | * @param volume Volume of the clip [0, 1] 531 | */ 532 | inline auto set_volume(f32 volume) noexcept -> void { 533 | QuickGame_Audio_Set_Volume(ir, volume); 534 | } 535 | 536 | /** 537 | * @brief Sets the clips' panning 538 | * 539 | * @param pan Panning from [-1, 1] 540 | */ 541 | inline auto set_pan(f32 pan) noexcept -> void { 542 | QuickGame_Audio_Set_Pan(ir, pan); 543 | } 544 | 545 | /** 546 | * @brief Plays an audio clip on a channel. There are channels 0-8 547 | * 548 | * @param channel Clip channel 549 | */ 550 | inline auto play(u8 channel) noexcept -> void { 551 | QuickGame_Audio_Play(ir, channel); 552 | } 553 | 554 | /** 555 | * @brief Pauses an audio clip (this toggles if you call pause on a paused clip) 556 | * 557 | * @param clip Clip to pause 558 | */ 559 | inline auto pause() noexcept -> void { 560 | QuickGame_Audio_Pause(ir); 561 | } 562 | 563 | /** 564 | * @brief Stops an audio clip 565 | * 566 | */ 567 | inline auto stop() noexcept -> void { 568 | QuickGame_Audio_Stop(ir); 569 | } 570 | 571 | private: 572 | QGAudioClip_t ir; 573 | 574 | }; 575 | 576 | } 577 | 578 | namespace Primitive { 579 | /** 580 | * @brief Initialize Primitive Drawings 581 | * 582 | * returns int < 0 on failure. 583 | */ 584 | int init(); 585 | 586 | /** 587 | * @brief Terminate Primitives 588 | * 589 | */ 590 | void terminate(); 591 | 592 | /** 593 | * @brief Draw a rectangle with a given transform and color 594 | * 595 | * @param transform Position, Rotation, Size 596 | * @param color Color to draw with 597 | */ 598 | void draw_rectangle(QGTransform2D transform, QGColor color); 599 | 600 | /** 601 | * @brief Draw a triangle with a given transform and color 602 | * 603 | * @param transform Position, Rotation, Size 604 | * @param color Color to draw with 605 | */ 606 | void draw_triangle(QGTransform2D transform, QGColor color); 607 | 608 | /** 609 | * @brief Draw a circle with a given transform and color 610 | * 611 | * @param transform Position, Rotation, Size 612 | * @param color Color to draw with 613 | */ 614 | void draw_circle(QGTransform2D transform, QGColor color); 615 | } 616 | 617 | namespace Input { 618 | 619 | /** 620 | * @brief Initialize the input system -- does not need termination 621 | * 622 | */ 623 | inline auto init() noexcept -> void { 624 | QuickGame_Input_Init(); 625 | } 626 | 627 | /** 628 | * @brief Update the input system 629 | * 630 | */ 631 | inline auto update() noexcept -> void { 632 | QuickGame_Input_Update(); 633 | } 634 | 635 | /** 636 | * @brief Is Button Pressed? This will only return true on the very FIRST time the button (combination) is pressed until it is released 637 | * 638 | * @param buttons Button or button combination from PSP CTRL 639 | * @return true Button is pressed 640 | * @return false Button is not pressed 641 | */ 642 | inline auto button_pressed(u32 buttons) noexcept -> bool { 643 | return QuickGame_Button_Pressed(buttons); 644 | } 645 | 646 | /** 647 | * @brief Is Button Held? This will return true on the subsequent times the button (combination) is held until it is released 648 | * 649 | * @param buttons Button or button combination from PSP CTRL 650 | * @return true Button is held 651 | * @return false Button is not held 652 | */ 653 | inline auto button_held(u32 buttons) noexcept -> bool { 654 | return QuickGame_Button_Held(buttons); 655 | } 656 | 657 | /** 658 | * @brief Is Button Released? Only returns true if this is the EXACT moment of release. This only trigger when a button (combination) stops being pressed. 659 | * 660 | * @param buttons Button or button combination from PSP CTRL 661 | * @return true 662 | * @return false 663 | */ 664 | inline auto button_released(u32 buttons) noexcept -> bool { 665 | return QuickGame_Button_Released(buttons); 666 | } 667 | 668 | 669 | /** 670 | * @brief Gets the current analog X position, scaled to [-1, 1] 671 | * 672 | * @return f32 X position 673 | */ 674 | inline auto analog_x() noexcept -> f32 { 675 | return QuickGame_Analog_X(); 676 | } 677 | 678 | /** 679 | * @brief Gets the current analog Y position, scaled to [-1, 1] 680 | * 681 | * @return f32 Y position 682 | */ 683 | inline auto analog_y() noexcept -> f32 { 684 | return QuickGame_Analog_Y(); 685 | } 686 | 687 | } 688 | 689 | } // QuickGame 690 | -------------------------------------------------------------------------------- /include/Sprite.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Sprite.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-12 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _SPRITE_INCLUDED_H_ 13 | #define _SPRITE_INCLUDED_H_ 14 | 15 | #include 16 | 17 | #if __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | typedef struct { 22 | QGTransform2D transform; 23 | i32 layer; 24 | QGColor color; 25 | QGTexture_t texture; 26 | QGVMesh_t mesh; 27 | bool contained; 28 | QGVector2 aabb_size; 29 | } QGSprite; 30 | 31 | typedef QGSprite *QGSprite_t; 32 | 33 | /** 34 | * @brief Creates a sprite 35 | * 36 | * @param position Position for sprite 37 | * @param size Size of sprite 38 | * @param texture Texture to use 39 | * @return QGSprite_t Sprite result or NULL if failed 40 | */ 41 | QGSprite_t QuickGame_Sprite_Create(QGVector2 position, QGVector2 size, QGTexture_t texture); 42 | 43 | /** 44 | * @brief Creates a sprite 45 | * 46 | * @param x X of sprite 47 | * @param y Y of sprite 48 | * @param w Width of Sprite 49 | * @param h Height of Sprite 50 | * @param texture Texture to use 51 | * @return QGSprite_t Sprite result or NULL if failed 52 | */ 53 | QGSprite_t QuickGame_Sprite_Create_Alt(float x, float y, float w, float h, QGTexture_t texture); 54 | 55 | /** 56 | * @brief Creates a sprite and loads a texture. This texture is owned by the sprite and destroyed on sprite destroy. 57 | * 58 | * @param x X of sprite 59 | * @param y Y of sprite 60 | * @param w Width of Sprite 61 | * @param h Height of Sprite 62 | * @param tex_info Information about the texture to load 63 | * @return QGSprite_t Sprite result or NULL if failed 64 | */ 65 | QGSprite_t QuickGame_Sprite_Create_Contained(float x, float y, float w, float h, QGTexInfo tex_info); 66 | 67 | /** 68 | * @brief Creates a sprite 69 | * 70 | * @param position Position of sprite 71 | * @param size Size of sprite 72 | * @param u1 top left image x cord 73 | * @param v1 top left image y cord 74 | * @param w width of the selected rect 75 | * @param h height of the selected rect 76 | * @param texture Texture to use 77 | * @return QGSprite_t Sprite result or NULL if failed 78 | */ 79 | QGSprite_t QuickGame_Sprite_Create_Drakonchik(QGVector2 position, QGVector2 size, float u1, float v1, float w, float h, QGTexture_t texture); 80 | 81 | 82 | /** 83 | * @brief Destroy a sprite and sets it to NULL 84 | * 85 | * @param sprite Pointer to Sprite 86 | */ 87 | void QuickGame_Sprite_Destroy(QGSprite_t* sprite); 88 | 89 | /** 90 | * @brief Draws a sprite to the screen 91 | * 92 | * @param sprite Sprite to draw 93 | */ 94 | void QuickGame_Sprite_Draw(QGSprite_t sprite); 95 | 96 | enum QGDirection{ 97 | QG_DIR_UP = 1, 98 | QG_DIR_DOWN = 2, 99 | QG_DIR_LEFT = 3, 100 | QG_DIR_RIGHT = 4, 101 | }; 102 | 103 | enum QGFlip { 104 | QG_FLIP_NONE = 0x00, 105 | QG_FLIP_VERTICAL = 0x01, 106 | QG_FLIP_HORIZONTAL = 0x02, 107 | QG_FLIP_BOTH = 0x03 108 | }; 109 | 110 | /** 111 | * @brief Draws a sprite to the screen 112 | * 113 | * @param sprite Sprite to draw 114 | * @param flip Flip type 115 | */ 116 | void QuickGame_Sprite_Draw_Flipped(QGSprite_t sprite, uint8_t flip); 117 | 118 | /** 119 | * @brief Intersection Detection 120 | * 121 | * @param a Sprite A 122 | * @param b Sprite B 123 | * @return true Sprites intersect 124 | * @return false Sprites do not intersect 125 | */ 126 | bool QuickGame_Sprite_Intersects(QGSprite_t a, QGSprite_t b); 127 | 128 | /** 129 | * @brief Intersection Detection 130 | * 131 | * @param a Transform A 132 | * @param b Transform B 133 | * @return true Transforms intersect 134 | * @return false Transforms do not intersect 135 | */ 136 | bool QuickGame_Intersect_Transform(QGTransform2D a, QGTransform2D b); 137 | 138 | /** 139 | * @brief Intersection Direction 140 | * 141 | * @param a Sprite A 142 | * @param b Sprite B 143 | * @return uint8_t Direction 144 | */ 145 | uint8_t QuickGame_Sprite_Intersect_Direction(QGSprite_t a, QGSprite_t b); 146 | 147 | #if __cplusplus 148 | }; 149 | #endif 150 | 151 | #endif -------------------------------------------------------------------------------- /include/Texture.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Texture.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-11 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | 13 | #ifndef _TEXTURE_INCLUDED_H_ 14 | #define _TEXTURE_INCLUDED_H_ 15 | 16 | #include 17 | 18 | #if __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * @brief Load a texture from disk 24 | * 25 | * @param filename File to load 26 | * @param vram Whether the texture should be stored in VRAM 27 | * @return QGTexture_t Texture loaded or NULL if failed 28 | */ 29 | QGTexture_t QuickGame_Texture_Load(const char* filename, const bool flip, const bool vram); 30 | 31 | /** 32 | * @brief Load a texture from disk 33 | * 34 | * @param tex_info Texture information about loading 35 | * @return QGTexture_t Texture loaded of NULL if failed 36 | */ 37 | QGTexture_t QuickGame_Texture_Load_Alt(const QGTexInfo tex_info); 38 | 39 | /** 40 | * @brief Destroys a texture pointer 41 | * 42 | * @param texture Texture to destroy, also sets the texture pointer to null. 43 | */ 44 | void QuickGame_Texture_Destroy(QGTexture_t* texture); 45 | 46 | /** 47 | * @brief Texture to bind to the graphics engine 48 | * 49 | * @param texture Texture to bind 50 | */ 51 | void QuickGame_Texture_Bind(const QGTexture_t texture); 52 | 53 | /** 54 | * @brief Texture to unbind from the graphics engine 55 | * 56 | */ 57 | void QuickGame_Texture_Unbind(); 58 | 59 | #if __cplusplus 60 | }; 61 | #endif 62 | 63 | #endif -------------------------------------------------------------------------------- /include/Tilemap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Tilemap.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-11 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | 13 | #ifndef _TILEMAP_INCLUDED_H_ 14 | #define _TILEMAP_INCLUDED_H_ 15 | 16 | #include 17 | 18 | #if __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * @brief Gets texture coordinates from an atlas given a position 24 | * 25 | * @param atlas Texture Atlas to use as reference 26 | * @param buf Buffer to fill with the coordinates 27 | * @param idx Index of the atlas to get coordinates for 28 | */ 29 | void QuickGame_Atlas_Index_Coords(const QGTextureAtlas atlas, f32* buf, const usize idx); 30 | 31 | /** 32 | * @brief Create a tilemap 33 | * 34 | * @param texture_atlas Texture Atlas size 35 | * @param texture Texture to use 36 | * @param size Size of tile map 37 | * @return QGTilemap_t Created tilemap or NULL on failure 38 | */ 39 | QGTilemap_t QuickGame_Tilemap_Create(QGTextureAtlas texture_atlas, QGTexture_t texture, QGVector2 size); 40 | 41 | /** 42 | * @brief Test intersection 43 | * 44 | * @param tilemap Tilemap to test against 45 | * @param transform Transform to test with 46 | */ 47 | bool QuickGame_Tilemap_Intersects(QGTilemap_t tilemap, QGTransform2D transform); 48 | 49 | /** 50 | * @brief Draws a string (font-based) 51 | * 52 | * @param tilemap Tilemap to use 53 | * @param str String 54 | */ 55 | void QuickGame_Tilemap_Draw_String(QGTilemap_t tilemap, const char* str, QGVector2 position); 56 | 57 | /** 58 | * @brief Draws a tilemap to the screen 59 | * 60 | * @param tilemap Tilemap 61 | */ 62 | void QuickGame_Tilemap_Draw(QGTilemap_t tilemap); 63 | 64 | /** 65 | * @brief Builds a tilemap to render 66 | * 67 | * @param tilemap Tilemap 68 | */ 69 | void QuickGame_Tilemap_Build(QGTilemap_t tilemap); 70 | 71 | /** 72 | * @brief Destroy a tilemap 73 | * 74 | * @param tilemap Tile map to destroy -- also gets set to null. 75 | */ 76 | void QuickGame_Tilemap_Destroy(QGTilemap_t* tilemap); 77 | 78 | 79 | #if __cplusplus 80 | }; 81 | #endif 82 | 83 | #endif -------------------------------------------------------------------------------- /include/Timer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Timer.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-09-29 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _TIMER_INCLUDED_H_ 13 | #define _TIMER_INCLUDED_H_ 14 | 15 | #include 16 | 17 | #if __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * @brief Start a timer 23 | * 24 | * @param timer Timer to use 25 | */ 26 | void QuickGame_Timer_Start(QGTimer* timer); 27 | 28 | /** 29 | * @brief Get Delta from last delta request / start 30 | * 31 | * @param timer Timer to use 32 | * @return double Delta Time 33 | */ 34 | double QuickGame_Timer_Delta(QGTimer* timer); 35 | 36 | /** 37 | * @brief Elapsed total time since start 38 | * 39 | * @param timer Timer to use 40 | * @return double Elapsed Time 41 | */ 42 | double QuickGame_Timer_Elapsed(QGTimer* timer); 43 | 44 | /** 45 | * @brief Resets a timer 46 | * 47 | * @param timer Timer to use 48 | */ 49 | void QuickGame_Timer_Reset(QGTimer* timer); 50 | 51 | #if __cplusplus 52 | }; 53 | #endif 54 | 55 | #endif -------------------------------------------------------------------------------- /include/Types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file GraphicsType.h 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 1.0 6 | * @date 2022-09-10 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #ifndef _GRAPHICS_TYPE_INCLUDED_H_ 13 | #define _GRAPHICS_TYPE_INCLUDED_H_ 14 | 15 | #include 16 | #include 17 | 18 | /** 19 | * @brief Unsigned Types 20 | * 21 | */ 22 | typedef uint8_t u8; 23 | typedef uint16_t u16; 24 | typedef uint32_t u32; 25 | typedef uint64_t u64; 26 | typedef u32 usize; 27 | 28 | /** 29 | * @brief Signed Types 30 | * 31 | */ 32 | typedef int8_t i8; 33 | typedef int16_t i16; 34 | typedef int32_t i32; 35 | typedef int64_t i64; 36 | typedef i32 isize; 37 | 38 | 39 | /** 40 | * @brief Floating point types 41 | * 42 | */ 43 | typedef float f32; 44 | typedef double f64; 45 | 46 | /** 47 | * @brief Color Union 48 | * 49 | */ 50 | typedef union { 51 | struct __attribute__ ((__packed__)) RGBA { 52 | u8 r; 53 | u8 g; 54 | u8 b; 55 | u8 a; 56 | } rgba; 57 | u32 color; 58 | } QGColor; 59 | 60 | typedef void anyopaque; 61 | 62 | /** 63 | * @brief Textures 64 | * 65 | */ 66 | typedef struct { 67 | u32 width, height; 68 | u32 pWidth, pHeight; 69 | anyopaque* data; 70 | } QGTexture; 71 | 72 | typedef QGTexture *QGTexture_t; 73 | 74 | typedef struct { 75 | const char* filename; 76 | bool flip; 77 | bool vram; 78 | } QGTexInfo; 79 | 80 | /** 81 | * @brief Vector2 82 | */ 83 | typedef struct { 84 | f32 x, y; 85 | } QGVector2; 86 | 87 | /** 88 | * @brief Vector3 89 | * 90 | */ 91 | typedef struct { 92 | f32 x, y, z; 93 | } QGVector3; 94 | 95 | /** 96 | * @brief Transform2D 97 | * 98 | */ 99 | typedef struct { 100 | QGVector2 position; 101 | f32 rotation; 102 | QGVector2 scale; 103 | } QGTransform2D; 104 | 105 | /** 106 | * @brief Vertex with Texture, and Position 107 | * 108 | */ 109 | typedef struct { 110 | float u, v; 111 | float x, y, z; 112 | } QGTexturedVertex; 113 | 114 | /** 115 | * @brief Vertex with Texture, Color, and Position 116 | * 117 | */ 118 | typedef struct { 119 | float u, v; 120 | QGColor color; 121 | float x, y, z; 122 | } QGFullVertex; 123 | 124 | /** 125 | * @brief Vertex with Color and Position 126 | * 127 | */ 128 | typedef struct { 129 | QGColor color; 130 | float x, y, z; 131 | } QGColoredVertex; 132 | 133 | /** 134 | * @brief Simplest vertex 135 | * 136 | */ 137 | typedef struct { 138 | float x, y, z; 139 | } QGSimpleVertex; 140 | 141 | /** 142 | * @brief Vertex types 143 | * 144 | */ 145 | typedef enum { 146 | QG_VERTEX_TYPE_TEXTURED = 0x00, 147 | QG_VERTEX_TYPE_COLORED = 0x01, 148 | QG_VERTEX_TYPE_FULL = 0x02, 149 | QG_VERTEX_TYPE_SIMPLE = 0x03 150 | } QGVType; 151 | 152 | /** 153 | * @brief Basic Mesh data structure 154 | * 155 | */ 156 | typedef struct { 157 | u8 type; 158 | usize count; 159 | 160 | void* data; 161 | u16* indices; 162 | } QGVMesh; 163 | 164 | typedef QGVMesh *QGVMesh_t; 165 | 166 | /** 167 | * @brief Camera data structure 168 | * 169 | */ 170 | typedef struct { 171 | QGVector2 position; 172 | f32 rotation; 173 | } QGCamera2D; 174 | 175 | /** 176 | * @brief Texture Atlas information 177 | * 178 | */ 179 | typedef struct { 180 | f32 x, y; 181 | } QGTextureAtlas; 182 | 183 | 184 | typedef struct { 185 | QGVector2 position; 186 | QGVector2 scale; 187 | usize atlas_idx; 188 | QGColor color; 189 | bool collide; 190 | } QGTile; 191 | 192 | typedef struct { 193 | QGTransform2D transform; 194 | QGTextureAtlas atlas; 195 | QGTexture_t texture; 196 | QGVector2 size; 197 | QGTile* tile_array; 198 | QGVMesh_t mesh; 199 | } QGTilemap; 200 | 201 | typedef QGTilemap *QGTilemap_t; 202 | 203 | typedef struct { 204 | f64 total; 205 | u64 last; 206 | u32 resolution; 207 | } QGTimer; 208 | 209 | typedef struct { 210 | void* data; 211 | } QGAudioClip; 212 | 213 | typedef QGAudioClip *QGAudioClip_t; 214 | 215 | #endif -------------------------------------------------------------------------------- /interpreter/audio.c: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | static int lua_qg_audio_load(lua_State* L) { 4 | int argc = lua_gettop(L); 5 | if (argc != 3) 6 | return luaL_error(L, "Error: AudioClip.load() takes 3 arguments."); 7 | 8 | QGAudioClip** audio = lua_newuserdata(L,sizeof(QGAudioClip*)); 9 | 10 | const char* filename = luaL_checkstring(L, 1); 11 | bool looping = luaL_checkint(L, 2); 12 | bool streaming = luaL_checkint(L, 3); 13 | 14 | *audio = QuickGame_Audio_Load(filename, looping, streaming); 15 | 16 | luaL_getmetatable(L, "AudioClip"); 17 | lua_setmetatable(L, -2); 18 | 19 | return 1; 20 | } 21 | 22 | QGAudioClip** getClip(lua_State* L){ 23 | return (QGAudioClip**)luaL_checkudata(L, 1, "AudioClip"); 24 | } 25 | 26 | static int lua_qg_audio_destroy(lua_State* L) { 27 | QGAudioClip** clip = getClip(L); 28 | QuickGame_Destroy(clip); 29 | 30 | return 0; 31 | } 32 | 33 | static int lua_qg_audio_set_volume(lua_State* L) { 34 | int argc = lua_gettop(L); 35 | if (argc != 2) 36 | return luaL_error(L, "Error: AudioClip:set_volume() takes 2 arguments."); 37 | 38 | QGAudioClip_t clip = *getClip(L); 39 | f32 volume = luaL_checknumber(L, 2); 40 | QuickGame_Audio_Set_Volume(clip, volume); 41 | 42 | return 0; 43 | } 44 | 45 | 46 | static int lua_qg_audio_set_pan(lua_State* L) { 47 | int argc = lua_gettop(L); 48 | if (argc != 2) 49 | return luaL_error(L, "Error: AudioClip:set_pan() takes 2 arguments."); 50 | 51 | QGAudioClip_t clip = *getClip(L); 52 | f32 pan = luaL_checknumber(L, 2); 53 | QuickGame_Audio_Set_Pan(clip, pan); 54 | 55 | return 0; 56 | } 57 | 58 | 59 | static int lua_qg_audio_set_loop(lua_State* L) { 60 | int argc = lua_gettop(L); 61 | if (argc != 2) 62 | return luaL_error(L, "Error: AudioClip:set_loop() takes 2 arguments."); 63 | 64 | QGAudioClip_t clip = *getClip(L); 65 | int looping = lua_toboolean(L, 2); 66 | QuickGame_Audio_Set_Looping(clip, looping); 67 | 68 | return 0; 69 | } 70 | 71 | static int lua_qg_audio_play(lua_State* L) { 72 | int argc = lua_gettop(L); 73 | if (argc != 2) 74 | return luaL_error(L, "Error: AudioClip:play() takes 2 arguments."); 75 | 76 | QGAudioClip_t clip = *getClip(L); 77 | int channel = luaL_checkint(L, 2); 78 | QuickGame_Audio_Play(clip, channel); 79 | 80 | return 0; 81 | } 82 | 83 | static int lua_qg_audio_pause(lua_State* L) { 84 | int argc = lua_gettop(L); 85 | if (argc != 1) 86 | return luaL_error(L, "Error: AudioClip:play() takes 1 argument."); 87 | 88 | QGAudioClip_t clip = *getClip(L); 89 | QuickGame_Audio_Pause(clip); 90 | 91 | return 0; 92 | } 93 | 94 | static int lua_qg_audio_stop(lua_State* L) { 95 | int argc = lua_gettop(L); 96 | if (argc != 1) 97 | return luaL_error(L, "Error: AudioClip:stop() takes 1 argument."); 98 | 99 | QGAudioClip_t clip = *getClip(L); 100 | QuickGame_Audio_Stop(clip); 101 | 102 | return 0; 103 | } 104 | 105 | 106 | static const luaL_Reg audioLib[] = { 107 | {"load", lua_qg_audio_load}, 108 | {"destroy", lua_qg_audio_destroy}, 109 | {"set_loop", lua_qg_audio_set_loop}, 110 | {"set_volume", lua_qg_audio_set_pan}, 111 | {"set_pan", lua_qg_audio_set_volume}, 112 | {"play", lua_qg_audio_play}, 113 | {"pause", lua_qg_audio_pause}, 114 | {"stop", lua_qg_audio_stop}, 115 | {0,0} 116 | }; 117 | 118 | static const luaL_Reg audioMetaLib[] = { 119 | {"__gc", lua_qg_audio_destroy}, 120 | {0,0} 121 | }; 122 | 123 | void initialize_audio(lua_State* L) { 124 | int lib_id, meta_id; 125 | 126 | // new class = {} 127 | lua_createtable(L, 0, 0); 128 | lib_id = lua_gettop(L); 129 | 130 | // meta table = {} 131 | luaL_newmetatable(L, "AudioClip"); 132 | meta_id = lua_gettop(L); 133 | luaL_setfuncs(L, audioMetaLib, 0); 134 | 135 | // meta table = methods 136 | luaL_newlib(L, audioLib); 137 | lua_setfield(L, meta_id, "__index"); 138 | 139 | // meta table.metatable = metatable 140 | luaL_newlib(L, audioMetaLib); 141 | lua_setfield(L, meta_id, "__metatable"); 142 | 143 | // class.metatable = metatable 144 | lua_setmetatable(L, lib_id); 145 | 146 | // AudioClip 147 | lua_setglobal(L, "AudioClip"); 148 | } 149 | -------------------------------------------------------------------------------- /interpreter/audio.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef AUDIO_INCLUDED_H 10 | #define AUDIO_INCLUDED_H 11 | 12 | void initialize_audio(lua_State* L); 13 | 14 | #endif -------------------------------------------------------------------------------- /interpreter/graphics.c: -------------------------------------------------------------------------------- 1 | #include "graphics.h" 2 | 3 | static int lua_qg_start_frame(lua_State* L) { 4 | int argc = lua_gettop(L); 5 | if (argc != 0) 6 | return luaL_error(L, "Error: Graphics.start_frame() takes 0 arguments."); 7 | 8 | QuickGame_Graphics_Start_Frame(); 9 | 10 | return 0; 11 | } 12 | 13 | static int lua_qg_clear(lua_State* L) { 14 | int argc = lua_gettop(L); 15 | if (argc != 0) 16 | return luaL_error(L, "Error: Graphics.clear() takes 0 arguments."); 17 | 18 | QuickGame_Graphics_Clear(); 19 | 20 | return 0; 21 | } 22 | 23 | static int lua_qg_set_clear_color(lua_State* L) { 24 | int argc = lua_gettop(L); 25 | if (argc != 1) 26 | return luaL_error(L, "Error: Graphics.set_clear_color() takes 1 argument."); 27 | 28 | int color = luaL_checkint(L, 1); 29 | 30 | QGColor col; 31 | col.color = color; 32 | 33 | QuickGame_Graphics_Set_Clear_Color(col); 34 | 35 | return 0; 36 | } 37 | 38 | static int lua_qg_end_frame(lua_State* L) { 39 | int argc = lua_gettop(L); 40 | if (argc != 1) 41 | return luaL_error(L, "Error: Graphics.end_frame() takes 1 argument."); 42 | 43 | bool b = lua_toboolean(L, 1); 44 | 45 | QuickGame_Graphics_End_Frame(b); 46 | 47 | return 0; 48 | } 49 | 50 | static int lua_qg_set_dialog_mode(lua_State* L) { 51 | int argc = lua_gettop(L); 52 | if (argc != 1) 53 | return luaL_error(L, "Error: Graphics.set_dialog_mode() takes 1 argument."); 54 | 55 | bool b = lua_toboolean(L, 1); 56 | 57 | QuickGame_Graphics_Set_Dialog_Mode(b); 58 | 59 | return 0; 60 | } 61 | 62 | static int lua_qg_set_wireframe_mode(lua_State* L) { 63 | int argc = lua_gettop(L); 64 | if (argc != 1) 65 | return luaL_error(L, "Error: Graphics.set_wireframe_mode() takes 1 argument."); 66 | 67 | bool b = lua_toboolean(L, 1); 68 | 69 | QuickGame_Graphics_Set_Wireframe_Mode(b); 70 | 71 | return 0; 72 | } 73 | 74 | static int lua_qg_set2D(lua_State* L) { 75 | int argc = lua_gettop(L); 76 | if (argc != 0) 77 | return luaL_error(L, "Error: Graphics.set2D() takes 0 arguments."); 78 | 79 | QuickGame_Graphics_Set2D(); 80 | 81 | return 0; 82 | } 83 | 84 | static int lua_qg_set_camera(lua_State* L) { 85 | int argc = lua_gettop(L); 86 | if (argc != 1) 87 | return luaL_error(L, "Error: Graphics.set_camera() takes 1 argument."); 88 | 89 | QuickGame_Graphics_Set_Camera(luaL_checkudata(L, 1, "Camera")); 90 | 91 | return 0; 92 | } 93 | 94 | static int lua_qg_unset_camera(lua_State* L) { 95 | int argc = lua_gettop(L); 96 | if (argc != 0) 97 | return luaL_error(L, "Error: Graphics.unset_camera() takes 0 arguments."); 98 | 99 | QuickGame_Graphics_Unset_Camera(); 100 | 101 | return 0; 102 | } 103 | 104 | static const luaL_Reg graphicsLib[] = { 105 | {"start_frame", lua_qg_start_frame}, 106 | {"set_clear_color", lua_qg_set_clear_color}, 107 | {"clear", lua_qg_clear}, 108 | {"end_frame", lua_qg_end_frame}, 109 | {"set_dialog_mode", lua_qg_set_dialog_mode}, 110 | {"set_wireframe_mode", lua_qg_set_wireframe_mode}, 111 | {"set2D", lua_qg_set2D}, 112 | {"set_camera", lua_qg_set_camera}, 113 | {"unset_camera", lua_qg_unset_camera}, 114 | {0, 0} 115 | }; 116 | 117 | 118 | static int lua_qg_color_create(lua_State* L) { 119 | int argc = lua_gettop(L); 120 | if (argc != 4) 121 | return luaL_error(L, "Error: Color.create() takes 4 arguments."); 122 | 123 | int r = luaL_checkint(L, 1); 124 | int g = luaL_checkint(L, 2); 125 | int b = luaL_checkint(L, 3); 126 | int a = luaL_checkint(L, 4); 127 | 128 | QGColor color; 129 | color.rgba.r = r; 130 | color.rgba.g = g; 131 | color.rgba.b = b; 132 | color.rgba.a = a; 133 | 134 | lua_pushinteger(L, color.color); 135 | 136 | return 1; 137 | } 138 | 139 | static const luaL_Reg colorLib[] = { 140 | {"create", lua_qg_color_create}, 141 | {0, 0} 142 | }; 143 | 144 | void initialize_graphics(lua_State* L){ 145 | lua_getglobal(L, "Color"); 146 | 147 | if(lua_isnil(L, -1)){ 148 | lua_pop(L, 1); 149 | lua_newtable(L); 150 | } 151 | 152 | luaL_setfuncs(L, colorLib, 0); 153 | lua_setglobal(L, "Color"); 154 | 155 | lua_getglobal(L, "Graphics"); 156 | 157 | if(lua_isnil(L, -1)){ 158 | lua_pop(L, 1); 159 | lua_newtable(L); 160 | } 161 | 162 | luaL_setfuncs(L, graphicsLib, 0); 163 | lua_setglobal(L, "Graphics"); 164 | } 165 | 166 | QGTransform2D* getQGTransform(lua_State* L){ 167 | return (QGTransform2D*)luaL_checkudata(L, 1, "Transform"); 168 | } 169 | QGTransform2D* getQGTransformn(lua_State* L, int n){ 170 | return (QGTransform2D*)luaL_checkudata(L, n, "Transform"); 171 | } 172 | 173 | static int lua_qg_draw_rectangle(lua_State* L) { 174 | int argc = lua_gettop(L); 175 | if (argc == 6){ 176 | float x = luaL_checknumber(L, 1); 177 | float y = luaL_checknumber(L, 2); 178 | float w = luaL_checknumber(L, 3); 179 | float h = luaL_checknumber(L, 4); 180 | 181 | int color = luaL_checkint(L, 5); 182 | QGColor col; 183 | col.color = color; 184 | 185 | float rotation = luaL_checknumber(L, 6); 186 | 187 | QGTransform2D transform = { 188 | .position = {.x = x, .y = y}, 189 | .scale = {.x = w, .y = h}, 190 | .rotation = rotation 191 | }; 192 | 193 | QuickGame_Primitive_Draw_Rectangle(transform, col); 194 | } else if(argc == 2) { 195 | QGTransform2D transform = *getQGTransformn(L, 1); 196 | 197 | int color = luaL_checkint(L, 2); 198 | QGColor col; 199 | col.color = color; 200 | 201 | QuickGame_Primitive_Draw_Rectangle(transform, col); 202 | } else { 203 | return luaL_error(L, "Error: Primitive.draw_rectangle(x, y, w, h, color, rotation) takes 6 arguments."); 204 | } 205 | 206 | return 0; 207 | } 208 | 209 | static int lua_qg_draw_triangle(lua_State* L) { 210 | int argc = lua_gettop(L); 211 | if (argc == 6){ 212 | float x = luaL_checknumber(L, 1); 213 | float y = luaL_checknumber(L, 2); 214 | float w = luaL_checknumber(L, 3); 215 | float h = luaL_checknumber(L, 4); 216 | 217 | int color = luaL_checkint(L, 5); 218 | QGColor col; 219 | col.color = color; 220 | 221 | float rotation = luaL_checknumber(L, 6); 222 | 223 | QGTransform2D transform = { 224 | .position = {.x = x, .y = y}, 225 | .scale = {.x = w, .y = h}, 226 | .rotation = rotation 227 | }; 228 | 229 | QuickGame_Primitive_Draw_Triangle(transform, col); 230 | } else if(argc == 2) { 231 | QGTransform2D transform = *getQGTransformn(L, 1); 232 | 233 | int color = luaL_checkint(L, 2); 234 | QGColor col; 235 | col.color = color; 236 | 237 | QuickGame_Primitive_Draw_Triangle(transform, col); 238 | } else { 239 | return luaL_error(L, "Error: Primitive.draw_triangle(x, y, w, h, color, rotation) takes 6 arguments."); 240 | } 241 | 242 | return 0; 243 | } 244 | 245 | static int lua_qg_draw_circle(lua_State* L) { 246 | int argc = lua_gettop(L); 247 | if (argc == 6){ 248 | float x = luaL_checknumber(L, 1); 249 | float y = luaL_checknumber(L, 2); 250 | float w = luaL_checknumber(L, 3); 251 | float h = luaL_checknumber(L, 4); 252 | 253 | int color = luaL_checkint(L, 5); 254 | QGColor col; 255 | col.color = color; 256 | 257 | float rotation = luaL_checknumber(L, 6); 258 | 259 | QGTransform2D transform = { 260 | .position = {.x = x, .y = y}, 261 | .scale = {.x = w, .y = h}, 262 | .rotation = rotation 263 | }; 264 | 265 | QuickGame_Primitive_Draw_Circle(transform, col); 266 | } else if(argc == 2) { 267 | QGTransform2D transform = *getQGTransformn(L, 1); 268 | 269 | int color = luaL_checkint(L, 2); 270 | QGColor col; 271 | col.color = color; 272 | 273 | QuickGame_Primitive_Draw_Circle(transform, col); 274 | } else { 275 | return luaL_error(L, "Error: Primitive.draw_circle(x, y, w, h, color, rotation) takes 6 arguments."); 276 | } 277 | 278 | return 0; 279 | } 280 | 281 | static const luaL_Reg primitiveLib[] = { 282 | {"draw_rectangle", lua_qg_draw_rectangle}, 283 | {"draw_triangle", lua_qg_draw_triangle}, 284 | {"draw_circle", lua_qg_draw_circle}, 285 | {0, 0} 286 | }; 287 | 288 | void initialize_primitive(lua_State* L) { 289 | lua_getglobal(L, "Primitive"); 290 | 291 | if(lua_isnil(L, -1)){ 292 | lua_pop(L, 1); 293 | lua_newtable(L); 294 | } 295 | 296 | luaL_setfuncs(L, primitiveLib, 0); 297 | lua_setglobal(L, "Primitive"); 298 | } 299 | 300 | static int lua_qg_camera_create(lua_State* L) { 301 | int argc = lua_gettop(L); 302 | if (argc != 0) 303 | return luaL_error(L, "Error: Camera.create() takes 0 arguments."); 304 | 305 | QGCamera2D* cam = lua_newuserdata(L,sizeof(QGCamera2D)); 306 | cam->position.x = 0; 307 | cam->position.y = 0; 308 | cam->rotation = 0.0f; 309 | 310 | luaL_getmetatable(L, "Camera"); 311 | lua_setmetatable(L, -2); 312 | 313 | return 1; 314 | } 315 | 316 | QGCamera2D* getQGCamera(lua_State* L){ 317 | return (QGCamera2D*)luaL_checkudata(L, 1, "Camera"); 318 | } 319 | 320 | static int lua_qg_camera_destroy(lua_State* L) { 321 | QGCamera2D* camera = getQGCamera(L); 322 | QuickGame_Destroy(&camera); 323 | 324 | return 0; 325 | } 326 | 327 | static int lua_qg_camera_set_pos(lua_State* L) { 328 | int argc = lua_gettop(L); 329 | if (argc != 3) 330 | return luaL_error(L, "Error: Camera:set_position() takes 3 arguments."); 331 | 332 | QGCamera2D* camera = getQGCamera(L); 333 | camera->position.x = luaL_checknumber(L, 2); 334 | camera->position.y = luaL_checknumber(L, 3); 335 | 336 | return 0; 337 | } 338 | 339 | static int lua_qg_camera_set_rot(lua_State* L) { 340 | int argc = lua_gettop(L); 341 | if (argc != 2) 342 | return luaL_error(L, "Error: Camera:set_rotation() takes 2 arguments."); 343 | 344 | QGCamera2D* camera = getQGCamera(L); 345 | camera->rotation = luaL_checknumber(L, 2) / 180.0f * 3.14159f; 346 | 347 | return 0; 348 | } 349 | 350 | 351 | static const luaL_Reg cameraLib[] = { // Timer methods 352 | {"create", lua_qg_camera_create}, 353 | {"destroy", lua_qg_camera_destroy}, 354 | {"set_rotation", lua_qg_camera_set_rot}, 355 | {"set_position", lua_qg_camera_set_pos}, 356 | {0,0} 357 | }; 358 | 359 | static const luaL_Reg cameraMetaLib[] = { 360 | {"__gc", lua_qg_camera_destroy}, 361 | {0,0} 362 | }; 363 | 364 | void initialize_camera(lua_State* L){ 365 | int lib_id, meta_id; 366 | 367 | // new class = {} 368 | lua_createtable(L, 0, 0); 369 | lib_id = lua_gettop(L); 370 | 371 | // meta table = {} 372 | luaL_newmetatable(L, "Camera"); 373 | meta_id = lua_gettop(L); 374 | luaL_setfuncs(L, cameraMetaLib, 0); 375 | 376 | // meta table = methods 377 | luaL_newlib(L, cameraLib); 378 | lua_setfield(L, meta_id, "__index"); 379 | 380 | // meta table.metatable = metatable 381 | luaL_newlib(L, cameraMetaLib); 382 | lua_setfield(L, meta_id, "__metatable"); 383 | 384 | // class.metatable = metatable 385 | lua_setmetatable(L, lib_id); 386 | 387 | // Camera 388 | lua_setglobal(L, "Camera"); 389 | } 390 | 391 | 392 | static int lua_qg_transform_create(lua_State* L) { 393 | int argc = lua_gettop(L); 394 | if (argc != 0) 395 | return luaL_error(L, "Error: Transform.create() takes 0 arguments."); 396 | 397 | QGTransform2D* t = lua_newuserdata(L,sizeof(QGTransform2D)); 398 | t->position.x = 0; 399 | t->position.y = 0; 400 | t->rotation = 0.0f; 401 | t->scale.x = 0; 402 | t->scale.y = 0; 403 | 404 | luaL_getmetatable(L, "Transform"); 405 | lua_setmetatable(L, -2); 406 | 407 | return 1; 408 | } 409 | 410 | static int lua_qg_transform_destroy(lua_State* L) { 411 | QGTransform2D* t = getQGTransform(L); 412 | QuickGame_Destroy(&t); 413 | 414 | return 0; 415 | } 416 | 417 | static int lua_qg_transform_set_position(lua_State* L) { 418 | int argc = lua_gettop(L); 419 | if (argc != 3) 420 | return luaL_error(L, "Error: Transform:set_position() takes 3 arguments."); 421 | 422 | QGTransform2D* t = getQGTransform(L); 423 | t->position.x = luaL_checknumber(L, 2); 424 | t->position.y = luaL_checknumber(L, 3); 425 | 426 | return 0; 427 | } 428 | 429 | static int lua_qg_transform_set_scale(lua_State* L) { 430 | int argc = lua_gettop(L); 431 | if (argc != 3) 432 | return luaL_error(L, "Error: Transform:set_scale() takes 3 arguments."); 433 | 434 | QGTransform2D* t = getQGTransform(L); 435 | t->scale.x = luaL_checknumber(L, 2); 436 | t->scale.y = luaL_checknumber(L, 3); 437 | 438 | return 0; 439 | } 440 | 441 | static int lua_qg_transform_set_rotation(lua_State* L) { 442 | int argc = lua_gettop(L); 443 | if (argc != 2) 444 | return luaL_error(L, "Error: Transform:set_rotation() takes 2 arguments."); 445 | 446 | QGTransform2D* t = getQGTransform(L); 447 | t->rotation = luaL_checknumber(L, 2) / 180.0f * 3.14159f; 448 | 449 | return 0; 450 | } 451 | static int lua_qg_transform_intersects(lua_State* L) { 452 | int argc = lua_gettop(L); 453 | if (argc != 2) 454 | return luaL_error(L, "Error: Transform:intersects() takes 2 arguments."); 455 | 456 | QGTransform2D* t1 = getQGTransformn(L, 1); 457 | QGTransform2D* t2 = getQGTransformn(L, 2); 458 | 459 | bool b = QuickGame_Intersect_Transform(*t1, *t2); 460 | 461 | lua_pushboolean(L, b); 462 | return 1; 463 | } 464 | 465 | static const luaL_Reg transformLib[] = { // Timer methods 466 | {"create", lua_qg_transform_create}, 467 | {"destroy", lua_qg_transform_destroy}, 468 | {"set_rotation", lua_qg_transform_set_rotation}, 469 | {"set_position", lua_qg_transform_set_position}, 470 | {"set_scale", lua_qg_transform_set_scale}, 471 | {"intersects", lua_qg_transform_intersects}, 472 | {0,0} 473 | }; 474 | 475 | static const luaL_Reg transformMetaLib[] = { 476 | {"__gc", lua_qg_transform_destroy}, 477 | {0,0} 478 | }; 479 | 480 | void initialize_transform(lua_State* L){ 481 | int lib_id, meta_id; 482 | 483 | // new class = {} 484 | lua_createtable(L, 0, 0); 485 | lib_id = lua_gettop(L); 486 | 487 | // meta table = {} 488 | luaL_newmetatable(L, "Transform"); 489 | meta_id = lua_gettop(L); 490 | luaL_setfuncs(L, transformMetaLib, 0); 491 | 492 | // meta table = methods 493 | luaL_newlib(L, transformLib); 494 | lua_setfield(L, meta_id, "__index"); 495 | 496 | // meta table.metatable = metatable 497 | luaL_newlib(L, transformMetaLib); 498 | lua_setfield(L, meta_id, "__metatable"); 499 | 500 | // class.metatable = metatable 501 | lua_setmetatable(L, lib_id); 502 | 503 | // Transform 504 | lua_setglobal(L, "Transform"); 505 | } -------------------------------------------------------------------------------- /interpreter/graphics.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef GRAPHICS_INCLUDED_H 10 | #define GRAPHICS_INCLUDED_H 11 | 12 | void initialize_graphics(lua_State* L); 13 | void initialize_primitive(lua_State* L); 14 | void initialize_camera(lua_State* L); 15 | void initialize_transform(lua_State* L); 16 | 17 | #endif -------------------------------------------------------------------------------- /interpreter/input.c: -------------------------------------------------------------------------------- 1 | #include "input.h" 2 | 3 | static int lua_qg_input_update(lua_State* L) { 4 | int argc = lua_gettop(L); 5 | if (argc != 0) 6 | return luaL_error(L, "Error: Input.update() takes 0 arguments."); 7 | 8 | QuickGame_Input_Update(); 9 | 10 | return 0; 11 | } 12 | 13 | static int lua_qg_button_pressed(lua_State* L) { 14 | int argc = lua_gettop(L); 15 | if (argc != 1) 16 | return luaL_error(L, "Error: Input.button_pressed() takes 1 arguments."); 17 | 18 | int button = luaL_checkinteger(L, 1); 19 | bool b = QuickGame_Button_Pressed(button); 20 | 21 | lua_pushboolean(L, b); 22 | return 1; 23 | } 24 | 25 | static int lua_qg_button_released(lua_State* L) { 26 | int argc = lua_gettop(L); 27 | if (argc != 1) 28 | return luaL_error(L, "Error: Input.button_released() takes 1 arguments."); 29 | 30 | int button = luaL_checkinteger(L, 1); 31 | bool b = QuickGame_Button_Released(button); 32 | 33 | lua_pushboolean(L, b); 34 | return 1; 35 | } 36 | 37 | static int lua_qg_button_held(lua_State* L) { 38 | int argc = lua_gettop(L); 39 | if (argc != 1) 40 | return luaL_error(L, "Error: Input.button_held() takes 1 arguments."); 41 | 42 | int button = luaL_checkinteger(L, 1); 43 | bool b = QuickGame_Button_Held(button); 44 | 45 | lua_pushboolean(L, b); 46 | return 1; 47 | } 48 | 49 | static int lua_qg_analogx(lua_State* L) { 50 | int argc = lua_gettop(L); 51 | if (argc != 0) 52 | return luaL_error(L, "Error: Input.analog_x() takes 0 arguments."); 53 | 54 | f32 x = QuickGame_Analog_X(); 55 | 56 | lua_pushnumber(L, x); 57 | return 1; 58 | } 59 | 60 | static int lua_qg_analogy(lua_State* L) { 61 | int argc = lua_gettop(L); 62 | if (argc != 0) 63 | return luaL_error(L, "Error: Input.analog_y() takes 0 arguments."); 64 | 65 | f32 y = QuickGame_Analog_Y(); 66 | 67 | lua_pushnumber(L, y); 68 | return 1; 69 | } 70 | 71 | static const luaL_Reg inputLib[] = { 72 | {"update", lua_qg_input_update}, 73 | {"button_pressed", lua_qg_button_pressed}, 74 | {"button_released", lua_qg_button_released}, 75 | {"button_held", lua_qg_button_held}, 76 | {"analog_x", lua_qg_analogx}, 77 | {"analog_y", lua_qg_analogy}, 78 | {0, 0} 79 | }; 80 | 81 | void initialize_input(lua_State* L) { 82 | lua_getglobal(L, "Input"); 83 | 84 | if(lua_isnil(L, -1)){ 85 | lua_pop(L, 1); 86 | lua_newtable(L); 87 | } 88 | 89 | luaL_setfuncs(L, inputLib, 0); 90 | lua_setglobal(L, "Input"); 91 | 92 | lua_pushinteger(L, PSP_CTRL_CROSS); 93 | lua_setglobal(L, "PSP_CROSS"); 94 | lua_pushinteger(L, PSP_CTRL_CIRCLE); 95 | lua_setglobal(L, "PSP_CIRCLE"); 96 | lua_pushinteger(L, PSP_CTRL_SQUARE); 97 | lua_setglobal(L, "PSP_SQUARE"); 98 | lua_pushinteger(L, PSP_CTRL_TRIANGLE); 99 | lua_setglobal(L, "PSP_TRIANGLE"); 100 | 101 | lua_pushinteger(L, PSP_CTRL_LEFT); 102 | lua_setglobal(L, "PSP_LEFT"); 103 | lua_pushinteger(L, PSP_CTRL_RIGHT); 104 | lua_setglobal(L, "PSP_RIGHT"); 105 | lua_pushinteger(L, PSP_CTRL_UP); 106 | lua_setglobal(L, "PSP_UP"); 107 | lua_pushinteger(L, PSP_CTRL_DOWN); 108 | lua_setglobal(L, "PSP_DOWN"); 109 | 110 | 111 | lua_pushinteger(L, PSP_CTRL_LTRIGGER); 112 | lua_setglobal(L, "PSP_LTRIGGER"); 113 | lua_pushinteger(L, PSP_CTRL_RTRIGGER); 114 | lua_setglobal(L, "PSP_RTRIGGER"); 115 | lua_pushinteger(L, PSP_CTRL_SELECT); 116 | lua_setglobal(L, "PSP_SELECT"); 117 | lua_pushinteger(L, PSP_CTRL_START); 118 | lua_setglobal(L, "PSP_START"); 119 | 120 | } -------------------------------------------------------------------------------- /interpreter/input.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef INPUT_INCLUDED_H 10 | #define INPUT_INCLUDED_H 11 | 12 | void initialize_input(lua_State* L); 13 | 14 | #endif -------------------------------------------------------------------------------- /interpreter/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.c 3 | * @author Nathan Bourgeois (iridescentrosesfall@gmail.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-10-03 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | #include 12 | #include "graphics.h" 13 | #include "input.h" 14 | #include "audio.h" 15 | #include "sprite.h" 16 | #include 17 | 18 | #define RAM_BLOCK 1024 19 | u32 ramAvailableMax(void) 20 | { 21 | u32 size, sizeblock; 22 | u8* ram; 23 | 24 | 25 | // Init variables 26 | size = 0; 27 | sizeblock = RAM_BLOCK; 28 | 29 | // Check loop 30 | while (sizeblock) 31 | { 32 | // Increment size 33 | size += sizeblock; 34 | 35 | // Allocate ram 36 | ram = (u8*)malloc(size); 37 | 38 | // Check allocate 39 | if (!(ram)) 40 | { 41 | // Restore old size 42 | size -= sizeblock; 43 | 44 | // Size block / 2 45 | sizeblock >>= 1; 46 | } 47 | else 48 | free(ram); 49 | } 50 | 51 | return size; 52 | } 53 | 54 | static int lua_memfree(lua_State* L) { 55 | lua_pushinteger(L, ramAvailableMax()); 56 | return 1; 57 | } 58 | 59 | static int lua_print(lua_State* L) { 60 | pspDebugScreenInit(); 61 | pspDebugScreenSetXY(0, 0); 62 | 63 | int argc = lua_gettop(L); 64 | int n; 65 | for (n = 1; n <= argc; n++) pspDebugScreenPrintf("%s\n", lua_tostring(L, n)); 66 | return 0; 67 | } 68 | 69 | lua_State *L; 70 | 71 | static int lua_qg_running(lua_State* L) { 72 | int argc = lua_gettop(L); 73 | if (argc != 0) 74 | return luaL_error(L, "Error: QuickGame.running() takes 0 arguments."); 75 | 76 | lua_pushboolean(L, QuickGame_Running()); 77 | return 1; 78 | } 79 | 80 | static int lua_qg_request_exit(lua_State* L) { 81 | int argc = lua_gettop(L); 82 | if (argc != 0) 83 | return luaL_error(L, "Error: QuickGame.request_exit() takes 0 arguments."); 84 | 85 | QuickGame_Request_Exit(); 86 | return 0; 87 | } 88 | 89 | static const luaL_Reg quickgameLib[] = { 90 | {"running", lua_qg_running}, 91 | {"request_exit", lua_qg_request_exit}, 92 | {0, 0} 93 | }; 94 | 95 | /** 96 | * @brief QuickGame API Functions 97 | * 98 | * @param L lua_State 99 | */ 100 | void initialize_quickgame(lua_State* L){ 101 | lua_getglobal(L, "QuickGame"); 102 | 103 | if(lua_isnil(L, -1)){ 104 | lua_pop(L, 1); 105 | lua_newtable(L); 106 | } 107 | 108 | luaL_setfuncs(L, quickgameLib, 0); 109 | lua_setglobal(L, "QuickGame"); 110 | } 111 | 112 | static int lua_qg_timer_create(lua_State* L) { 113 | int argc = lua_gettop(L); 114 | if (argc != 0) 115 | return luaL_error(L, "Error: Timer.create() takes 0 arguments."); 116 | 117 | QGTimer* timer = lua_newuserdata(L,sizeof(QGTimer)); 118 | QuickGame_Timer_Start(timer); 119 | 120 | luaL_getmetatable(L, "Timer"); 121 | lua_setmetatable(L, -2); 122 | 123 | return 1; 124 | } 125 | 126 | QGTimer* getQGTimer(lua_State* L){ 127 | return (QGTimer*)luaL_checkudata(L, 1, "Timer"); 128 | } 129 | 130 | static int lua_qg_timer_destroy(lua_State* L) { 131 | QGTimer* timer = getQGTimer(L); 132 | QuickGame_Destroy(&timer); 133 | 134 | return 0; 135 | } 136 | 137 | static int lua_qg_timer_delta(lua_State* L) { 138 | int argc = lua_gettop(L); 139 | if (argc != 1) 140 | return luaL_error(L, "Error: Timer:delta() takes 1 argument."); 141 | 142 | QGTimer* timer = getQGTimer(L); 143 | f32 delta = QuickGame_Timer_Delta(timer); 144 | 145 | lua_pushnumber(L, delta); 146 | return 1; 147 | } 148 | 149 | static int lua_qg_timer_elapsed(lua_State* L) { 150 | int argc = lua_gettop(L); 151 | if (argc != 1) 152 | return luaL_error(L, "Error: Timer:elapsed() takes 1 argument."); 153 | 154 | 155 | QGTimer* timer = getQGTimer(L); 156 | f32 elapsed = QuickGame_Timer_Elapsed(timer); 157 | 158 | lua_pushnumber(L, elapsed); 159 | return 1; 160 | } 161 | 162 | static int lua_qg_timer_reset(lua_State* L) { 163 | int argc = lua_gettop(L); 164 | if (argc != 1) 165 | return luaL_error(L, "Error: Timer:reset() takes 1 argument."); 166 | 167 | 168 | QGTimer* timer = getQGTimer(L); 169 | QuickGame_Timer_Reset(timer); 170 | 171 | return 0; 172 | } 173 | 174 | static const luaL_Reg timerLib[] = { // Timer methods 175 | {"create", lua_qg_timer_create}, 176 | {"destroy", lua_qg_timer_destroy}, 177 | {"delta", lua_qg_timer_delta}, 178 | {"reset", lua_qg_timer_reset}, 179 | {"elapsed", lua_qg_timer_elapsed}, 180 | {0,0} 181 | }; 182 | 183 | static const luaL_Reg timerMetaLib[] = { 184 | {"__gc", lua_qg_timer_destroy}, 185 | {0,0} 186 | }; 187 | 188 | void initialize_timer(lua_State* L) { 189 | int lib_id, meta_id; 190 | 191 | // new class = {} 192 | lua_createtable(L, 0, 0); 193 | lib_id = lua_gettop(L); 194 | 195 | // meta table = {} 196 | luaL_newmetatable(L, "Timer"); 197 | meta_id = lua_gettop(L); 198 | luaL_setfuncs(L, timerMetaLib, 0); 199 | 200 | // meta table = methods 201 | luaL_newlib(L, timerLib); 202 | lua_setfield(L, meta_id, "__index"); 203 | 204 | // meta table.metatable = metatable 205 | luaL_newlib(L, timerMetaLib); 206 | lua_setfield(L, meta_id, "__metatable"); 207 | 208 | // class.metatable = metatable 209 | lua_setmetatable(L, lib_id); 210 | 211 | // Timer 212 | lua_setglobal(L, "Timer"); 213 | } 214 | 215 | /** 216 | * @brief Initialize Lua 217 | * 218 | */ 219 | void qg_lua_init() { 220 | L = luaL_newstate(); 221 | luaL_openlibs(L); 222 | lua_register(L, "print", lua_print); 223 | lua_register(L, "memoryfree", lua_memfree); 224 | 225 | //QuickGame Lib 226 | initialize_quickgame(L); 227 | 228 | //Graphics Lib 229 | initialize_graphics(L); 230 | 231 | //Camera Object 232 | initialize_camera(L); 233 | 234 | //Primitive Lib 235 | initialize_primitive(L); 236 | 237 | //Input Lib 238 | initialize_input(L); 239 | 240 | //Timer Object 241 | initialize_timer(L); 242 | 243 | //AudioClip Object 244 | initialize_audio(L); 245 | 246 | //Texture Object 247 | initialize_texture(L); 248 | 249 | //Sprite Object 250 | initialize_sprite(L); 251 | 252 | //Transform Object 253 | initialize_transform(L); 254 | 255 | //Tilemap Object 256 | initialize_tilemap(L); 257 | } 258 | 259 | /** 260 | * @brief Run Lua 261 | * 262 | * @return int Returns Status code 263 | */ 264 | int qg_lua_run() { 265 | qg_lua_start: 266 | 267 | // Load file 268 | int ret_stat = luaL_loadfile(L, "script.lua"); 269 | 270 | // Failure 271 | if(ret_stat != 0) { 272 | lua_close(L); 273 | QuickGame_Terminate(); 274 | return 1; 275 | } 276 | 277 | // Run Script 278 | ret_stat = lua_pcall(L, 0, LUA_MULTRET, 0); 279 | 280 | // Error! 281 | if(ret_stat != 0){ 282 | pspDebugScreenInit(); 283 | pspDebugScreenEnableBackColor(0); 284 | 285 | while(QuickGame_Running()){ 286 | pspDebugScreenSetXY(0,0); 287 | 288 | pspDebugScreenPrintf("Lua Error:\n%s\n", lua_tostring(L, -1)); 289 | pspDebugScreenPrintf("Press Start to reset.\n"); 290 | 291 | QuickGame_Input_Update(); 292 | 293 | if(QuickGame_Button_Pressed(PSP_CTRL_START)) 294 | goto qg_lua_start; 295 | } 296 | } 297 | 298 | // End Game 299 | lua_close(L); 300 | return ret_stat; 301 | } 302 | 303 | int main() { 304 | // Init engine 305 | if(QuickGame_Init() < 0) 306 | return 1; 307 | 308 | // Set Graphics 2D 309 | QuickGame_Graphics_Set2D(); 310 | 311 | // Initialize Lua 312 | qg_lua_init(); 313 | 314 | // Run Lua Script 315 | int r = qg_lua_run(); 316 | if(r != 0) 317 | return r; 318 | 319 | // Terminate engine 320 | QuickGame_Terminate(); 321 | return 0; 322 | } -------------------------------------------------------------------------------- /interpreter/sprite.c: -------------------------------------------------------------------------------- 1 | #include "sprite.h" 2 | 3 | static int lua_qg_texture_load(lua_State* L) { 4 | int argc = lua_gettop(L); 5 | if (argc != 3) 6 | return luaL_error(L, "Error: Texture.load() takes 3 arguments."); 7 | 8 | QGTexture_t* texture = lua_newuserdata(L,sizeof(QGTexture_t)); 9 | 10 | const char* filename = luaL_checkstring(L, 1); 11 | bool flip = luaL_checkint(L, 2); 12 | bool vram = luaL_checkint(L, 3); 13 | 14 | *texture = QuickGame_Texture_Load(filename, flip, vram); 15 | 16 | luaL_getmetatable(L, "Texture"); 17 | lua_setmetatable(L, -2); 18 | 19 | return 1; 20 | } 21 | 22 | QGTexture_t* getTex(lua_State* L){ 23 | return (QGTexture_t*)luaL_checkudata(L, 1, "Texture"); 24 | } 25 | QGTexture_t* getTexn(lua_State* L, int n){ 26 | return (QGTexture_t*)luaL_checkudata(L, n, "Texture"); 27 | } 28 | 29 | static int lua_qg_texture_destroy(lua_State* L) { 30 | QGTexture_t* tex = getTex(L); 31 | QuickGame_Destroy(tex); 32 | 33 | return 0; 34 | } 35 | 36 | 37 | static int lua_qg_texture_bind(lua_State* L) { 38 | int argc = lua_gettop(L); 39 | if (argc != 1) 40 | return luaL_error(L, "Error: Texture:bind() takes 1 arguments."); 41 | 42 | QGTexture_t texture = *getTex(L); 43 | 44 | QuickGame_Texture_Bind(texture); 45 | 46 | return 0; 47 | } 48 | 49 | static int lua_qg_texture_unbind(lua_State* L) { 50 | int argc = lua_gettop(L); 51 | if (argc != 1) 52 | return luaL_error(L, "Error: Texture:unbind() takes 1 arguments."); 53 | 54 | QGTexture_t texture = *getTex(L); 55 | 56 | QuickGame_Texture_Unbind(); 57 | 58 | return 0; 59 | } 60 | 61 | static const luaL_Reg textureLib[] = { 62 | {"load", lua_qg_texture_load}, 63 | {"destroy", lua_qg_texture_destroy}, 64 | {"bind", lua_qg_texture_bind}, 65 | {"unbind", lua_qg_texture_unbind}, 66 | {0,0} 67 | }; 68 | 69 | static const luaL_Reg textureMetaLib[] = { 70 | {"__gc", lua_qg_texture_destroy}, 71 | {0,0} 72 | }; 73 | 74 | void initialize_texture(lua_State* L) { 75 | int lib_id, meta_id; 76 | 77 | // new class = {} 78 | lua_createtable(L, 0, 0); 79 | lib_id = lua_gettop(L); 80 | 81 | // meta table = {} 82 | luaL_newmetatable(L, "Texture"); 83 | meta_id = lua_gettop(L); 84 | luaL_setfuncs(L, textureMetaLib, 0); 85 | 86 | // meta table = methods 87 | luaL_newlib(L, textureLib); 88 | lua_setfield(L, meta_id, "__index"); 89 | 90 | // meta table.metatable = metatable 91 | luaL_newlib(L, textureMetaLib); 92 | lua_setfield(L, meta_id, "__metatable"); 93 | 94 | // class.metatable = metatable 95 | lua_setmetatable(L, lib_id); 96 | 97 | // AudioClip 98 | lua_setglobal(L, "Texture"); 99 | } 100 | 101 | static int lua_qg_sprite_create(lua_State* L) { 102 | int argc = lua_gettop(L); 103 | if (argc != 5) 104 | return luaL_error(L, "Error: Sprite.create() takes 5 arguments."); 105 | 106 | QGSprite_t* sprite = lua_newuserdata(L,sizeof(QGSprite_t)); 107 | 108 | int x = luaL_checkint(L, 1); 109 | int y = luaL_checkint(L, 2); 110 | int w = luaL_checkint(L, 3); 111 | int h = luaL_checkint(L, 4); 112 | 113 | QGTexture_t tex = *getTexn(L,5); 114 | 115 | *sprite = QuickGame_Sprite_Create_Alt(x, y, w, h, tex); 116 | 117 | luaL_getmetatable(L, "Sprite"); 118 | lua_setmetatable(L, -2); 119 | 120 | return 1; 121 | } 122 | 123 | QGSprite_t* getSprite(lua_State* L){ 124 | return (QGSprite_t*)luaL_checkudata(L, 1, "Sprite"); 125 | } 126 | 127 | QGSprite_t* getSpriten(lua_State* L, int n){ 128 | return (QGSprite_t*)luaL_checkudata(L, n, "Sprite"); 129 | } 130 | 131 | static int lua_qg_sprite_destroy(lua_State* L) { 132 | QGSprite_t* sprite = getSprite(L); 133 | QuickGame_Sprite_Destroy(sprite); 134 | 135 | return 0; 136 | } 137 | 138 | static int lua_qg_sprite_draw(lua_State* L) { 139 | int argc = lua_gettop(L); 140 | if (argc != 1) 141 | return luaL_error(L, "Error: Sprite:draw() takes 1 arguments."); 142 | 143 | QGSprite_t sprite = *getSprite(L); 144 | 145 | QuickGame_Sprite_Draw(sprite); 146 | 147 | return 0; 148 | } 149 | 150 | static int lua_qg_sprite_intersects(lua_State* L) { 151 | int argc = lua_gettop(L); 152 | if (argc != 2) 153 | return luaL_error(L, "Error: Sprite:intersects() takes 2 arguments."); 154 | 155 | QGSprite_t sprite1 = *getSpriten(L,1); 156 | QGSprite_t sprite2 = *getSpriten(L, 2); 157 | 158 | bool i = QuickGame_Sprite_Intersects(sprite1, sprite2); 159 | 160 | lua_pushboolean(L, i); 161 | return 1; 162 | } 163 | 164 | 165 | static int lua_qg_sprite_set_position(lua_State* L) { 166 | int argc = lua_gettop(L); 167 | if (argc != 3) 168 | return luaL_error(L, "Error: Sprite:set_position() takes 3 arguments."); 169 | 170 | QGSprite_t sprite1 = *getSpriten(L,1); 171 | int x = luaL_checkint(L, 2); 172 | int y = luaL_checkint(L, 3); 173 | 174 | sprite1->transform.position.x = x; 175 | sprite1->transform.position.y = y; 176 | 177 | return 0; 178 | } 179 | static int lua_qg_sprite_set_scale(lua_State* L) { 180 | int argc = lua_gettop(L); 181 | if (argc != 3) 182 | return luaL_error(L, "Error: Sprite:set_scale() takes 3 arguments."); 183 | 184 | QGSprite_t sprite1 = *getSpriten(L,1); 185 | int w = luaL_checkint(L, 2); 186 | int h = luaL_checkint(L, 3); 187 | 188 | sprite1->transform.scale.x = w; 189 | sprite1->transform.scale.y = h; 190 | 191 | return 0; 192 | } 193 | 194 | static int lua_qg_sprite_set_rotation(lua_State* L) { 195 | int argc = lua_gettop(L); 196 | if (argc != 2) 197 | return luaL_error(L, "Error: Sprite:set_rotation() takes 2 arguments."); 198 | 199 | QGSprite_t sprite1 = *getSpriten(L,1); 200 | f32 rot = luaL_checknumber(L, 2); 201 | 202 | sprite1->transform.rotation = rot; 203 | 204 | return 0; 205 | } 206 | 207 | static int lua_qg_sprite_set_layer(lua_State* L) { 208 | int argc = lua_gettop(L); 209 | if (argc != 2) 210 | return luaL_error(L, "Error: Sprite:set_layer() takes 2 arguments."); 211 | 212 | QGSprite_t sprite1 = *getSpriten(L,1); 213 | int layer = luaL_checkint(L, 2); 214 | 215 | sprite1->layer = layer; 216 | 217 | return 0; 218 | } 219 | 220 | 221 | static int lua_qg_sprite_set_color(lua_State* L) { 222 | int argc = lua_gettop(L); 223 | if (argc != 2) 224 | return luaL_error(L, "Error: Sprite:set_color() takes 2 arguments."); 225 | 226 | QGSprite_t sprite1 = *getSpriten(L,1); 227 | int color = luaL_checkint(L, 2); 228 | 229 | sprite1->color.color = color; 230 | 231 | return 0; 232 | } 233 | 234 | 235 | static const luaL_Reg spriteLib[] = { 236 | {"create", lua_qg_sprite_create}, 237 | {"destroy", lua_qg_sprite_destroy}, 238 | {"draw", lua_qg_sprite_draw}, 239 | {"intersects", lua_qg_sprite_intersects}, 240 | {"set_position", lua_qg_sprite_set_position}, 241 | {"set_rotation", lua_qg_sprite_set_rotation}, 242 | {"set_scale", lua_qg_sprite_set_scale}, 243 | {"set_layer", lua_qg_sprite_set_layer}, 244 | {"set_color", lua_qg_sprite_set_color}, 245 | {0,0} 246 | }; 247 | 248 | static const luaL_Reg spriteMetaLib[] = { 249 | {"__gc", lua_qg_sprite_destroy}, 250 | {0,0} 251 | }; 252 | 253 | void initialize_sprite(lua_State* L) { 254 | int lib_id, meta_id; 255 | 256 | // new class = {} 257 | lua_createtable(L, 0, 0); 258 | lib_id = lua_gettop(L); 259 | 260 | // meta table = {} 261 | luaL_newmetatable(L, "Sprite"); 262 | meta_id = lua_gettop(L); 263 | luaL_setfuncs(L, spriteMetaLib, 0); 264 | 265 | // meta table = methods 266 | luaL_newlib(L, spriteLib); 267 | lua_setfield(L, meta_id, "__index"); 268 | 269 | // meta table.metatable = metatable 270 | luaL_newlib(L, spriteMetaLib); 271 | lua_setfield(L, meta_id, "__metatable"); 272 | 273 | // class.metatable = metatable 274 | lua_setmetatable(L, lib_id); 275 | 276 | // AudioClip 277 | lua_setglobal(L, "Sprite"); 278 | } 279 | 280 | //////// 281 | static int lua_qg_tilemap_create(lua_State* L) { 282 | int argc = lua_gettop(L); 283 | if (argc != 5) 284 | return luaL_error(L, "Error: Tilemap.create() takes 5 arguments."); 285 | 286 | QGTilemap_t* tmap = lua_newuserdata(L,sizeof(QGTilemap_t)); 287 | 288 | int x = luaL_checkint(L, 1); 289 | int y = luaL_checkint(L, 2); 290 | QGTexture_t tex = *getTexn(L,3); 291 | int w = luaL_checkint(L, 4); 292 | int h = luaL_checkint(L, 5); 293 | 294 | QGVector2 size = { 295 | .x = w, 296 | .y = h 297 | }; 298 | 299 | QGTextureAtlas atlas = { 300 | .x = x, 301 | .y = y 302 | }; 303 | 304 | *tmap = QuickGame_Tilemap_Create(atlas, tex, size); 305 | 306 | luaL_getmetatable(L, "Tilemap"); 307 | lua_setmetatable(L, -2); 308 | 309 | return 1; 310 | } 311 | 312 | QGTilemap_t* getTilemap(lua_State* L){ 313 | return (QGTilemap_t*)luaL_checkudata(L, 1, "Tilemap"); 314 | } 315 | 316 | static int lua_qg_tilemap_destroy(lua_State* L) { 317 | QGTilemap_t* tilemap = getTilemap(L); 318 | QuickGame_Tilemap_Destroy(tilemap); 319 | 320 | return 0; 321 | } 322 | 323 | static int lua_qg_tilemap_draw(lua_State* L) { 324 | int argc = lua_gettop(L); 325 | if (argc != 1) 326 | return luaL_error(L, "Error: Tilemap:draw() takes 1 arguments."); 327 | 328 | QGTilemap_t tilemap = *getTilemap(L); 329 | 330 | QuickGame_Tilemap_Draw(tilemap); 331 | 332 | return 0; 333 | } 334 | 335 | static int lua_qg_tilemap_build(lua_State* L) { 336 | int argc = lua_gettop(L); 337 | if (argc != 1) 338 | return luaL_error(L, "Error: Tilemap:build() takes 1 arguments."); 339 | 340 | QGTilemap_t tilemap = *getTilemap(L); 341 | 342 | QuickGame_Tilemap_Build(tilemap); 343 | 344 | return 0; 345 | } 346 | 347 | static int lua_qg_tilemap_intersects(lua_State* L) { 348 | int argc = lua_gettop(L); 349 | if (argc != 2) 350 | return luaL_error(L, "Error: Tilemap:intersects() takes 2 arguments."); 351 | 352 | QGTilemap_t tilemap = *getTilemap(L); 353 | QGSprite_t sprite2 = *getSpriten(L, 2); 354 | 355 | bool i = QuickGame_Tilemap_Intersects(tilemap, sprite2->transform); 356 | 357 | lua_pushboolean(L, i); 358 | return 1; 359 | } 360 | 361 | 362 | static int lua_qg_tilemap_draw_string(lua_State* L) { 363 | int argc = lua_gettop(L); 364 | if (argc != 4) 365 | return luaL_error(L, "Error: Tilemap:draw_string() takes 4 arguments."); 366 | 367 | QGTilemap_t tilemap = *getTilemap(L); 368 | int x = luaL_checkint(L, 2); 369 | int y = luaL_checkint(L, 3); 370 | const char* str = luaL_checkstring(L, 4); 371 | 372 | QGVector2 pos = { 373 | .x = x, 374 | .y = y 375 | }; 376 | 377 | QuickGame_Tilemap_Draw_String(tilemap, str, pos); 378 | 379 | return 0; 380 | } 381 | 382 | 383 | static int lua_qg_tilemap_set_tile(lua_State* L) { 384 | int argc = lua_gettop(L); 385 | if (argc != 9) 386 | return luaL_error(L, "Error: Tilemap:set_tile() takes 9 arguments."); 387 | 388 | QGTilemap_t tilemap = *getTilemap(L); 389 | int idx = luaL_checkint(L, 2); 390 | int x = luaL_checkint(L, 3); 391 | int y = luaL_checkint(L, 4); 392 | int w = luaL_checkint(L, 5); 393 | int h = luaL_checkint(L, 6); 394 | int aidx = luaL_checkint(L, 7); 395 | int color = luaL_checkint(L, 8); 396 | int collide = luaL_checkint(L, 9); 397 | 398 | QGTile t = { 399 | .position = {.x = x, .y = y}, 400 | .scale = {.x = w, .y = h}, 401 | .atlas_idx = aidx, 402 | .color.color = color, 403 | .collide = collide 404 | }; 405 | 406 | tilemap->tile_array[idx] = t; 407 | 408 | return 0; 409 | } 410 | 411 | static int lua_qg_tilemap_set_position(lua_State* L) { 412 | int argc = lua_gettop(L); 413 | if (argc != 3) 414 | return luaL_error(L, "Error: Tilemap:set_position() takes 3 arguments."); 415 | 416 | QGTilemap_t tilemap = *getTilemap(L); 417 | int x = luaL_checkint(L, 2); 418 | int y = luaL_checkint(L, 3); 419 | 420 | tilemap->transform.position.x = x; 421 | tilemap->transform.position.y = y; 422 | 423 | return 0; 424 | } 425 | static int lua_qg_tilemap_set_scale(lua_State* L) { 426 | int argc = lua_gettop(L); 427 | if (argc != 3) 428 | return luaL_error(L, "Error: Tilemap:set_scale() takes 3 arguments."); 429 | 430 | QGTilemap_t tilemap = *getTilemap(L); 431 | int w = luaL_checkint(L, 2); 432 | int h = luaL_checkint(L, 3); 433 | 434 | tilemap->transform.scale.x = w; 435 | tilemap->transform.scale.y = h; 436 | 437 | return 0; 438 | } 439 | 440 | static int lua_qg_tilemap_set_rotation(lua_State* L) { 441 | int argc = lua_gettop(L); 442 | if (argc != 2) 443 | return luaL_error(L, "Error: Tilemap:set_rotation() takes 2 arguments."); 444 | 445 | QGTilemap_t tilemap = *getTilemap(L); 446 | f32 rot = luaL_checknumber(L, 2); 447 | 448 | tilemap->transform.rotation = rot; 449 | 450 | return 0; 451 | } 452 | 453 | static const luaL_Reg tileLib[] = { 454 | {"create", lua_qg_tilemap_create}, 455 | {"destroy", lua_qg_tilemap_destroy}, 456 | {"build", lua_qg_tilemap_build}, 457 | {"draw", lua_qg_tilemap_draw}, 458 | {"draw_string", lua_qg_tilemap_draw_string}, 459 | {"set_tile", lua_qg_tilemap_set_tile}, 460 | {"intersects", lua_qg_tilemap_intersects}, 461 | {"set_position", lua_qg_tilemap_set_position}, 462 | {"set_rotation", lua_qg_tilemap_set_rotation}, 463 | {"set_scale", lua_qg_tilemap_set_scale}, 464 | {0,0} 465 | }; 466 | 467 | static const luaL_Reg tileMetaLib[] = { 468 | {"__gc", lua_qg_tilemap_destroy}, 469 | {0,0} 470 | }; 471 | 472 | void initialize_tilemap(lua_State* L) { 473 | int lib_id, meta_id; 474 | 475 | // new class = {} 476 | lua_createtable(L, 0, 0); 477 | lib_id = lua_gettop(L); 478 | 479 | // meta table = {} 480 | luaL_newmetatable(L, "Tilemap"); 481 | meta_id = lua_gettop(L); 482 | luaL_setfuncs(L, tileMetaLib, 0); 483 | 484 | // meta table = methods 485 | luaL_newlib(L, tileLib); 486 | lua_setfield(L, meta_id, "__index"); 487 | 488 | // meta table.metatable = metatable 489 | luaL_newlib(L, tileMetaLib); 490 | lua_setfield(L, meta_id, "__metatable"); 491 | 492 | // class.metatable = metatable 493 | lua_setmetatable(L, lib_id); 494 | 495 | // AudioClip 496 | lua_setglobal(L, "Tilemap"); 497 | } -------------------------------------------------------------------------------- /interpreter/sprite.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef SPRITE_INCLUDED_H 10 | #define SPRITE_INCLUDED_H 11 | 12 | void initialize_sprite(lua_State* L); 13 | void initialize_texture(lua_State* L); 14 | 15 | void initialize_tilemap(lua_State* L); 16 | 17 | #endif -------------------------------------------------------------------------------- /samples/audio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(audio) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(audio main.cpp) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(audio PUBLIC QuickGame) 12 | target_include_directories(audio PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET audio 16 | TITLE audio 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/audio/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | QuickGame::init(); 5 | QuickGame::Graphics::set2D(); 6 | 7 | QuickGame::Audio::Clip clip("test.wav", false, false); 8 | clip.play(0); 9 | 10 | while(QuickGame::running()){ 11 | QuickGame::Graphics::start_frame(); 12 | QuickGame::Graphics::clear(); 13 | 14 | QuickGame::Graphics::end_frame(true); 15 | } 16 | 17 | QuickGame::terminate(); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /samples/basic-contained/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(basic-contained) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(basic-contained main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(basic-contained PUBLIC QuickGame) 12 | target_include_directories(basic-contained PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET basic-contained 16 | TITLE basic-contained 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/basic-contained/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | if(QuickGame_Init() < 0) 5 | return 1; 6 | 7 | QGTexInfo tex_info = {.filename = "circle.png", .flip = 0, .vram = 0}; 8 | QGSprite_t sprite = QuickGame_Sprite_Create_Contained(240, 136, 256, 256, tex_info); 9 | 10 | QuickGame_Graphics_Set2D(); 11 | 12 | while(QuickGame_Running()){ 13 | QuickGame_Graphics_Start_Frame(); 14 | QuickGame_Graphics_Clear(); 15 | 16 | QuickGame_Sprite_Draw(sprite); 17 | 18 | QuickGame_Graphics_End_Frame(true); 19 | } 20 | QuickGame_Sprite_Destroy(&sprite); 21 | 22 | QuickGame_Terminate(); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /samples/basic-cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(basic) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(basic main.cpp) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(basic PUBLIC QuickGame) 12 | target_include_directories(basic PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET basic 16 | TITLE basic 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/basic-cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | QuickGame::init(); 5 | QuickGame::Graphics::set2D(); 6 | 7 | QuickGame::Graphics::Sprite sprite({240, 136}, {256, 256}, {"circle.png", 0, 0}); 8 | 9 | while(QuickGame::running()){ 10 | QuickGame::Graphics::start_frame(); 11 | QuickGame::Graphics::clear(); 12 | 13 | sprite.draw(); 14 | 15 | QuickGame::Graphics::end_frame(true); 16 | } 17 | 18 | QuickGame::terminate(); 19 | return 0; 20 | } -------------------------------------------------------------------------------- /samples/basic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(basic) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(basic main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(basic PUBLIC QuickGame) 12 | target_include_directories(basic PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET basic 16 | TITLE basic 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/basic/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | if(QuickGame_Init() < 0) 5 | return 1; 6 | 7 | QGTexture_t tex = QuickGame_Texture_Load("circle.png", 0, 0); 8 | QGSprite_t sprite = QuickGame_Sprite_Create_Alt(240, 136, 256, 256, tex); 9 | 10 | QuickGame_Graphics_Set2D(); 11 | 12 | while(QuickGame_Running()){ 13 | QuickGame_Graphics_Start_Frame(); 14 | 15 | QuickGame_Graphics_Clear(); 16 | 17 | QuickGame_Sprite_Draw(sprite); 18 | 19 | QuickGame_Graphics_End_Frame(true); 20 | } 21 | 22 | QuickGame_Sprite_Destroy(&sprite); 23 | QuickGame_Texture_Destroy(&tex); 24 | 25 | QuickGame_Terminate(); 26 | return 0; 27 | } -------------------------------------------------------------------------------- /samples/camera-input/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(camera) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(camera main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(camera PUBLIC QuickGame) 12 | target_include_directories(camera PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET camera 16 | TITLE camera 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/camera-input/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char** argv){ 5 | if(QuickGame_Init() < 0) 6 | return 1; 7 | 8 | QGTexInfo tex_info = {.filename = "circle.png", .flip = 0, .vram = 0}; 9 | QGSprite_t sprite = QuickGame_Sprite_Create_Contained(0, 0, 256, 256, tex_info); 10 | 11 | QGCamera2D camera = { 12 | {.x = 0, .y = 120}, 13 | .rotation = 0.0f, 14 | }; 15 | QuickGame_Graphics_Set_Camera(&camera); 16 | QuickGame_Graphics_Set2D(); 17 | 18 | while(QuickGame_Running()){ 19 | QuickGame_Input_Update(); 20 | 21 | if(QuickGame_Button_Held(PSP_CTRL_UP)) { 22 | camera.position.y += 1.0f; 23 | } 24 | if(QuickGame_Button_Held(PSP_CTRL_DOWN)) { 25 | camera.position.y -= 1.0f; 26 | } 27 | if(QuickGame_Button_Held(PSP_CTRL_LEFT)) { 28 | camera.position.x -= 1.0f; 29 | } 30 | if(QuickGame_Button_Held(PSP_CTRL_RIGHT)) { 31 | camera.position.x += 1.0f; 32 | } 33 | 34 | QuickGame_Graphics_Start_Frame(); 35 | QuickGame_Graphics_Clear(); 36 | 37 | QuickGame_Sprite_Draw(sprite); 38 | 39 | QuickGame_Graphics_End_Frame(true); 40 | } 41 | QuickGame_Sprite_Destroy(&sprite); 42 | 43 | QuickGame_Terminate(); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /samples/collision-basic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(collision-basic) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(collision-basic main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(collision-basic PUBLIC QuickGame) 12 | target_include_directories(collision-basic PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET collision-basic 16 | TITLE collision-basic 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/collision-basic/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char** argv){ 5 | if(QuickGame_Init() < 0) 6 | return 1; 7 | 8 | QGTexInfo tex_info = {.filename = "circle.png", .flip = 0, .vram = 0}; 9 | QGSprite_t sprite = QuickGame_Sprite_Create_Contained(360, 136, 64, 64, tex_info); 10 | 11 | QGTexInfo tex_info2 = {.filename = "container.jpg", .flip = 0, .vram = 0}; 12 | QGSprite_t sprite2 = QuickGame_Sprite_Create_Contained(120, 136, 64, 64, tex_info2); 13 | 14 | QuickGame_Graphics_Set2D(); 15 | 16 | while(QuickGame_Running()){ 17 | QuickGame_Input_Update(); 18 | 19 | if(QuickGame_Button_Held(PSP_CTRL_UP)) { 20 | sprite2->transform.position.y += 1.0f; 21 | } 22 | if(QuickGame_Button_Held(PSP_CTRL_DOWN)) { 23 | sprite2->transform.position.y -= 1.0f; 24 | } 25 | if(QuickGame_Button_Held(PSP_CTRL_LEFT)) { 26 | sprite2->transform.position.x -= 1.0f; 27 | } 28 | if(QuickGame_Button_Held(PSP_CTRL_RIGHT)) { 29 | sprite2->transform.position.x += 1.0f; 30 | } 31 | 32 | if(QuickGame_Sprite_Intersects(sprite, sprite2)) { 33 | sprite2->color.color = 0xFF00FF00; 34 | sprite->color.color = 0xFF0000FF; 35 | } else { 36 | sprite2->color.color = 0xFFFFFFFF; 37 | sprite->color.color = 0xFFFFFFFF; 38 | } 39 | 40 | QuickGame_Graphics_Start_Frame(); 41 | QuickGame_Graphics_Clear(); 42 | 43 | QuickGame_Sprite_Draw(sprite); 44 | QuickGame_Sprite_Draw(sprite2); 45 | 46 | QuickGame_Graphics_End_Frame(true); 47 | } 48 | QuickGame_Sprite_Destroy(&sprite); 49 | 50 | QuickGame_Terminate(); 51 | return 0; 52 | } -------------------------------------------------------------------------------- /samples/lua/audio.lua: -------------------------------------------------------------------------------- 1 | clear_col = Color.create(255, 0, 255, 255) 2 | 3 | Graphics.set_clear_color(clear_col) 4 | 5 | clip = AudioClip.load("test.wav", 0, 0) 6 | clip:play(0) 7 | 8 | while(QuickGame.running()) do 9 | Graphics.start_frame() 10 | Graphics.clear() 11 | 12 | Graphics.end_frame(true) 13 | end -------------------------------------------------------------------------------- /samples/lua/basic.lua: -------------------------------------------------------------------------------- 1 | clear_col = Color.create(32, 32, 32, 255) 2 | Graphics.set_clear_color(clear_col) 3 | 4 | texture = Texture.load("circle.png", 0, 0) 5 | sprite = Sprite.create(240, 136, 256, 256, texture) 6 | 7 | while(QuickGame.running()) do 8 | Graphics.start_frame() 9 | Graphics.clear() 10 | 11 | sprite:draw() 12 | 13 | Graphics.end_frame(true) 14 | end -------------------------------------------------------------------------------- /samples/lua/blank.lua: -------------------------------------------------------------------------------- 1 | clear_col = Color.create(255, 0, 255, 255) 2 | 3 | Graphics.set_clear_color(clear_col) 4 | 5 | while(QuickGame.running()) do 6 | Graphics.start_frame() 7 | Graphics.clear() 8 | 9 | Graphics.end_frame(true) 10 | end -------------------------------------------------------------------------------- /samples/lua/camera.lua: -------------------------------------------------------------------------------- 1 | clear_col = Color.create(32, 32, 32, 255) 2 | 3 | tri_color = Color.create(255, 0, 0, 255) 4 | rect_color = Color.create(0, 255, 0, 255) 5 | circle_color = Color.create(0, 0, 255, 255) 6 | 7 | Graphics.set_clear_color(clear_col) 8 | 9 | timer = Timer.create() 10 | camera = Camera.create() 11 | cx = 0.0 12 | cy = 0.0 13 | cr = 0.0 14 | 15 | Graphics.set_camera(camera) 16 | 17 | while(QuickGame.running()) do 18 | Input.update() 19 | 20 | dt = timer:delta() 21 | 22 | if Input.button_held(PSP_RIGHT) then 23 | cx = cx + dt * 40.0 24 | end 25 | if Input.button_held(PSP_LEFT) then 26 | cx = cx - dt * 40.0 27 | end 28 | 29 | if Input.button_held(PSP_UP) then 30 | cy = cy + dt * 40.0 31 | end 32 | if Input.button_held(PSP_DOWN) then 33 | cy = cy - dt * 40.0 34 | end 35 | 36 | if Input.button_held(PSP_LTRIGGER) then 37 | cr = cr - dt * 40.0 38 | end 39 | if Input.button_held(PSP_RTRIGGER) then 40 | cr = cr + dt * 40.0 41 | end 42 | 43 | camera:set_position(cx, cy) 44 | camera:set_rotation(cr) 45 | 46 | Graphics.start_frame() 47 | Graphics.clear() 48 | 49 | Primitive.draw_triangle(120, 136, 40, 40, tri_color, 0.0) 50 | Primitive.draw_rectangle(240, 136, 40, 40, rect_color, 0.0) 51 | Primitive.draw_circle(360, 136, 40, 40, circle_color, 0.0) 52 | 53 | Graphics.end_frame(true) 54 | end -------------------------------------------------------------------------------- /samples/lua/input.lua: -------------------------------------------------------------------------------- 1 | clear_col1 = Color.create(255, 0, 255, 255) 2 | clear_col2 = Color.create(0, 255, 0, 255) 3 | 4 | while(QuickGame.running()) do 5 | Input.update() 6 | 7 | if Input.button_held(PSP_CROSS) then 8 | Graphics.set_clear_color(clear_col1) 9 | else 10 | Graphics.set_clear_color(clear_col2) 11 | end 12 | 13 | Graphics.start_frame() 14 | Graphics.clear() 15 | 16 | Graphics.end_frame(true) 17 | end -------------------------------------------------------------------------------- /samples/lua/primitive.lua: -------------------------------------------------------------------------------- 1 | clear_col = Color.create(32, 32, 32, 255) 2 | 3 | Graphics.set_clear_color(clear_col) 4 | 5 | while(QuickGame.running()) do 6 | Graphics.start_frame() 7 | Graphics.clear() 8 | 9 | tri_color = Color.create(255, 0, 0, 255) 10 | rect_color = Color.create(0, 255, 0, 255) 11 | circle_color = Color.create(0, 0, 255, 255) 12 | 13 | Primitive.draw_triangle(120, 136, 40, 40, tri_color, 0.0) 14 | Primitive.draw_rectangle(240, 136, 40, 40, rect_color, 0.0) 15 | Primitive.draw_circle(360, 136, 40, 40, circle_color, 0.0) 16 | 17 | Graphics.end_frame(true) 18 | end -------------------------------------------------------------------------------- /samples/lua/print.lua: -------------------------------------------------------------------------------- 1 | print("Hello World From QuickGame!") 2 | 3 | while(true) 4 | do 5 | end -------------------------------------------------------------------------------- /samples/lua/timer.lua: -------------------------------------------------------------------------------- 1 | timer = Timer.create() 2 | 3 | while(QuickGame.running()) do 4 | delta = timer:delta() 5 | print(tostring(delta)) 6 | end -------------------------------------------------------------------------------- /samples/primitive/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(primitive) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(primitive main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(primitive PUBLIC QuickGame) 12 | target_include_directories(primitive PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET primitive 16 | TITLE primitive 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/primitive/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | if(QuickGame_Init() < 0) 5 | return 1; 6 | 7 | QuickGame_Graphics_Set2D(); 8 | 9 | while(QuickGame_Running()){ 10 | QuickGame_Graphics_Start_Frame(); 11 | QuickGame_Graphics_Clear(); 12 | 13 | QGTransform2D transform = { 14 | .position = {.x = 240, .y = 136}, 15 | .rotation = 0.0f, 16 | .scale = {.x = 40, .y = 40} 17 | }; 18 | QGColor col; 19 | 20 | col.color = 0xFF0000FF; 21 | QuickGame_Primitive_Draw_Rectangle(transform, col); 22 | 23 | transform.position.x = 120; 24 | col.color = 0xFF00FF00; 25 | QuickGame_Primitive_Draw_Triangle(transform, col); 26 | 27 | transform.position.x = 360; 28 | col.color = 0xFFFF0000; 29 | QuickGame_Primitive_Draw_Circle(transform, col); 30 | 31 | QuickGame_Graphics_End_Frame(true); 32 | } 33 | 34 | QuickGame_Terminate(); 35 | return 0; 36 | } -------------------------------------------------------------------------------- /samples/tilemap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(basic) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_executable(basic main.c) 8 | 9 | add_subdirectory(../../ QuickGame) 10 | 11 | target_link_libraries(basic PUBLIC QuickGame) 12 | target_include_directories(basic PUBLIC ../../include) 13 | 14 | create_pbp_file( 15 | TARGET basic 16 | TITLE basic 17 | BUILD_PRX ON 18 | ) -------------------------------------------------------------------------------- /samples/tilemap/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv){ 4 | if(QuickGame_Init() < 0) 5 | return 1; 6 | 7 | QGTexture_t tex = QuickGame_Texture_Load("terrain.png", 0, 0); 8 | QGTextureAtlas atlas = {.x = 16, .y = 16}; 9 | QGVector2 size = {.x = 8, .y = 8}; 10 | QGTilemap_t tilemap = QuickGame_Tilemap_Create(atlas, tex, size); 11 | 12 | for(int i = 0; i < 64; i++){ 13 | QGTile t = { 14 | .position = {.x = 16 * (i % 8), .y = 16 * (i / 8)}, 15 | .scale = {.x = 16.0f, .y = 16.0f}, 16 | .collide = false, 17 | .atlas_idx = i, 18 | .color.color = 0xFFFFFFFF, 19 | }; 20 | tilemap->tile_array[i] = t; 21 | } 22 | QuickGame_Tilemap_Build(tilemap); 23 | 24 | QuickGame_Graphics_Set2D(); 25 | 26 | while(QuickGame_Running()){ 27 | QuickGame_Graphics_Start_Frame(); 28 | 29 | QuickGame_Graphics_Clear(); 30 | 31 | QuickGame_Tilemap_Draw(tilemap); 32 | 33 | QuickGame_Graphics_End_Frame(true); 34 | } 35 | 36 | QuickGame_Tilemap_Destroy(&tilemap); 37 | QuickGame_Texture_Destroy(&tex); 38 | 39 | QuickGame_Terminate(); 40 | return 0; 41 | } -------------------------------------------------------------------------------- /src/Audio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "osl_sound/oslib.h" 4 | #include "osl_sound/audio.h" 5 | 6 | void QuickGame_Audio_Init() { 7 | VirtualFileInit(); 8 | oslInitAudio(); 9 | } 10 | 11 | void QuickGame_Audio_Terminate() { 12 | oslDeinitAudio(); 13 | } 14 | 15 | QGAudioClip_t QuickGame_Audio_Load(const char* filename, bool looping, bool streaming){ 16 | QGAudioClip_t clip = QuickGame_Allocate(sizeof(QGAudioClip)); 17 | if(clip == NULL) 18 | return NULL; 19 | 20 | clip->data = oslLoadSoundFile(filename, streaming ? OSL_FMT_STREAM : OSL_FMT_NONE); 21 | 22 | if(clip->data == NULL){ 23 | QuickGame_Destroy(&clip); 24 | return NULL; 25 | } 26 | 27 | oslSetSoundLoop(((OSL_SOUND*)clip->data), looping); 28 | return clip; 29 | } 30 | 31 | void QuickGame_Audio_Destroy(QGAudioClip_t* clip){ 32 | if(clip == NULL || (*clip) == NULL) 33 | return; 34 | 35 | oslDeleteSound((*clip)->data); 36 | QuickGame_Destroy(*clip); 37 | *clip = NULL; 38 | } 39 | 40 | void QuickGame_Audio_Set_Looping(QGAudioClip_t clip, bool looping) { 41 | if(clip == NULL) 42 | return; 43 | oslSetSoundLoop(((OSL_SOUND*)clip->data), looping); 44 | } 45 | 46 | void QuickGame_Audio_Set_Volume(QGAudioClip_t clip, f32 volume) { 47 | if(clip == NULL) 48 | return; 49 | ((OSL_SOUND*)clip->data)->volumeLeft = (1.0f-volume) * 100; 50 | ((OSL_SOUND*)clip->data)->volumeRight = (1.0f-volume) * 100; 51 | } 52 | 53 | void QuickGame_Audio_Set_Pan(QGAudioClip_t clip, f32 pan) { 54 | if(clip == NULL) 55 | return; 56 | float left = 1.0f - pan; 57 | float right = pan - 1.0f; 58 | 59 | ((OSL_SOUND*)clip->data)->volumeLeft *= left; 60 | ((OSL_SOUND*)clip->data)->volumeRight *= right; 61 | } 62 | 63 | void QuickGame_Audio_Play(QGAudioClip_t clip, u8 channel) { 64 | if(clip == NULL) 65 | return; 66 | oslPlaySound(((OSL_SOUND*)clip->data), channel); 67 | } 68 | void QuickGame_Audio_Pause(QGAudioClip_t clip) { 69 | if(clip == NULL) 70 | return; 71 | oslPauseSound(((OSL_SOUND*)clip->data), -1); 72 | } 73 | void QuickGame_Audio_Stop(QGAudioClip_t clip) { 74 | if(clip == NULL) 75 | return; 76 | oslStopSound(((OSL_SOUND*)clip->data)); 77 | } 78 | -------------------------------------------------------------------------------- /src/GraphicsContext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define GUGL_IMPLEMENTATION 4 | #include 5 | 6 | static u32 __attribute__((aligned(16))) list[262144]; 7 | static QGColor clearColor; 8 | static QGCamera2D* cam_ptr; 9 | static bool dialogMode; 10 | static bool wireframeMode; 11 | 12 | void QuickGame_Graphics_Init() { 13 | guglInit(list); 14 | 15 | QuickGame_Graphics_Start_Frame(); 16 | glDisable(GL_DEPTH_TEST); 17 | glBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); 18 | glEnable(GL_BLEND); 19 | glFrontFace(GL_CCW); 20 | QuickGame_Graphics_End_Frame(false); 21 | 22 | dialogMode = false; 23 | cam_ptr = NULL; 24 | wireframeMode = false; 25 | } 26 | 27 | void QuickGame_Graphics_Terminate() { 28 | guglTerm(); 29 | } 30 | 31 | void QuickGame_Graphics_Set_Dialog_Mode(bool mode) { 32 | dialogMode = mode; 33 | } 34 | 35 | void QuickGame_Graphics_Set_Wireframe_Mode(bool mode) { 36 | wireframeMode = mode; 37 | } 38 | 39 | void QuickGame_Graphics_Start_Frame() { 40 | guglStartFrame(list, dialogMode); 41 | 42 | if(cam_ptr) { 43 | // Set to location 44 | glMatrixMode(GL_VIEW); 45 | glLoadIdentity(); 46 | 47 | ScePspFVector3 temp = { 48 | .x = -cam_ptr->position.x, 49 | .y = -cam_ptr->position.y, 50 | .z = 0, 51 | }; 52 | 53 | gluTranslate(&temp); 54 | gluRotateZ(cam_ptr->rotation); 55 | 56 | glMatrixMode(GL_MODEL); 57 | glLoadIdentity(); 58 | } 59 | } 60 | 61 | void QuickGame_Graphics_End_Frame(bool vsync) { 62 | guglSwapBuffers(vsync, dialogMode); 63 | } 64 | 65 | void QuickGame_Graphics_Set_Clear_Color(QGColor color) { 66 | clearColor = color; 67 | glClearColor(clearColor.color); 68 | } 69 | 70 | void QuickGame_Graphics_Clear(){ 71 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 72 | } 73 | 74 | QGVMesh_t QuickGame_Graphics_Create_Mesh(const u8 type, const usize vcount, const usize icount) { 75 | QGVMesh_t mesh = (QGVMesh_t)QuickGame_Allocate(sizeof(QGVMesh)); 76 | 77 | if(!mesh) 78 | return NULL; 79 | 80 | mesh->type = type; 81 | mesh->count = icount; 82 | if(type == QG_VERTEX_TYPE_TEXTURED) { 83 | mesh->data = QuickGame_Allocate_Aligned(16, sizeof(QGTexturedVertex) * vcount); 84 | } else if (type == QG_VERTEX_TYPE_COLORED) { 85 | mesh->data = QuickGame_Allocate_Aligned(16, sizeof(QGColoredVertex) * vcount); 86 | } else if (type == QG_VERTEX_TYPE_FULL) { 87 | mesh->data = QuickGame_Allocate_Aligned(16, sizeof(QGFullVertex) * vcount); 88 | } else if (type == QG_VERTEX_TYPE_SIMPLE) { 89 | mesh->data = QuickGame_Allocate_Aligned(16, sizeof(QGSimpleVertex) * vcount); 90 | } else { 91 | QuickGame_Destroy(mesh); 92 | return NULL; 93 | } 94 | mesh->indices = (u16*)QuickGame_Allocate_Aligned(16, sizeof(u16) * icount); 95 | 96 | if(!mesh->data) { 97 | QuickGame_Destroy(mesh); 98 | QuickGame_Destroy(mesh->indices); 99 | return NULL; 100 | } 101 | 102 | if(!mesh->indices) { 103 | QuickGame_Destroy(mesh->data); 104 | QuickGame_Destroy(mesh); 105 | return NULL; 106 | } 107 | 108 | return mesh; 109 | } 110 | 111 | void QuickGame_Graphics_Destroy_Mesh(QGVMesh_t* mesh) { 112 | if(!mesh) 113 | return; 114 | 115 | QuickGame_Destroy((*mesh)->data); 116 | QuickGame_Destroy((*mesh)->indices); 117 | QuickGame_Destroy(*mesh); 118 | *mesh = NULL; 119 | } 120 | 121 | 122 | void QuickGame_Graphics_Draw_Mesh(QGVMesh_t mesh) { 123 | if(!mesh || !mesh->data || !mesh->indices) 124 | return; 125 | 126 | usize vtype = GL_INDEX_16BIT | GL_VERTEX_32BITF | GL_TRANSFORM_3D; 127 | 128 | if(mesh->type == QG_VERTEX_TYPE_TEXTURED){ 129 | glEnable(GL_TEXTURE_2D); 130 | vtype |= GL_TEXTURE_32BITF; 131 | } else if (mesh->type == QG_VERTEX_TYPE_COLORED) { 132 | glDisable(GL_TEXTURE_2D); 133 | vtype |= GL_COLOR_8888; 134 | QuickGame_Texture_Unbind(); 135 | } else if (mesh->type == QG_VERTEX_TYPE_FULL) { 136 | glEnable(GL_TEXTURE_2D); 137 | vtype |= GL_TEXTURE_32BITF | GL_COLOR_8888; 138 | } else if (mesh->type == QG_VERTEX_TYPE_SIMPLE) { 139 | glDisable(GL_TEXTURE_2D); 140 | } else { 141 | return; 142 | } 143 | 144 | int mode = GL_TRIANGLES; 145 | if(wireframeMode) 146 | mode = GL_LINE_STRIP; 147 | 148 | glDrawElements(mode, vtype, mesh->count, mesh->indices, mesh->data); 149 | } 150 | 151 | void QuickGame_Graphics_Set2D() { 152 | glMatrixMode(GL_PROJECTION); 153 | glLoadIdentity(); 154 | glOrtho(0.0f, 480.0f, 0.0f, 272.0f, -30.0f, 30.0f); 155 | 156 | glMatrixMode(GL_VIEW); 157 | glLoadIdentity(); 158 | 159 | glMatrixMode(GL_MODEL); 160 | glLoadIdentity(); 161 | } 162 | 163 | 164 | void QuickGame_Graphics_Set_Camera(QGCamera2D* camera) { 165 | cam_ptr = camera; 166 | } 167 | 168 | void QuickGame_Graphics_Unset_Camera() { 169 | cam_ptr = NULL; 170 | } -------------------------------------------------------------------------------- /src/Input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static struct SceCtrlData padData; 5 | static struct SceCtrlData oldData; 6 | 7 | void QuickGame_Input_Init() { 8 | sceCtrlSetSamplingCycle(0); 9 | sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); 10 | } 11 | 12 | void QuickGame_Input_Update() { 13 | oldData = padData; 14 | sceCtrlReadBufferPositive(&padData,1); 15 | } 16 | 17 | bool QuickGame_Button_Pressed(u32 buttons) { 18 | bool current = (padData.Buttons & buttons) == buttons; 19 | bool last = (oldData.Buttons & buttons) == buttons; 20 | return current && !last; // Currently pressed, not pressed last time 21 | } 22 | 23 | bool QuickGame_Button_Held(u32 buttons) { 24 | bool current = (padData.Buttons & buttons) == buttons; 25 | bool last = (oldData.Buttons & buttons) == buttons; 26 | return current && last; // Currently pressed, and pressed last time 27 | } 28 | 29 | bool QuickGame_Button_Released(u32 buttons) { 30 | bool current = (padData.Buttons & buttons) == buttons; 31 | bool last = (oldData.Buttons & buttons) == buttons; 32 | return !current && last; // Currently not pressed, and pressed last time 33 | } 34 | 35 | f32 QuickGame_Analog_X() { 36 | float v = (float)padData.Lx / 255.0f; 37 | v -= 0.5f; 38 | v *= 2.0f; 39 | 40 | return v; 41 | } 42 | 43 | f32 QuickGame_Analog_Y() { 44 | float v = (float)padData.Ly / 255.0f; 45 | v -= 0.5f; 46 | v *= 2.0f; 47 | 48 | return v; 49 | } 50 | -------------------------------------------------------------------------------- /src/Primitive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | QGVMesh_t _quickgame_rect; 8 | QGVMesh_t _quickgame_tri; 9 | QGVMesh_t _quickgame_circle; 10 | 11 | QGSimpleVertex create_simple_vert(float x, float y, float z){ 12 | QGSimpleVertex vert = { 13 | .x = x, 14 | .y = y, 15 | .z = z 16 | }; 17 | 18 | return vert; 19 | } 20 | 21 | /** 22 | * @brief Initialize Primitive Drawings 23 | * 24 | * returns int < 0 on failure. 25 | */ 26 | int QuickGame_Primitive_Init() { 27 | _quickgame_rect = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_SIMPLE, 4, 6); 28 | _quickgame_tri = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_SIMPLE, 3, 4); 29 | _quickgame_circle = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_SIMPLE, 22, 63); 30 | 31 | if(_quickgame_rect == NULL || _quickgame_tri == NULL || _quickgame_circle == NULL) 32 | return 0; 33 | 34 | ((QGSimpleVertex*)_quickgame_rect->data)[0] = create_simple_vert(-0.5f, -0.5f, 0.0f); 35 | ((QGSimpleVertex*)_quickgame_rect->data)[1] = create_simple_vert( 0.5f, -0.5f, 0.0f); 36 | ((QGSimpleVertex*)_quickgame_rect->data)[2] = create_simple_vert( 0.5f, 0.5f, 0.0f); 37 | ((QGSimpleVertex*)_quickgame_rect->data)[3] = create_simple_vert(-0.5f, 0.5f, 0.0f); 38 | 39 | _quickgame_rect->indices[0] = 0; 40 | _quickgame_rect->indices[1] = 1; 41 | _quickgame_rect->indices[2] = 2; 42 | _quickgame_rect->indices[3] = 2; 43 | _quickgame_rect->indices[4] = 3; 44 | _quickgame_rect->indices[5] = 0; 45 | 46 | 47 | ((QGSimpleVertex*)_quickgame_tri->data)[0] = create_simple_vert( 0, 0.5f, 0.0f); 48 | ((QGSimpleVertex*)_quickgame_tri->data)[1] = create_simple_vert( 0.70f, -0.5f, 0.0f); 49 | ((QGSimpleVertex*)_quickgame_tri->data)[2] = create_simple_vert( -0.70f, -0.5f, 0.0f); 50 | 51 | _quickgame_tri->indices[0] = 0; 52 | _quickgame_tri->indices[1] = 2; 53 | _quickgame_tri->indices[2] = 1; 54 | _quickgame_tri->indices[3] = 0; 55 | 56 | 57 | ((QGSimpleVertex*)_quickgame_circle->data)[0] = create_simple_vert(0.0f, 0.0f, 0.0f); 58 | 59 | for(int i = 0; i < 21; i++){ 60 | float angle = 2.0f * 3.14159f / 20.0f * (float)i; 61 | 62 | float x = sinf(angle) * 0.5f; 63 | float y = cosf(angle) * 0.5f; 64 | 65 | ((QGSimpleVertex*)_quickgame_circle->data)[i+1] = create_simple_vert(x, y, 0.0f); 66 | 67 | _quickgame_circle->indices[i * 3 + 0] = 0; 68 | 69 | _quickgame_circle->indices[i * 3 + 1] = i + 1; 70 | 71 | _quickgame_circle->indices[i * 3 + 2] = i; 72 | } 73 | 74 | sceKernelDcacheWritebackInvalidateAll(); 75 | 76 | return 0; 77 | } 78 | 79 | void QuickGame_Primitive_Terminate() { 80 | QuickGame_Graphics_Destroy_Mesh(&_quickgame_circle); 81 | QuickGame_Graphics_Destroy_Mesh(&_quickgame_rect); 82 | QuickGame_Graphics_Destroy_Mesh(&_quickgame_tri); 83 | } 84 | 85 | void QuickGame_Primitive_Draw_Rectangle(QGTransform2D transform, QGColor color) { 86 | glMatrixMode(GL_MODEL); 87 | glLoadIdentity(); 88 | 89 | ScePspFVector3 v1 = {transform.position.x, transform.position.y, 0.0f}; 90 | gluTranslate(&v1); 91 | 92 | gluRotateZ(transform.rotation / 180.0f * GL_PI); 93 | 94 | ScePspFVector3 v = {transform.scale.x, transform.scale.y, 1.0f}; 95 | gluScale(&v); 96 | 97 | glColor(color.color); 98 | 99 | QuickGame_Graphics_Draw_Mesh(_quickgame_rect); 100 | } 101 | 102 | void QuickGame_Primitive_Draw_Triangle(QGTransform2D transform, QGColor color) { 103 | glMatrixMode(GL_MODEL); 104 | glLoadIdentity(); 105 | 106 | ScePspFVector3 v1 = {transform.position.x, transform.position.y, 0.0f}; 107 | gluTranslate(&v1); 108 | 109 | gluRotateZ(transform.rotation / 180.0f * GL_PI); 110 | 111 | ScePspFVector3 v = {transform.scale.x, transform.scale.y, 1.0f}; 112 | gluScale(&v); 113 | 114 | glColor(color.color); 115 | 116 | QuickGame_Graphics_Draw_Mesh(_quickgame_tri); 117 | } 118 | 119 | void QuickGame_Primitive_Draw_Circle(QGTransform2D transform, QGColor color) { 120 | glMatrixMode(GL_MODEL); 121 | glLoadIdentity(); 122 | 123 | ScePspFVector3 v1 = {transform.position.x, transform.position.y, 0.0f}; 124 | gluTranslate(&v1); 125 | 126 | gluRotateZ(transform.rotation / 180.0f * GL_PI); 127 | 128 | ScePspFVector3 v = {transform.scale.x, transform.scale.y, 1.0f}; 129 | gluScale(&v); 130 | 131 | glColor(color.color); 132 | 133 | QuickGame_Graphics_Draw_Mesh(_quickgame_circle); 134 | } -------------------------------------------------------------------------------- /src/QuickGame.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | PSP_MODULE_INFO("QuickGame", 0, 1, 1); 8 | PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU); 9 | 10 | static int exitRequest = 0; 11 | 12 | int exitCallback() 13 | { 14 | exitRequest = 1; 15 | return 0; 16 | } 17 | 18 | int callbackThread(SceSize args, void *argp) 19 | { 20 | if(args){ 21 | args = 0; 22 | } 23 | 24 | if(argp){ 25 | argp = NULL; 26 | } 27 | 28 | int cbid = sceKernelCreateCallback("Exit Callback", exitCallback, NULL); 29 | sceKernelRegisterExitCallback(cbid); 30 | 31 | sceKernelSleepThreadCB(); 32 | 33 | return 0; 34 | } 35 | 36 | int setupCallbacks(void) 37 | { 38 | int thid = 0; 39 | 40 | thid = sceKernelCreateThread("update_thread", callbackThread, 0x11, 0xFA0, 0, 0); 41 | if(thid >= 0) 42 | { 43 | sceKernelStartThread(thid, 0, 0); 44 | } 45 | 46 | return thid; 47 | } 48 | 49 | bool QuickGame_Running() { 50 | return !exitRequest; 51 | } 52 | 53 | void QuickGame_Request_Exit() { 54 | exitRequest = true; 55 | } 56 | 57 | i32 QuickGame_Init() { 58 | if(setupCallbacks() < 0){ 59 | return -1; 60 | } 61 | 62 | // Technically this could fail 63 | // FIXME: Handle Fail Case 64 | QuickGame_Graphics_Init(); 65 | 66 | // Initialize input 67 | QuickGame_Input_Init(); 68 | 69 | // Init Audio 70 | QuickGame_Audio_Init(); 71 | 72 | if(QuickGame_Primitive_Init() < 0){ 73 | return -1; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | void QuickGame_Terminate() { 80 | QuickGame_Audio_Terminate(); 81 | QuickGame_Graphics_Terminate(); 82 | sceKernelExitGame(); 83 | } 84 | 85 | #ifndef QUICKGAME_CUSTOM_ALLOCATOR 86 | anyopaque* QuickGame_Allocate(usize n) { 87 | anyopaque* a = malloc(n); 88 | if(a != NULL){ 89 | memset(a, 0, n); 90 | } 91 | 92 | return a; 93 | } 94 | 95 | anyopaque* QuickGame_Allocate_Aligned(usize a, usize n) { 96 | anyopaque* p = memalign(a, n); 97 | if(p != NULL){ 98 | memset(p, 0, n); 99 | } 100 | 101 | return p; 102 | } 103 | 104 | void QuickGame_Destroy(anyopaque* src) { 105 | if(src) 106 | free(src); 107 | } 108 | #endif -------------------------------------------------------------------------------- /src/Sprite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | QGSprite_t QuickGame_Sprite_Create_Contained(float x, float y, float w, float h, QGTexInfo tex_info){ 10 | QGTexture_t tex = QuickGame_Texture_Load_Alt(tex_info); 11 | if(!tex) 12 | return NULL; 13 | 14 | QGSprite_t result = QuickGame_Sprite_Create_Alt(x, y, w, h, tex); 15 | 16 | if(!result) 17 | QuickGame_Texture_Destroy(&tex); 18 | else 19 | result->contained = true; 20 | 21 | return result; 22 | } 23 | 24 | QGSprite_t QuickGame_Sprite_Create_Alt(float x, float y, float w, float h, QGTexture_t texture) { 25 | QGVector2 position = {x, y}; 26 | QGVector2 size = {w, h}; 27 | 28 | return QuickGame_Sprite_Create(position, size, texture); 29 | } 30 | 31 | QGSprite_t QuickGame_Sprite_Create(QGVector2 position, QGVector2 size, QGTexture_t texture) { 32 | if(!texture) 33 | return NULL; 34 | 35 | QGSprite_t sprite = QuickGame_Allocate(sizeof(QGSprite)); 36 | if(!sprite) 37 | return NULL; 38 | 39 | sprite->layer = 0; 40 | sprite->color.color = 0xFFFFFFFF; 41 | 42 | sprite->transform.position = position; 43 | sprite->transform.scale = size; 44 | sprite->aabb_size = size; 45 | sprite->texture = texture; 46 | 47 | sprite->mesh = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_TEXTURED, 4, 6); 48 | if(!sprite->mesh) { 49 | QuickGame_Destroy(sprite); 50 | return NULL; 51 | } 52 | 53 | float wRatio = (float)texture->width / (float)texture->pWidth; 54 | float hRatio = (float)texture->height / (float)texture->pHeight; 55 | 56 | QGTexturedVertex* verts = sprite->mesh->data; 57 | verts[0].u = 0.0f * wRatio; 58 | verts[0].v = 0.0f * hRatio; 59 | verts[0].x = -0.5f; 60 | verts[0].y = -0.5f; 61 | verts[0].z = 0.0f; 62 | 63 | 64 | verts[1].u = 1.0f * wRatio; 65 | verts[1].v = 0.0f * hRatio; 66 | verts[1].x = 0.5f; 67 | verts[1].y = -0.5f; 68 | verts[1].z = 0.0f; 69 | 70 | 71 | verts[2].u = 1.0f * wRatio; 72 | verts[2].v = 1.0f * hRatio; 73 | verts[2].x = 0.5f; 74 | verts[2].y = 0.5f; 75 | verts[2].z = 0.0f; 76 | 77 | 78 | verts[3].u = 0.0f * wRatio; 79 | verts[3].v = 1.0f * hRatio; 80 | verts[3].x = -0.5f; 81 | verts[3].y = 0.5f; 82 | verts[3].z = 0.0f; 83 | 84 | u16* indices = sprite->mesh->indices; 85 | indices[0] = 0; 86 | indices[1] = 1; 87 | indices[2] = 2; 88 | indices[3] = 2; 89 | indices[4] = 3; 90 | indices[5] = 0; 91 | 92 | sceKernelDcacheWritebackInvalidateAll(); 93 | 94 | return sprite; 95 | } 96 | 97 | /** 98 | * @brief Destroy a sprite and sets it to NULL 99 | * 100 | * @param sprite Pointer to Sprite 101 | */ 102 | void QuickGame_Sprite_Destroy(QGSprite_t* sprite) { 103 | if(sprite != NULL) 104 | return; 105 | 106 | QuickGame_Graphics_Destroy_Mesh(&(*sprite)->mesh); 107 | 108 | if((*sprite)->contained) 109 | QuickGame_Texture_Destroy(&(*sprite)->texture); 110 | 111 | QuickGame_Destroy((*sprite)); 112 | *sprite = NULL; 113 | } 114 | 115 | QGSprite_t QuickGame_Sprite_Create_Drakonchik(QGVector2 position, QGVector2 size, float u1, float v1, float w, float h, QGTexture_t texture) 116 | { 117 | if(!texture) 118 | return NULL; 119 | 120 | QGSprite_t sprite = QuickGame_Allocate(sizeof(QGSprite)); 121 | if(!sprite) 122 | return NULL; 123 | 124 | sprite->layer = 0; 125 | sprite->color.color = 0xFFFFFFFF; 126 | 127 | sprite->transform.position.x = position.x; 128 | sprite->transform.position.y = position.y; 129 | sprite->transform.scale = size; 130 | sprite->aabb_size = size; 131 | sprite->texture = texture; 132 | 133 | sprite->mesh = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_TEXTURED, 4, 6); 134 | if(!sprite->mesh) { 135 | QuickGame_Destroy(sprite); 136 | return NULL; 137 | } 138 | 139 | float wRatio = (float)texture->width / (float)texture->pWidth; 140 | float hRatio = (float)texture->height / (float)texture->pHeight; 141 | 142 | float ux = (float)(u1) / (float)texture->width; 143 | float uw = (float)(u1 + w) / (float)texture->width; 144 | 145 | float vy = (float)(v1) / (float)texture->height; 146 | float vh = (float)(v1 + h) / (float)texture->height; 147 | 148 | QGTexturedVertex* verts = sprite->mesh->data; 149 | verts[0].u = ux; 150 | verts[0].v = vh; 151 | verts[0].x = -0.5f; 152 | verts[0].y = -0.5f; 153 | verts[0].z = 0.0f; 154 | 155 | verts[1].u = uw; 156 | verts[1].v = vh; 157 | verts[1].x = 0.5f; 158 | verts[1].y = -0.5f; 159 | verts[1].z = 0.0f; 160 | 161 | verts[2].u = uw; 162 | verts[2].v = vy; 163 | verts[2].x = 0.5f; 164 | verts[2].y = 0.5f; 165 | verts[2].z = 0.0f; 166 | 167 | verts[3].u = ux; 168 | verts[3].v = vy; 169 | verts[3].x = -0.5f; 170 | verts[3].y = 0.5f; 171 | verts[3].z = 0.0f; 172 | 173 | u16* indices = sprite->mesh->indices; 174 | indices[0] = 0; 175 | indices[1] = 1; 176 | indices[2] = 2; 177 | indices[3] = 2; 178 | indices[4] = 3; 179 | indices[5] = 0; 180 | 181 | for(int i = 0; i < 4; i++){ 182 | verts[i].u *= wRatio; 183 | verts[i].v *= hRatio; 184 | } 185 | 186 | sceKernelDcacheWritebackInvalidateAll(); 187 | 188 | return sprite; 189 | } 190 | 191 | void QuickGame_Sprite_Draw(QGSprite_t sprite) { 192 | if(!sprite) 193 | return; 194 | 195 | glMatrixMode(GL_MODEL); 196 | glLoadIdentity(); 197 | 198 | 199 | ScePspFVector3 v1 = {sprite->transform.position.x, sprite->transform.position.y, sprite->layer}; 200 | gluTranslate(&v1); 201 | 202 | gluRotateZ(sprite->transform.rotation / 180.0f * GL_PI); 203 | 204 | ScePspFVector3 v = {sprite->transform.scale.x, sprite->transform.scale.y, 1.0f}; 205 | gluScale(&v); 206 | 207 | glColor(sprite->color.color); 208 | 209 | QuickGame_Texture_Bind(sprite->texture); 210 | QuickGame_Graphics_Draw_Mesh(sprite->mesh); 211 | QuickGame_Texture_Unbind(sprite->texture); 212 | } 213 | 214 | 215 | void QuickGame_Sprite_Draw_Flipped(QGSprite_t sprite, uint8_t flip){ 216 | if(!sprite) 217 | return; 218 | 219 | glMatrixMode(GL_MODEL); 220 | glLoadIdentity(); 221 | 222 | // Note: Could be optimized to just an xor 223 | if(flip == QG_FLIP_HORIZONTAL || flip == QG_FLIP_VERTICAL) 224 | glFrontFace(GL_CW); 225 | 226 | ScePspFVector3 v1 = {sprite->transform.position.x, sprite->transform.position.y, sprite->layer}; 227 | gluTranslate(&v1); 228 | 229 | if(flip == QG_FLIP_HORIZONTAL || flip == QG_FLIP_BOTH) 230 | gluRotateY(GL_PI); 231 | 232 | if(flip == QG_FLIP_VERTICAL || flip == QG_FLIP_BOTH) 233 | gluRotateX(GL_PI); 234 | 235 | gluRotateZ(sprite->transform.rotation / 180.0f * GL_PI); 236 | 237 | ScePspFVector3 v = {sprite->transform.scale.x, sprite->transform.scale.y, 1.0f}; 238 | gluScale(&v); 239 | 240 | glColor(sprite->color.color); 241 | 242 | QuickGame_Texture_Bind(sprite->texture); 243 | QuickGame_Graphics_Draw_Mesh(sprite->mesh); 244 | QuickGame_Texture_Unbind(sprite->texture); 245 | 246 | glFrontFace(GL_CCW); 247 | } 248 | 249 | 250 | bool QuickGame_Sprite_Intersects(QGSprite_t a, QGSprite_t b) { 251 | if(!a || !b) 252 | return false; 253 | 254 | float aMinX = a->transform.position.x - a->aabb_size.x / 2.0f; 255 | float aMinY = a->transform.position.y - a->aabb_size.y / 2.0f; 256 | float aMaxX = a->transform.position.x + a->aabb_size.x / 2.0f; 257 | float aMaxY = a->transform.position.y + a->aabb_size.y / 2.0f; 258 | 259 | float bMinX = b->transform.position.x - b->aabb_size.x / 2.0f; 260 | float bMinY = b->transform.position.y - b->aabb_size.y / 2.0f; 261 | float bMaxX = b->transform.position.x + b->aabb_size.x / 2.0f; 262 | float bMaxY = b->transform.position.y + b->aabb_size.y / 2.0f; 263 | 264 | return ( 265 | aMinX <= bMaxX && 266 | aMaxX >= bMinX && 267 | aMinY <= bMaxY && 268 | aMaxY >= bMinY 269 | ); 270 | } 271 | 272 | bool QuickGame_Intersect_Transform(QGTransform2D a, QGTransform2D b) { 273 | float aMinX = a.position.x - a.scale.x / 2.0f; 274 | float aMinY = a.position.y - a.scale.y / 2.0f; 275 | float aMaxX = a.position.x + a.scale.x / 2.0f; 276 | float aMaxY = a.position.y + a.scale.y / 2.0f; 277 | 278 | float bMinX = b.position.x - b.scale.x / 2.0f; 279 | float bMinY = b.position.y - b.scale.y / 2.0f; 280 | float bMaxX = b.position.x + b.scale.x / 2.0f; 281 | float bMaxY = b.position.y + b.scale.y / 2.0f; 282 | 283 | return ( 284 | aMinX <= bMaxX && 285 | aMaxX >= bMinX && 286 | aMinY <= bMaxY && 287 | aMaxY >= bMinY 288 | ); 289 | } 290 | 291 | u8 QuickGame_Sprite_Intersect_Direction(QGSprite_t a, QGSprite_t b) { 292 | float a_bottom = a->transform.position.y + a->aabb_size.y / 2.0f; 293 | float b_bottom = b->transform.position.y + b->aabb_size.y / 2.0f; 294 | float a_right = a->transform.position.x + a->aabb_size.x / 2.0f; 295 | float b_right = b->transform.position.x + b->aabb_size.x / 2.0f; 296 | 297 | float b_collision = b_bottom - (a->transform.position.y - a->aabb_size.y / 2.0f); 298 | float t_collision = a_bottom - (b->transform.position.y - b->aabb_size.y / 2.0f); 299 | float l_collision = a_right - (b->transform.position.x - b->aabb_size.x / 2.0f ); 300 | float r_collision = b_right - (a->transform.position.x - a->aabb_size.x / 2.0f ); 301 | 302 | if (t_collision < b_collision && t_collision < l_collision && t_collision < r_collision ) { 303 | return QG_DIR_UP; 304 | } 305 | if (b_collision < t_collision && b_collision < l_collision && b_collision < r_collision) { 306 | return QG_DIR_DOWN; 307 | } 308 | if (l_collision < r_collision && l_collision < t_collision && l_collision < b_collision) { 309 | return QG_DIR_LEFT; 310 | } 311 | if (r_collision < l_collision && r_collision < t_collision && r_collision < b_collision ) { 312 | return QG_DIR_RIGHT; 313 | } 314 | 315 | return 0; 316 | } -------------------------------------------------------------------------------- /src/Texture.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void swizzle_fast(u8 *out, const u8 *in, const u32 width, const u32 height) { 8 | u32 blockx, blocky; 9 | u32 j; 10 | 11 | u32 width_blocks = (width / 16); 12 | u32 height_blocks = (height / 8); 13 | 14 | u32 src_pitch = (width - 16) / 4; 15 | u32 src_row = width * 8; 16 | 17 | const u8 *ysrc = in; 18 | u32 *dst = (u32 *)out; 19 | 20 | for (blocky = 0; blocky < height_blocks; ++blocky) { 21 | const u8 *xsrc = ysrc; 22 | for (blockx = 0; blockx < width_blocks; ++blockx) { 23 | const u32 *src = (u32 *)xsrc; 24 | for (j = 0; j < 8; ++j) { 25 | *(dst++) = *(src++); 26 | *(dst++) = *(src++); 27 | *(dst++) = *(src++); 28 | *(dst++) = *(src++); 29 | src += src_pitch; 30 | } 31 | xsrc += 16; 32 | } 33 | ysrc += src_row; 34 | } 35 | } 36 | 37 | u32 pow2(const u32 value) { 38 | u32 poweroftwo = 1; 39 | while (poweroftwo < value) { 40 | poweroftwo <<= 1; 41 | } 42 | return poweroftwo; 43 | } 44 | 45 | void copy_texture_data(void* dest, const void* src, const int pW, const int width, const int height){ 46 | for (u32 y = 0; y < height; y++) { 47 | for (u32 x = 0; x < width; x++) { 48 | ((u32*)dest)[x + y * pW] = ((u32 *)src)[x + y * width]; 49 | } 50 | } 51 | } 52 | 53 | QGTexture_t QuickGame_Texture_Load_Alt(const QGTexInfo tex_info){ 54 | return QuickGame_Texture_Load(tex_info.filename, tex_info.flip, tex_info.vram); 55 | } 56 | 57 | QGTexture_t QuickGame_Texture_Load(const char* filename, const bool flip, const bool vram) { 58 | 59 | int width, height, nrChannels; 60 | stbi_set_flip_vertically_on_load(flip); 61 | unsigned char *data = stbi_load(filename, &width, &height, 62 | &nrChannels, STBI_rgb_alpha); 63 | 64 | if(!data) 65 | return NULL; 66 | 67 | QGTexture_t tex = (QGTexture_t)QuickGame_Allocate(sizeof(QGTexture)); 68 | if(!tex) { 69 | stbi_image_free(data); 70 | return NULL; 71 | } 72 | 73 | tex->width = width; 74 | tex->height = height; 75 | tex->pWidth = pow2(width); 76 | tex->pHeight = pow2(height); 77 | 78 | u32 *dataBuffer = QuickGame_Allocate_Aligned(16, tex->pHeight * tex->pWidth * 4); 79 | if(!dataBuffer) { 80 | stbi_image_free(data); 81 | QuickGame_Destroy(tex); 82 | return NULL; 83 | } 84 | 85 | // Copy to Data Buffer 86 | copy_texture_data(dataBuffer, data, tex->pWidth, tex->width, tex->height); 87 | 88 | stbi_image_free(data); 89 | 90 | u32* swizzled_pixels = NULL; 91 | size_t size = tex->pHeight * tex->pWidth * 4; 92 | if(vram){ 93 | swizzled_pixels = getStaticVramTexture(tex->pWidth, tex->pHeight, GU_PSM_8888); 94 | } else { 95 | swizzled_pixels = QuickGame_Allocate_Aligned(16, size); 96 | } 97 | 98 | if(!swizzled_pixels) { 99 | QuickGame_Destroy(dataBuffer); 100 | QuickGame_Destroy(tex); 101 | return NULL; 102 | } 103 | 104 | swizzle_fast((u8*)swizzled_pixels, (const u8*)dataBuffer, tex->pWidth * 4, tex->pHeight); 105 | 106 | QuickGame_Destroy(dataBuffer); 107 | tex->data = swizzled_pixels; 108 | 109 | sceKernelDcacheWritebackInvalidateAll(); 110 | 111 | return tex; 112 | } 113 | 114 | void QuickGame_Texture_Destroy(QGTexture_t* texture) { 115 | if(!texture || !*texture) 116 | return; 117 | 118 | QuickGame_Destroy((*texture)->data); 119 | QuickGame_Destroy(*texture); 120 | *texture = NULL; 121 | } 122 | 123 | void QuickGame_Texture_Bind(const QGTexture_t texture) { 124 | if(texture == NULL) 125 | return; 126 | 127 | glEnable(GL_TEXTURE_2D); 128 | glTexMode(GU_PSM_8888, 0, 0, 1); 129 | glTexFunc(GL_TFX_MODULATE, GL_TCC_RGBA); 130 | glTexFilter(GL_NEAREST, GL_NEAREST); 131 | glTexWrap(GL_REPEAT, GL_REPEAT); 132 | glTexImage(0, texture->pWidth, texture->pHeight, texture->pWidth, texture->data); 133 | } 134 | 135 | void QuickGame_Texture_Unbind() { 136 | glDisable(GL_TEXTURE_2D); 137 | glTexImage(0, 0, 0, 0, 0); 138 | } -------------------------------------------------------------------------------- /src/Tilemap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Gets texture coordinates from an atlas given a position 9 | * 10 | * @param atlas Texture Atlas to use as reference 11 | * @param buf Buffer to fill with the coordinates 12 | * @param idx Index of the atlas to get coordinates for 13 | */ 14 | void QuickGame_Atlas_Index_Coords(const QGTextureAtlas atlas, f32* buf, const usize idx) { 15 | if(buf == NULL) 16 | return; 17 | 18 | float x = (f32)(idx % 16) / atlas.x; 19 | float y = (f32)(idx / 16) / atlas.y; 20 | float w = x + 1.0f / atlas.x; 21 | float h = y + 1.0f / atlas.y; 22 | 23 | buf[0] = x; 24 | buf[1] = h; 25 | 26 | buf[2] = w; 27 | buf[3] = h; 28 | 29 | buf[4] = w; 30 | buf[5] = y; 31 | 32 | buf[6] = x; 33 | buf[7] = y; 34 | } 35 | 36 | /** 37 | * @brief Create a tilemap 38 | * 39 | * @param texture_atlas Texture Atlas size 40 | * @param texture Texture to use 41 | * @param size Size of tile map 42 | * @return QGTilemap_t Created tilemap or NULL on failure 43 | */ 44 | QGTilemap_t QuickGame_Tilemap_Create(QGTextureAtlas texture_atlas, QGTexture_t texture, QGVector2 size) { 45 | QGTilemap_t tilemap = (QGTilemap*)QuickGame_Allocate(sizeof(QGTilemap)); 46 | if(tilemap == NULL) 47 | return NULL; 48 | 49 | tilemap->tile_array = (QGTile*)QuickGame_Allocate(sizeof(QGTile) * size.x * size.y); 50 | if(tilemap->tile_array == NULL){ 51 | QuickGame_Destroy(tilemap); 52 | return NULL; 53 | } 54 | 55 | tilemap->mesh = QuickGame_Graphics_Create_Mesh(QG_VERTEX_TYPE_FULL, size.x * size.y * 4, size.x * size.y * 6); 56 | if(tilemap->mesh == NULL){ 57 | QuickGame_Destroy(tilemap->tile_array); 58 | QuickGame_Destroy(tilemap); 59 | } 60 | 61 | tilemap->atlas = texture_atlas; 62 | tilemap->texture = texture; 63 | tilemap->size = size; 64 | 65 | tilemap->transform.position.x = 0; 66 | tilemap->transform.position.y = 0; 67 | tilemap->transform.rotation = 0; 68 | tilemap->transform.scale.x = 1.0f; 69 | tilemap->transform.scale.y = 1.0f; 70 | 71 | return tilemap; 72 | } 73 | 74 | QGFullVertex create_vert(float u, float v, unsigned int color, float x, float y, float z){ 75 | QGFullVertex vert = { 76 | .u = u, 77 | .v = v, 78 | .color.color = color, 79 | .x = x, 80 | .y = y, 81 | .z = z 82 | }; 83 | 84 | return vert; 85 | } 86 | 87 | bool QuickGame_Tilemap_Intersects(QGTilemap_t tilemap, QGTransform2D transform){ 88 | for(int i = 0; i < tilemap->size.x * tilemap->size.y; i++){ 89 | QGTransform2D a = { 90 | .rotation = 0.0f, 91 | .scale = tilemap->tile_array->scale, 92 | .position = tilemap->tile_array->position 93 | }; 94 | 95 | if(QuickGame_Intersect_Transform(transform, a)) 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | void QuickGame_Tilemap_Draw_String(QGTilemap_t tilemap, const char* str, QGVector2 position){ 102 | int len = strlen(str); 103 | 104 | for(int i = 0; i < len; i++){ 105 | QGTile tile = { 106 | .atlas_idx = str[i], 107 | .collide = false, 108 | .position = {.x = position.x + i, .y = position.y}, 109 | .scale = {.x = 16, .y = 16}, 110 | .color.color = 0xFFFFFFFF 111 | }; 112 | tilemap->tile_array[i] = tile; 113 | } 114 | } 115 | 116 | /** 117 | * @brief Builds a tilemap to render 118 | * 119 | * @param tilemap Tilemap 120 | */ 121 | void QuickGame_Tilemap_Build(QGTilemap_t tilemap) { 122 | if(!tilemap) 123 | return; 124 | 125 | for(usize y = 0; y < tilemap->size.y; y++) 126 | for(usize x = 0; x < tilemap->size.x; x++){ 127 | usize idx = x + y * tilemap->size.x; 128 | 129 | QGTile* tile = &tilemap->tile_array[idx]; 130 | 131 | unsigned int color = tile->color.color; 132 | 133 | f32 buf[8]; 134 | QuickGame_Atlas_Index_Coords(tilemap->atlas, buf, tile->atlas_idx); 135 | 136 | float tx = tile->position.x; 137 | float ty = tile->position.y; 138 | float tw = tx + tile->scale.x; 139 | float th = ty + tile->scale.y; 140 | 141 | float wRatio = (float)tilemap->texture->width / (float)tilemap->texture->pWidth; 142 | float hRatio = (float)tilemap->texture->height / (float)tilemap->texture->pHeight; 143 | 144 | ((QGFullVertex*)tilemap->mesh->data)[idx * 4 + 0] = create_vert(buf[0] * wRatio, buf[1] * hRatio, color, tx, ty, 0.0f); 145 | ((QGFullVertex*)tilemap->mesh->data)[idx * 4 + 1] = create_vert(buf[2] * wRatio, buf[3] * hRatio, color, tw, ty, 0.0f); 146 | ((QGFullVertex*)tilemap->mesh->data)[idx * 4 + 2] = create_vert(buf[4] * wRatio, buf[5] * hRatio, color, tw, th, 0.0f); 147 | ((QGFullVertex*)tilemap->mesh->data)[idx * 4 + 3] = create_vert(buf[6] * wRatio, buf[7] * hRatio, color, tx, th, 0.0f); 148 | 149 | tilemap->mesh->indices[idx * 6 + 0] = (idx * 4) + 0; 150 | tilemap->mesh->indices[idx * 6 + 1] = (idx * 4) + 1; 151 | tilemap->mesh->indices[idx * 6 + 2] = (idx * 4) + 2; 152 | tilemap->mesh->indices[idx * 6 + 3] = (idx * 4) + 2; 153 | tilemap->mesh->indices[idx * 6 + 4] = (idx * 4) + 3; 154 | tilemap->mesh->indices[idx * 6 + 5] = (idx * 4) + 0; 155 | } 156 | } 157 | 158 | /** 159 | * @brief Destroy a tilemap 160 | * 161 | * @param tilemap Tile map to destroy -- also gets set to null. 162 | */ 163 | void QuickGame_Tilemap_Destroy(QGTilemap_t* tilemap) { 164 | if(tilemap == NULL || (*tilemap) == NULL) 165 | return; 166 | 167 | if((*tilemap)->tile_array != NULL) 168 | QuickGame_Destroy((*tilemap)->tile_array); 169 | 170 | if((*tilemap)->mesh != NULL) 171 | QuickGame_Graphics_Destroy_Mesh(&(*tilemap)->mesh); 172 | 173 | QuickGame_Destroy((*tilemap)); 174 | } 175 | 176 | 177 | /** 178 | * @brief Draws a tilemap to the screen 179 | * 180 | * @param tilemap Tilemap 181 | */ 182 | void QuickGame_Tilemap_Draw(QGTilemap_t tilemap) { 183 | if(tilemap == NULL) 184 | return; 185 | 186 | 187 | glMatrixMode(GL_MODEL); 188 | glLoadIdentity(); 189 | 190 | 191 | ScePspFVector3 v1 = {tilemap->transform.position.x, tilemap->transform.position.y, 0.0f}; 192 | gluTranslate(&v1); 193 | 194 | gluRotateZ(tilemap->transform.rotation / 180.0f * GL_PI); 195 | 196 | ScePspFVector3 v = {tilemap->transform.scale.x, tilemap->transform.scale.y, 1.0f}; 197 | gluScale(&v); 198 | 199 | QuickGame_Texture_Bind(tilemap->texture); 200 | QuickGame_Graphics_Draw_Mesh(tilemap->mesh); 201 | QuickGame_Texture_Unbind(tilemap->texture); 202 | 203 | } -------------------------------------------------------------------------------- /src/Timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | * @brief Start a timer 7 | * 8 | * @param timer Timer to use 9 | */ 10 | void QuickGame_Timer_Start(QGTimer* timer) { 11 | timer->resolution = sceRtcGetTickResolution(); 12 | sceRtcGetCurrentTick(&timer->last); 13 | timer->total = 0; 14 | } 15 | 16 | /** 17 | * @brief Get Delta from last delta request / start 18 | * 19 | * @param timer Timer to use 20 | * @return double Delta Time 21 | */ 22 | double QuickGame_Timer_Delta(QGTimer* timer) { 23 | u64 current; 24 | sceRtcGetCurrentTick(¤t); 25 | 26 | double dt = (double)(current - timer->last) / ((double)timer->resolution); 27 | timer->total += dt; 28 | 29 | timer->last = current; 30 | 31 | return dt; 32 | } 33 | 34 | /** 35 | * @brief Elapsed total time since start 36 | * 37 | * @param timer Timer to use 38 | * @return double Elapsed Time 39 | */ 40 | double QuickGame_Timer_Elapsed(QGTimer* timer) { 41 | return timer->total; 42 | } 43 | 44 | /** 45 | * @brief Resets a timer 46 | * 47 | * @param timer Timer to use 48 | */ 49 | void QuickGame_Timer_Reset(QGTimer* timer) { 50 | sceRtcGetCurrentTick(&timer->last); 51 | timer->total = 0.0f; 52 | } -------------------------------------------------------------------------------- /src/osl_sound/VirtualFile.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IridescentRose/QuickGame/37ff7445017eb9d5230d87312d80a5814fb16f1f/src/osl_sound/VirtualFile.c -------------------------------------------------------------------------------- /src/osl_sound/VirtualFile.h: -------------------------------------------------------------------------------- 1 | /*! \file VirtualFile.h 2 | \brief 3 | Virtual File support for Oldschool Library. 4 | This API is meant to be an universal mean to manipulate every file source possible as you can define your own. 5 | */ 6 | 7 | #ifndef __OSL_VIRTUALFILE_H__ 8 | #define __OSL_VIRTUALFILE_H__ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** @defgroup virtualfile Virtual Files 15 | 16 | Virtual File support for OSLib. 17 | This API is meant to be an universal mean to manipulate every file source possible as you can define your own. 18 | @{ 19 | */ 20 | 21 | /** Virtual File type. */ 22 | typedef struct { 23 | void *ioPtr; //!< User data for IO processing (usually a pointer to data, FILE*, usw.) 24 | unsigned short type; //!< Virtual file type (source number). 25 | unsigned long userData; //!< Additional data 26 | int offset, maxSize; //!< Internal variables for memory-based (RAM / ROM) sources 27 | } VIRTUAL_FILE; 28 | 29 | /** Enumeration describing the available file open modes. Please note that some sources do not support some modes like READWRITE or WRITE, in 30 | this case they'll fail when an attempt to open a file in one of these modes is made. The resulting file returned by VirtualFileOpen will be NULL. */ 31 | enum VF_OPEN_MODES { 32 | VF_O_READ, //!< Read only 33 | VF_O_READWRITE, //!< Read & Write 34 | VF_O_WRITE //!< Write only 35 | }; 36 | //enum {SEEK_SET, SEEK_CUR, SEEK_END}; 37 | 38 | /** Structure of a Virtual File Source. */ 39 | typedef struct { 40 | /** 41 | Open a file. 42 | 43 | Return 1 if anything went well, or 0 to throw an error (the opening will be cancelled and NULL will be returned to the user). 44 | */ 45 | int (*fOpen)(void *param1, int param2, int type, int mode, VIRTUAL_FILE* f); 46 | 47 | /** 48 | Close a file. 49 | 50 | Return 1 if anything went well, 0 to throw an error. 51 | */ 52 | int (*fClose)(VIRTUAL_FILE *f); 53 | 54 | /** 55 | Read in a file. 56 | 57 | Returns the number of bytes effectively read. 58 | */ 59 | int (*fRead)(void *ptr, size_t size, size_t n, VIRTUAL_FILE* f); 60 | 61 | /** 62 | Write in a file 63 | 64 | Returns the number of bytes effectively written. 65 | */ 66 | int (*fWrite)(const void *ptr, size_t size, size_t n, VIRTUAL_FILE* f); 67 | 68 | /** 69 | Read a single character. 70 | 71 | Returns the next character (byte) in the file. 72 | */ 73 | int (*fGetc)(VIRTUAL_FILE *f); 74 | 75 | /** 76 | Write a single character 77 | 78 | Writes a single character. Returns the character value if anything went well, -1 else. 79 | */ 80 | int (*fPutc)(int caractere, VIRTUAL_FILE *f); 81 | 82 | /** 83 | Read a string 84 | 85 | Reads a string to the buffer pointed to by str, of a maximum size of maxLen. Returns a pointer to the read string (str in general). 86 | Reading stops to the next carriage return found (\\n). The routine should handle correctly the following sequences by reading them 87 | entirely: \\r, \\r\\n, \\n. If a \\r is found, you should read the following byte and rewind one byte behind if it's not a \\n. 88 | If you cannot afford a rewind, then keep the next character in cache and return it to the next read. 89 | */ 90 | char* (*fGets)(char *str, int maxLen, VIRTUAL_FILE *f); 91 | 92 | /** 93 | Write a string 94 | 95 | Writes an entire string to the file. 96 | */ 97 | void (*fPuts)(const char *s, VIRTUAL_FILE *f); 98 | 99 | /** 100 | Moving in the file 101 | 102 | Sets the current file position and returns the old one. The whence parameter uses the same values as stdio (SEEK_SET, SEEK_CUR, SEEK_END). 103 | */ 104 | void (*fSeek)(VIRTUAL_FILE *f, int offset, int whence); 105 | 106 | /** 107 | Get current file position 108 | 109 | Returns the current pointer position in the file. You can then use VirtualFileSeek(f, position, SEEK_SET) to return to it. 110 | */ 111 | int (*fTell)(VIRTUAL_FILE *f); 112 | 113 | /** 114 | End of file 115 | 116 | Returns true (1) if it's the end of the file, false (0) else. 117 | */ 118 | int (*fEof)(VIRTUAL_FILE *f); 119 | 120 | } VIRTUAL_FILE_SOURCE; 121 | 122 | /** Virtual file list item. Used for RAM based devices. */ 123 | typedef struct { 124 | const char *name; //!< Virtual file name 125 | void *data; //!< RAM data block 126 | int size; //!< Block data size 127 | int *type; //!< Associated source (e.g. &VF_MEMORY). Don't forget the &, which is there so you can pass a variable which is not known at compile time (virtual file sources are registered upon start, so the compiler doesn't know the ID it will be given in advance). 128 | } OSL_VIRTUALFILENAME; 129 | 130 | /** Initializes the virtual filesystem. Done by default by OSLib, so there is no need to call it by yourself. */ 131 | void VirtualFileInit(); 132 | 133 | /** Open a new file. 134 | \param param1 135 | Pointer to a string representing the file name. 136 | \param type 137 | File type. By default, can be: 138 | - VF_MEMORY: read/write from a memory block 139 | - VF_FILE: read from standard stdio routines. 140 | \param mode 141 | One of VF_OPEN_MODES. 142 | */ 143 | VIRTUAL_FILE *VirtualFileOpen(void *param1, int param2, int type, int mode); 144 | /** Closes an open file. */ 145 | int VirtualFileClose(VIRTUAL_FILE *f); 146 | 147 | 148 | /** @defgroup virtualfile_io I/O routines 149 | 150 | Routines for reading / writing to a virtual file. Make sure to check the Virtual File main page to know how to open files, etc. 151 | @{ 152 | */ 153 | 154 | /** Writes in a file and returns the number of bytes effectively written. */ 155 | #define VirtualFileWrite(ptr, size, n, f) (VirtualFileGetSource(f)->fWrite(ptr, size, n, f)) 156 | /** Reads in a file and returns the number of bytes effectively read. */ 157 | #define VirtualFileRead(ptr, size, n, f) (VirtualFileGetSource(f)->fRead(ptr, size, n, f)) 158 | /** Reads a single character. Returns the next character (byte) in the file. */ 159 | #define VirtualFileGetc(f) (VirtualFileGetSource(f)->fGetc(f)) 160 | /** Writes a single character. Returns the character value if anything went well, -1 else. */ 161 | #define VirtualFilePutc(caractere, f) (VirtualFileGetSource(f)->fPutc(caractere, f)) 162 | /** Reads a string to the buffer pointed to by str, of a maximum size of maxLen. Returns a pointer to the read string (str in general). 163 | 164 | Reading stops to the next carriage return found (\\n, \\r\\n or \\r), supporting files created by every OS (I think). */ 165 | #define VirtualFileGets(str, maxLen, f) (VirtualFileGetSource(f)->fGets(str, maxLen, f)) 166 | /** Writes a string to the file. */ 167 | #define VirtualFilePuts(s, f) (VirtualFileGetSource(f)->fPuts(s, f)) 168 | /** Sets the current file position and returns the old one. The whence parameter uses the same values as stdio (SEEK_SET, SEEK_CUR, SEEK_END). */ 169 | #define VirtualFileSeek(f, offset, whence) (VirtualFileGetSource(f)->fSeek(f, offset, whence)) 170 | /** Returns the current file pointer. */ 171 | #define VirtualFileTell(f) (VirtualFileGetSource(f)->fTell(f)) 172 | /** Returns true (1) if it's the end of the file, false (0) else. */ 173 | #define VirtualFileEof(f) (VirtualFileGetSource(f)->fEof(f)) 174 | 175 | /** @} */ // end of virtualfile_io 176 | 177 | 178 | /** Adds a new file source to the virtual file system. 179 | 180 | \param vfs 181 | Must be a pointer to a valid VIRTUAL_FILE_SOURCE interface containing all your functions for handling the file source. 182 | \return 183 | Returns the identifier of your source or -1 if it has failed. You can then use this ID as a file type (parameter for VirtualFileOpen). */ 184 | int VirtualFileRegisterSource(VIRTUAL_FILE_SOURCE *vfs); 185 | 186 | /* UNSUPPORTED! SEE oslAddVirtualFileList INSTEAD! 187 | 188 | Registers a file list for RAM based devices (such as VF_MEMORY). 189 | \param vfl 190 | List of files (each file is a UL_VIRTUALFILENAME item) 191 | */ 192 | //void oslSetVirtualFilenameList(OSL_VIRTUALFILENAME *vfl, int numberOfEntries); 193 | 194 | /** Call this function in your virtual file source OPEN handler, if it's a RAM based device and param2 == 0 (means null size, impossible). 195 | It will return a UL_VIRTUALFILENAME where you can get a pointer to the data and their size. Note that the return value can be NULL if the file has not been found or an error occured (e.g. fname is NULL). */ 196 | OSL_VIRTUALFILENAME *oslFindFileInVirtualFilenameList(const char *fname, int type); 197 | 198 | //Maximum number of sources 199 | #define VF_MAX_SOURCES 16 200 | //Gets a pointer to the virtual file source associated to a file 201 | #define VirtualFileGetSource(vf) (VirtualFileSources[(vf)->type]) 202 | //List of virtual file sources 203 | extern VIRTUAL_FILE_SOURCE *VirtualFileSources[VF_MAX_SOURCES]; 204 | extern int VirtualFileSourcesNb; 205 | extern OSL_VIRTUALFILENAME *osl_virtualFileList; 206 | extern int osl_virtualFileListNumber; 207 | extern int osl_defaultVirtualFileSource; 208 | extern const char *osl_tempFileName; 209 | 210 | /** Reads an entire file to memory and returns a pointer to the memory block. The block memory usage is stepped by 4 kB, so even a 1 kB file will take 16 kB, so this function is not recommended for opening a lot of small files. The bloc must be freed (using free) once you've finished with it! 211 | \param f 212 | Pointer to an open virtual file. 213 | \param size 214 | Pointer to an integer that will contain the number of bytes read from the file. Can be NULL (in this case the value is discarded). 215 | 216 | \code 217 | int size; 218 | VIRTUAL_FILE *f; 219 | char *data; 220 | //Open a file 221 | f = VirtualFileOpen("test.txt", 0, VF_AUTO, VF_O_READ); 222 | //Read it entirely to RAM 223 | data = oslReadEntireFileToMemory(f, &size); 224 | //Print each character 225 | for (i=0;idataplus (WAV_SRC) 28 | Stream� 29 | s->dataplus (WAV_SRC) 30 | fp (FILE) 31 | Normal 32 | s->dataplus (WAV_SRC) 33 | database (malloc: s->size) 34 | Fichier ADPCM 35 | Commun 36 | s->dataplus (OSL_ADGlobals) 37 | Stream� 38 | s->data (FILE) 39 | Normal 40 | s->data (malloc: s->size) 41 | */ 42 | 43 | /** Structure of a sound in OSLib. You can make your own "drivers" for other sound types but I can't guarantee they will be forward compatible, so please release your source when creating one ;-) */ 44 | typedef struct OSL_SOUND { 45 | char filename[64]; //!< Original file name, so that the file can be reopen after a resume from stand-by. Used only if the song is streamed. 46 | void *data; //!< User sound data (depends on the sound file format). 47 | void *dataplus; //!< Extended user sound data. 48 | int baseoffset; //!< Offset from the beginning of the file (without the header). Used to resume the sound after stand-by. 49 | int format; //!< - 50 | int divider; //!< - 51 | int size; //!< - 52 | int mono; //!< 0x10 for Mono, 0 for Stereo output 53 | int isStreamed; //!< Holds whether the sound is streamed or not. 54 | u16 volumeLeft; //!< Volume of the left channel (set volumeLeft == volumeRight for normal operation, another value creates a panning effect) 55 | u16 volumeRight; //!< Volume of the right channel 56 | int suspendNumber; //!< - 57 | int (*endCallback)(struct OSL_SOUND*, int); //!< Function called when the sound has finished to play 58 | u8 userdata[32]; //!< Custom user data 59 | int numSamples; //!< Number of samples per read. Default is osl_audioDefaultNumSamples, that is, 512. 60 | 61 | void (*playSound)(struct OSL_SOUND*); //!< Custom function called when the sound must be played 62 | void (*stopSound)(struct OSL_SOUND*); //!< Custom function called when the sound must be stopped (not paused, completely stopped) 63 | int (*audioCallback)(unsigned int, void*, unsigned int); //!< Custom function which must feed a buffer with a certain number of samples 64 | VIRTUAL_FILE* (*standBySound)(struct OSL_SOUND*); //!< Function called when the PSP enters in stand by mode 65 | VIRTUAL_FILE** (*reactiveSound)(struct OSL_SOUND*, VIRTUAL_FILE*); //!< Function called when the sound must be reactivated (after a stand by). 66 | void (*deleteSound)(struct OSL_SOUND*); //!< Custom function called to destroy the sound. 67 | } OSL_SOUND; 68 | 69 | /** Currently playing channel status, system part. Don't access it */ 70 | typedef struct { 71 | int active; 72 | int threadhandle; 73 | int handle; 74 | // int volumeleft; 75 | // int volumeright; 76 | void (*callback)(unsigned int channel, void *buf, unsigned int reqn); 77 | int inProgress; 78 | } osl_audio_channelinfo; 79 | 80 | /** Currently playing channel, user part. Only sound drivers should play with this, the user will only work with OSL_SOUND. */ 81 | typedef struct { 82 | void *data, *dataplus; 83 | int format, divider, size, mono, isStreamed; 84 | // int volumeLeft, volumeRight; 85 | int numSamples; 86 | OSL_SOUND *sound; 87 | } OSL_AUDIO_VOICE; 88 | 89 | 90 | /** @defgroup audio_general General 91 | 92 | General tasks for audio. 93 | @{ 94 | */ 95 | 96 | /** Initializes the audio system. Don't forget to call this before trying to play any song. Maybe your application will not crash if you forget it, but weird things can happen randomly! */ 97 | extern int oslInitAudio(); 98 | /** Deinitializes the audio system. No sound will output anymore. Call this for clean-up (saves memory). 99 | 100 | However, before deinitializing audio, you should delete all sounds by yourself (oslDeleteSound). */ 101 | extern void oslDeinitAudio(); 102 | 103 | /** Initializes Media Engine audio (need to call this before loading an at3 or mp3 file). 104 | 105 | \params formats 106 | One or more of the #oslInitAudioME_formats values, added together. 107 | 108 | \b Important: Requires to be executed in kernel mode! Look for kernel mode programs in the documentation. */ 109 | extern void oslInitAudioME(int formats); 110 | 111 | //extern void oslClearAudio(); 112 | 113 | /** Number of audio channels. No more than 8 sounds can be played at once! */ 114 | #define OSL_NUM_AUDIO_CHANNELS 8 115 | /** This is the default volume for audio channels. Though the real maximum value is 0xffff, this value is the maximum value before distorsion may happen. */ 116 | #define OSL_VOLUME_MAX 0x8000 117 | 118 | /** Sets the default number of samples per read. If you generate more samples at once, OSLib will need less calls, making it faster. But more data will be read at once, and the CPU will be blocked for 119 | a longer time, which may be too much and cause screen tearing. It's only an advanced command, let it to default (512) if you don't know exactly what you are doing. */ 120 | #define oslAudioSetDefaultSampleNumber(num) (osl_audioDefaultNumSamples = num) 121 | 122 | //Don't access these 123 | extern int osl_audioDefaultNumSamples; 124 | 125 | /** @} */ // end of audio_general 126 | 127 | 128 | /** @defgroup audio_load Loading 129 | 130 | Loading sounds. 131 | @{ 132 | */ 133 | 134 | /** Loads a sound type and determines its format with its extension. 135 | \param filename 136 | Name of the file to be loaded. If the song must be streamed, always use files that are stored on the Memory Stick, alternate file sources have not been tested and may not always work properly. 137 | \param stream 138 | Either OSL_FMT_STREAM (sound is streamed) or OSL_FMT_NONE (sound is entierly loaded in memory). Streamed sounds use less memory but require more CPU power. 139 | 140 | This remark applies for every format: the biggest the sample rate, the more CPU time it will need to be played back. 141 | */ 142 | extern OSL_SOUND *oslLoadSoundFile(const char *filename, int stream); 143 | /** Loads a WAV sound file. See oslLoadSoundFile for more information. */ 144 | extern OSL_SOUND *oslLoadSoundFileWAV(const char *filename, int stream); 145 | /** Loads a BGM sound file. See oslLoadSoundFile for more information. BGM is an audio format specific to OSLib. It stores "true" sound, taking less room than WAV, but is only mono. 146 | 147 | You can find an encoder in the distribution. 148 | 149 | Some other formats are available in the OSTools extension library, take a look to it. */ 150 | extern OSL_SOUND *oslLoadSoundFileBGM(const char *filename, int stream); 151 | 152 | /** Loads a MOD sound file. Requires to link with the mod library (-lmikmod in the library list). Currently supports the following formats: .mod, .it, .s3m and .xm. 153 | 154 | You can only play one MOD song at a time! I didn't try to do it on 2 voices but I imagine it would play at a double speed and louder. Do NOT think this is normal and most important do not use it in your 155 | game! This behavior is very likely to change in a future version of OSLib. 156 | 157 | \b Note: Streaming is not supported for MOD playback. Files will always be loaded from RAM, so don't give OSL_FMT_STREAM as an argument for \e stream, else you may experience problems in a future 158 | OSLib version! Set it to OSL_FMT_NONE for the moment. 159 | 160 | \b Warning: Note that MOD playback is excessively heavy as much in terms of required memory (increases the size of your EBOOT) as speed (watch out the CPU usage when or without MOD playback: 161 | with some songs it may increase up to 50%%!). 162 | To reduce this problem, you can define the sample rate (#oslSetModSampleRate) which can help a bit. But it's still not recommended if you need a lot of CPU power for your game. However if your game does 163 | not require a high CPU load but only a high GPU (graphic) load, this is not a problem, as sound decoding will mostly be done during periods where the CPU is waiting on the GPU to finish its drawing, and 164 | thus not affecting performance very much. */ 165 | OSL_SOUND *oslLoadSoundFileMOD(const char *filename, int stream); 166 | 167 | /** Loads an MP3 file. It is necessary to call #oslInitAudioME in kernel mode before, else your program will crash! */ 168 | OSL_SOUND *oslLoadSoundFileMP3(const char *filename, int stream); 169 | /** Loads an AT3 file. It is necessary to call #oslInitAudioME in kernel mode before, else your program will crash! */ 170 | OSL_SOUND *oslLoadSoundFileAT3(const char *filename, int stream); 171 | 172 | 173 | /** Sets the sample rate for the MOD player (it does not affect other formats!). Decreasing it reduces the audio quality but can reduce the CPU load required to decode MOD audio. 174 | \param freq 175 | Sample rate (in samples per second). The higher it is, the more CPU load is needed. Allowed values are 44100 (good quality), 22050 (medium quality), 11025 (low quality). 176 | The default is 44100, but if you need to use less CPU power, 22050 is a good choice. 11025 uses less CPU but it's not worth the big loss of quality IMHO, but it depends from the track in question. 177 | \param stereo 178 | The only allowed value is 1 currently. Do NOT put any other value! 179 | \param shift 180 | The shift sets the ratio for playback. Values are a power of two. 0 means 1x, 1 means 2x, 2 means 4x, etc. 181 | This parameter is necessary if you use another frequency than 44100, because the audio stream will always be played at 44100, due to the internal PSP audio capabilities. If you play a 22050 stream 182 | at 44100 Hz it will play twice as fast! To reduce this problem, set a shift of 1 (meaning 2x). This parameter is here so that you can make an accelerated music effect. 183 | For example if you set \e shift to 0 (44100 Hz) and \e freq to 22050, the sound will play twice as fast. Note that in this case (and only in this case!) the \e freq parameter can have another value 184 | than 11025, 22050 or 44100. 185 | 186 | \b Note: it is unrecommended to set any other sample rate than 44100 (with a shift of 0), 22050 (with a shift of 1), 11025 (with a shift of 2) as it's tricky and not heavily tested. Also, it is currently 187 | not sure that this behavior will be kept in the future. 188 | \code 189 | //The 3 "normal" possibilities, you can copy them as is if you need them. 190 | oslSetModSampleRate(11025, 1, 2); //Very low CPU, bad sound 191 | oslSetModSampleRate(22050, 1, 1); //Low CPU, medium sound 192 | oslSetModSampleRate(44100, 1, 0); //Normal CPU usage, good sound 193 | 194 | //Extended possibilities, not recommended as said above. 195 | oslSetModSampleRate(22050, 1, 0); //Twice the normal speed 196 | oslSetModSampleRate(33075, 1, 0); //3/2 speed 197 | oslSetModSampleRate(11025, 1, 1); //2x speed, lower quality 198 | \endcode */ 199 | void oslSetModSampleRate(int freq, int stereo, int shift); 200 | 201 | 202 | /** @} */ // end of audio_load 203 | 204 | 205 | /** @defgroup audio_play Player 206 | 207 | Audio player. 208 | @{ 209 | */ 210 | 211 | /** Plays a sound on the specified channel. The channel is the channel number from 0 to 7. Playing a sound on an active channel stops the currently playing sound. The advantage of having 8 channels is that 212 | you can play up to 8 sounds at the same time. 213 | 214 | \code 215 | //These must of course be loaded, but I skipped this step as it's not the goal of this sample. 216 | OSL_SOUND *coin, *jump, *stomp, *music; 217 | //Play the music on an own voice reserved for it (0) 218 | oslPlaySound(music, 0); 219 | [...] 220 | //Play the "coin" sound on another channel than the music else it will replace it 221 | oslPlaySound(coin, 1); 222 | [...] 223 | //Play a second time the "coin" sound. Putting it on the same channel will replace the currently playing coin sound. 224 | //This is useful as sometimes you don't want or need two specific sounds to be played together. 225 | //Especially, imagine your character is in a field of coins, and you take 10 coins in a short period. 226 | //Having 10 times the coin sound played in a different channel would be very noisy. 227 | //So it's good to stop the old sound to play the new one. 228 | oslPlaySound(coin, 1); 229 | [...] 230 | //The jump sound have its own channel, the coin sound will be playing together with the jump and the music. 231 | oslPlaySound(jump, 2); 232 | [...] 233 | //The stomp sound will replace the coin sound as it's played on the same channel. 234 | //But the jump sound and the music are left unaffected. 235 | oslPlaySound(stomp, 1); 236 | \endcode */ 237 | extern void oslPlaySound(OSL_SOUND *s, int voice); 238 | /** Stops a sound currently playing. */ 239 | extern void oslStopSound(OSL_SOUND *s); 240 | /** Pauses a sound. 241 | \param s 242 | Sound to pause or resume. 243 | \param pause 244 | - 1: Pause the sound 245 | - 0: Resume the sound (where it was paused) 246 | - -1: Toggle pause / play 247 | */ 248 | extern void oslPauseSound(OSL_SOUND *s, int pause); 249 | 250 | /** Deletes a sound, freeing associated memory. If the sound is currently being played, it will be stopped. */ 251 | extern void oslDeleteSound(OSL_SOUND *s); 252 | 253 | /** Call this in your loop if a file is streamed and the PSP can go in stand-by mode. If you forget to call this often enough, the sound will not resume after a return from stand-by. 254 | 255 | \b Important: This function is called by #oslEndFrame, so you don't need to do it twice if you've added a #oslEndFrame call to your code. Consider using #oslEndFrame instead of oslAudioVSync for your 256 | new projects, as it's cleaner. */ 257 | extern void oslAudioVSync(); 258 | 259 | /** Sets the function to be called after a sound has finished to play. 260 | 261 | The function has the following parameters: OSL_SOUND* (pointer to the sound), int (number of the voice). It must return the following value: 0 to destroy the channel, 1 to continue playback 262 | (imagine you play something else or replay the same song). 263 | 264 | \code 265 | OSL_SOUND *s; 266 | 267 | int myFunction(OSL_SOUND *s, int voice) { 268 | //We replay the same sound on the same voice 269 | oslPlaySound(s, voice); 270 | //Return 1 because we continue sound playback 271 | return 1; 272 | } 273 | 274 | oslSetSoundEndCallback(sound, myFunction); 275 | oslPlaySound(sound, 0); 276 | [...] \endcode */ 277 | #define oslSetSoundEndCallback(s, fct) (s->endCallback = (fct)) 278 | 279 | /** Sets whether sound is looped or not. This is done by defining the same callback as in the example above. */ 280 | #define oslSetSoundLoop(s,loop) oslSetSoundEndCallback(s, (loop)?oslSoundLoopFunc:NULL) 281 | 282 | 283 | //Do not use this. 284 | enum {OSL_FMT_NONE=0}; 285 | enum {OSL_FMT_MASK=0xff}; 286 | //enum {OSL_FMT_WAV=0x100, OSL_FMT_ADPCM}; //Fichier wav 287 | enum {OSL_FMT_MONO=0, OSL_FMT_STEREO=0x200, OSL_FMT_STREAM=0x400}; 288 | enum {OSL_FMT_44K=0, OSL_FMT_22K=1, OSL_FMT_11K=2}; 289 | 290 | /** Formats de fichier � initialiser pour #oslInitAudioME. */ 291 | enum oslInitAudioME_formats { 292 | OSL_FMT_AT3 = 1, //!< Atrac3 and Atrac3+ 293 | OSL_FMT_MP3 = 2, //!< Mpeg Audio-Layer 3 294 | OSL_FMT_ALL = 3, //!< All formats 295 | }; 296 | 297 | 298 | /** @} */ // end of audio_play 299 | 300 | 301 | 302 | /** @defgroup audio_adv Advanced 303 | 304 | Lower level work with active audio channels. 305 | @{ 306 | */ 307 | 308 | /** Returns the channel on which a specific sound is being played, or -1 if the sound is not being played currently. */ 309 | extern int oslGetSoundChannel(OSL_SOUND *s); 310 | 311 | //Internal 312 | extern int oslAudioCreateChannel(int i, int format, int numSamples, OSL_SOUND *s); 313 | extern int oslAudioRecreateChannel(int i, int format, int numSamples, OSL_SOUND *s); 314 | extern void oslAudioDeleteChannel(int i); 315 | extern int oslAudioOutBlocking(unsigned int channel, unsigned int vol1, unsigned int vol2, void *buf); 316 | typedef int (* oslAudioThreadfunc_t)(int args, void *argp); 317 | 318 | extern volatile int osl_audioActive[OSL_NUM_AUDIO_CHANNELS], osl_audioBusy[OSL_NUM_AUDIO_CHANNELS]; 319 | extern int osl_suspendNumber; 320 | 321 | 322 | /** Represents the currently active voices properties. Especially, you can find a 'sound' member, which holds a pointer to the currently playing sound in this voice. */ 323 | extern OSL_AUDIO_VOICE osl_audioVoices[OSL_NUM_AUDIO_CHANNELS]; 324 | 325 | /** Standard callback function that loops a sound. Set as the sound end callback by oslSetSoundLoop. */ 326 | extern int oslSoundLoopFunc(OSL_SOUND *s, int voice); 327 | 328 | /** @} */ // end of audio_adv 329 | 330 | 331 | /** @} */ // end of audio 332 | 333 | 334 | #ifdef __cplusplus 335 | } 336 | #endif 337 | 338 | #endif 339 | 340 | -------------------------------------------------------------------------------- /src/osl_sound/bgm.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IridescentRose/QuickGame/37ff7445017eb9d5230d87312d80a5814fb16f1f/src/osl_sound/bgm.c -------------------------------------------------------------------------------- /src/osl_sound/bgm.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IridescentRose/QuickGame/37ff7445017eb9d5230d87312d80a5814fb16f1f/src/osl_sound/bgm.h -------------------------------------------------------------------------------- /src/osl_sound/media.c: -------------------------------------------------------------------------------- 1 | #include "oslib.h" 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | VIRTUAL_FILE *handle; 8 | unsigned long *codecBuffer; 9 | u8* dataBuffer; 10 | u32 sample_per_frame; 11 | u32 data_start_init; 12 | 13 | //Codec specific 14 | u16 at3_type; 15 | u16 at3_data_align; 16 | u16 at3_channel_mode; 17 | u8 at3_at3plus_flagdata[2]; 18 | u32 at3_data_size; 19 | u8 at3_getEDRAM; 20 | u32 at3_channels; 21 | u32 at3_samplerate; 22 | } AT3_INFO; 23 | 24 | #define TYPE_ATRAC3 0x270 25 | #define TYPE_ATRAC3PLUS 0xFFFE 26 | 27 | 28 | typedef struct { 29 | VIRTUAL_FILE *handle; 30 | unsigned long *codecBuffer; 31 | //u8* dataBuffer; 32 | unsigned char dataBuffer[2889]__attribute__((aligned(64))); 33 | u32 sample_per_frame; 34 | u32 data_start_init; 35 | 36 | //Codec specific 37 | u32 channels; 38 | u32 samplerate; 39 | u32 data_start; 40 | u8 getEDRAM; 41 | } MP3_INFO; 42 | 43 | 44 | static int samplerates[4][3] = 45 | { 46 | {11025, 12000, 8000,},//mpeg 2.5 47 | {0, 0, 0,}, //reserved 48 | {22050, 24000, 16000,},//mpeg 2 49 | {44100, 48000, 32000}//mpeg 1 50 | }; 51 | static int bitrates[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; 52 | static int bitrates_v2[] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }; 53 | 54 | #define NUMBLOCKS 4 55 | 56 | //Load a module: 57 | SceUID LoadStartAudioModule(char *modname, int partition){ 58 | SceKernelLMOption option; 59 | SceUID modid; 60 | 61 | memset(&option, 0, sizeof(option)); 62 | option.size = sizeof(option); 63 | option.mpidtext = partition; 64 | option.mpiddata = partition; 65 | option.position = 0; 66 | option.access = 1; 67 | 68 | modid = sceKernelLoadModule(modname, 0, &option); 69 | if (modid < 0) 70 | return modid; 71 | 72 | return sceKernelStartModule(modid, 0, NULL, NULL, NULL); 73 | } 74 | 75 | int GetID3TagSize(char *fname) 76 | { 77 | SceUID fd; 78 | char header[10]; 79 | int size = 0; 80 | fd = sceIoOpen(fname, PSP_O_RDONLY, 0777); 81 | if (fd < 0) 82 | return 0; 83 | 84 | sceIoRead(fd, header, sizeof(header)); 85 | sceIoClose(fd); 86 | 87 | if (!strncmp((char*)header, "ea3", 3) || !strncmp((char*)header, "EA3", 3) 88 | ||!strncmp((char*)header, "ID3", 3)) 89 | { 90 | //get the real size from the syncsafe int 91 | size = header[6]; 92 | size = (size<<7) | header[7]; 93 | size = (size<<7) | header[8]; 94 | size = (size<<7) | header[9]; 95 | 96 | size += 10; 97 | 98 | if (header[5] & 0x10) //has footer 99 | size += 10; 100 | return size; 101 | } 102 | return 0; 103 | } 104 | 105 | 106 | int SeekNextFrameMP3(VIRTUAL_FILE *fd) 107 | { 108 | int offset = 0; 109 | unsigned char buf[1024]; 110 | unsigned char *pBuffer; 111 | int i; 112 | int size = 0; 113 | 114 | VirtualFileSeek(fd, 0, PSP_SEEK_CUR); 115 | offset = VirtualFileTell(fd); 116 | VirtualFileRead(buf, sizeof(buf), 1, fd); 117 | if (!strncmp((char*)buf, "ID3", 3) || !strncmp((char*)buf, "ea3", 3)) //skip past id3v2 header, which can cause a false sync to be found 118 | { 119 | //get the real size from the syncsafe int 120 | size = buf[6]; 121 | size = (size<<7) | buf[7]; 122 | size = (size<<7) | buf[8]; 123 | size = (size<<7) | buf[9]; 124 | 125 | size += 10; 126 | 127 | if (buf[5] & 0x10) //has footer 128 | size += 10; 129 | } 130 | 131 | VirtualFileSeek(fd, offset, PSP_SEEK_SET); //now seek for a sync 132 | while(1) 133 | { 134 | VirtualFileSeek(fd, 0, PSP_SEEK_CUR); 135 | offset = VirtualFileTell(fd); 136 | size = VirtualFileRead(buf, sizeof(buf), 1, fd); 137 | 138 | if (size <= 2)//at end of file 139 | return -1; 140 | 141 | if (!strncmp((char*)buf, "EA3", 3))//oma mp3 files have non-safe ints in the EA3 header 142 | { 143 | VirtualFileSeek(fd, (buf[4]<<8)+buf[5], PSP_SEEK_CUR); 144 | continue; 145 | } 146 | 147 | pBuffer = buf; 148 | for( i = 0; i < size; i++) 149 | { 150 | //if this is a valid frame sync (0xe0 is for mpeg version 2.5,2+1) 151 | if ( (pBuffer[i] == 0xff) && ((pBuffer[i+1] & 0xE0) == 0xE0)) 152 | { 153 | offset += i; 154 | VirtualFileSeek(fd, offset, PSP_SEEK_SET); 155 | return offset; 156 | } 157 | } 158 | //go back two bytes to catch any syncs that on the boundary 159 | VirtualFileSeek(fd, -2, PSP_SEEK_CUR); 160 | } 161 | } 162 | 163 | 164 | void initME(){ 165 | if (sceKernelDevkitVersion() == 0x01050001) 166 | { 167 | LoadStartAudioModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL); 168 | LoadStartAudioModule("flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL); 169 | } 170 | else 171 | { 172 | sceUtilityLoadAvModule(PSP_AV_MODULE_AVCODEC); 173 | } 174 | } 175 | 176 | static int osl_at3Inited = 0, osl_mp3Inited = 0; 177 | 178 | static int osl_at3Init() { 179 | int result = 0; 180 | if (!osl_at3Inited) 181 | initME(); 182 | 183 | osl_at3Inited = 1; 184 | 185 | return result; 186 | } 187 | 188 | static int osl_mp3Init() { 189 | int result = 0; 190 | 191 | if (!osl_mp3Inited) 192 | initME(); 193 | 194 | osl_mp3Inited = 1; 195 | return result; 196 | } 197 | 198 | static void osl_mp3DestroyInfo(MP3_INFO *info) { 199 | 200 | if (info) { 201 | if (info->codecBuffer) 202 | free(info->codecBuffer); 203 | 204 | //if (info->dataBuffer) 205 | // free(info->dataBuffer); 206 | 207 | if (info->handle) 208 | VirtualFileClose(info->handle); 209 | 210 | if (info->getEDRAM) 211 | sceAudiocodecReleaseEDRAM(info->codecBuffer); 212 | 213 | free(info); 214 | } 215 | } 216 | 217 | static MP3_INFO *osl_mp3CreateInfo() { 218 | int success = 0; 219 | MP3_INFO *info; 220 | 221 | info = (MP3_INFO*)malloc(sizeof(MP3_INFO)); 222 | 223 | if (info) { 224 | memset(info, 0, sizeof(MP3_INFO)); 225 | 226 | //Allocate memory for the codec param buffer 227 | info->codecBuffer = memalign(64, 65 * sizeof(unsigned long)); 228 | 229 | if (info->codecBuffer) { 230 | memset(info->codecBuffer, 0, 65 * sizeof(unsigned long)); 231 | success = 1; 232 | } 233 | } 234 | 235 | //Clean up if failed 236 | if (!success) { 237 | osl_mp3DestroyInfo(info); 238 | info = NULL; 239 | } 240 | 241 | return info; 242 | } 243 | 244 | static void osl_at3DestroyInfo(AT3_INFO *info) { 245 | //Does the same 246 | osl_mp3DestroyInfo((MP3_INFO*)info); 247 | } 248 | 249 | 250 | static int osl_mp3Load(const char *fileName, MP3_INFO *info) { 251 | int success = 0; 252 | 253 | //Try to load the file 254 | info->handle = VirtualFileOpen((void*)fileName, 0, VF_AUTO, VF_O_READ); 255 | if (info->handle) { 256 | info->channels = 2; 257 | info->samplerate = 44100; //this is mp3 file's samplerate, also can be 48000,.... 258 | info->sample_per_frame = 1152; 259 | 260 | info->data_start_init = GetID3TagSize((char*)fileName); 261 | VirtualFileSeek(info->handle, info->data_start_init, PSP_SEEK_SET); 262 | 263 | info->data_start_init = VirtualFileTell(info->handle); 264 | 265 | //Initialize the codec 266 | if (sceAudiocodecCheckNeedMem(info->codecBuffer, 0x1002) >= 0) { 267 | if (sceAudiocodecGetEDRAM(info->codecBuffer, 0x1002) >= 0) { 268 | info->getEDRAM = 1; 269 | 270 | if (sceAudiocodecInit(info->codecBuffer, 0x1002) >= 0) 271 | success = 1; 272 | } 273 | } 274 | } 275 | return success; 276 | } 277 | 278 | static AT3_INFO *osl_at3CreateInfo() { 279 | int success = 0; 280 | AT3_INFO *info; 281 | 282 | info = (AT3_INFO*)malloc(sizeof(AT3_INFO)); 283 | 284 | if (info) { 285 | memset(info, 0, sizeof(AT3_INFO)); 286 | 287 | //Allocate memory for the codec param buffer 288 | info->codecBuffer = memalign(64, 65 * sizeof(unsigned long)); 289 | 290 | if (info->codecBuffer) { 291 | memset(info->codecBuffer, 0, 65 * sizeof(unsigned long)); 292 | success = 1; 293 | } 294 | } 295 | 296 | //Clean up if failed 297 | if (!success) { 298 | osl_at3DestroyInfo(info); 299 | info = NULL; 300 | } 301 | 302 | return info; 303 | } 304 | 305 | static int osl_at3Load(const char *fileName, AT3_INFO *info) { 306 | int success = 0; 307 | 308 | //Try to load the file 309 | info->handle = VirtualFileOpen((void*)fileName, 0, VF_AUTO, VF_O_READ); 310 | info->data_start_init = GetID3TagSize((char*)fileName); 311 | VirtualFileSeek(info->handle, info->data_start_init, PSP_SEEK_SET); 312 | 313 | if (info->handle) { 314 | u32 riff_header[2]; 315 | if (VirtualFileRead(riff_header, 8, 1, info->handle) != 8) 316 | goto end; 317 | 318 | //RIFF 319 | if (riff_header[0] != 0x46464952) 320 | goto end; 321 | 322 | u32 wavefmt_header[3]; 323 | 324 | if (VirtualFileRead(wavefmt_header, 12, 1, info->handle) != 12) 325 | goto end; 326 | 327 | if (wavefmt_header[0] != 0x45564157 || wavefmt_header[1] != 0x20746D66) 328 | goto end; 329 | 330 | u8* wavefmt_data = (u8*)malloc(wavefmt_header[2]); 331 | if (wavefmt_data == NULL) 332 | goto end; 333 | 334 | if (VirtualFileRead(wavefmt_data, wavefmt_header[2], 1, info->handle) != wavefmt_header[2] ) { 335 | free(wavefmt_data); 336 | goto end; 337 | } 338 | 339 | info->at3_type = *((u16*)wavefmt_data); 340 | info->at3_channels = *((u16*)(wavefmt_data+2)); 341 | info->at3_samplerate = *((u32*)(wavefmt_data+4)); 342 | info->at3_data_align = *((u16*)(wavefmt_data+12)); 343 | 344 | if (info->at3_type == TYPE_ATRAC3PLUS) { 345 | info->at3_at3plus_flagdata[0] = wavefmt_data[42]; 346 | info->at3_at3plus_flagdata[1] = wavefmt_data[43]; 347 | } 348 | 349 | free(wavefmt_data); 350 | 351 | u32 data_header[2]; 352 | if (VirtualFileRead(data_header, 8, 1, info->handle) != 8) 353 | goto end; 354 | 355 | //data 356 | while (data_header[0] != 0x61746164) { 357 | VirtualFileSeek(info->handle, data_header[1], PSP_SEEK_CUR); 358 | if (VirtualFileRead(data_header, 8, 1, info->handle) != 8) 359 | goto end; 360 | } 361 | 362 | info->data_start_init = VirtualFileTell(info->handle); 363 | info->at3_data_size = data_header[1]; 364 | 365 | if (info->at3_data_size % info->at3_data_align != 0) 366 | goto end; 367 | 368 | if (info->at3_type == TYPE_ATRAC3) { 369 | info->at3_channel_mode = 0x0; 370 | if (info->at3_data_align == 0xC0) // atract3 have 3 bitrate, 132k,105k,66k, 132k align=0x180, 105k align = 0x130, 66k align = 0xc0 371 | info->at3_channel_mode = 0x1; 372 | info->sample_per_frame = 1024; 373 | info->dataBuffer = (u8*)memalign(64, 0x180); 374 | if (info->dataBuffer == NULL) 375 | goto end; 376 | info->codecBuffer[26] = 0x20; 377 | if ( sceAudiocodecCheckNeedMem(info->codecBuffer, 0x1001) < 0 ) 378 | goto end; 379 | if ( sceAudiocodecGetEDRAM(info->codecBuffer, 0x1001) < 0 ) 380 | goto end; 381 | info->at3_getEDRAM = 1; 382 | info->codecBuffer[10] = 4; 383 | info->codecBuffer[44] = 2; 384 | if (info->at3_data_align == 0x130 ) 385 | info->codecBuffer[10] = 6; 386 | if (sceAudiocodecInit(info->codecBuffer, 0x1001) < 0 ) { 387 | goto end; 388 | } 389 | success = 1; 390 | } 391 | else if (info->at3_type == TYPE_ATRAC3PLUS) { 392 | info->sample_per_frame = 2048; 393 | int temp_size = info->at3_data_align + 8; 394 | int mod_64 = temp_size & 0x3f; 395 | if (mod_64 != 0) temp_size += 64 - mod_64; 396 | info->dataBuffer = (u8*)memalign(64, temp_size); 397 | if (info->dataBuffer == NULL) 398 | goto end; 399 | info->codecBuffer[5] = 0x1; 400 | info->codecBuffer[10] = info->at3_at3plus_flagdata[1]; 401 | info->codecBuffer[10] = (info->codecBuffer[10] << 8 ) | info->at3_at3plus_flagdata[0]; 402 | info->codecBuffer[12] = 0x1; 403 | info->codecBuffer[14] = 0x1; 404 | if (sceAudiocodecCheckNeedMem(info->codecBuffer, 0x1000) < 0) 405 | goto end; 406 | if (sceAudiocodecGetEDRAM(info->codecBuffer, 0x1000) < 0) 407 | goto end; 408 | info->at3_getEDRAM = 1; 409 | if (sceAudiocodecInit(info->codecBuffer, 0x1000) < 0) { 410 | goto end; 411 | } 412 | success = 1; 413 | } 414 | else 415 | goto end; 416 | } 417 | 418 | end: 419 | return success; 420 | } 421 | 422 | 423 | /* 424 | Callbacks standard 425 | */ 426 | void oslAudioCallback_StopSound_ME(OSL_SOUND *s) { 427 | MP3_INFO *info = (MP3_INFO*)s->data; 428 | info->data_start = info->data_start_init; 429 | VirtualFileSeek(info->handle, info->data_start, PSP_SEEK_SET); 430 | } 431 | 432 | void oslAudioCallback_PlaySound_ME(OSL_SOUND *s) { 433 | oslAudioCallback_StopSound_ME(s); 434 | } 435 | 436 | int oslAudioCallback_AudioCallback_MP3(unsigned int i, void* buf, unsigned int length) { 437 | int eof = 0; 438 | MP3_INFO *info = (MP3_INFO*)osl_audioVoices[i].data; 439 | unsigned long decode_type = 0x1002; 440 | unsigned char mp3_header_buf[4]; 441 | 442 | info->data_start = SeekNextFrameMP3(info->handle); 443 | 444 | start: 445 | if (VirtualFileRead(mp3_header_buf, 4, 1, info->handle) != 4) { 446 | eof = 1; 447 | goto end; 448 | } 449 | 450 | int mp3_header = mp3_header_buf[0]; 451 | mp3_header = (mp3_header<<8) | mp3_header_buf[1]; 452 | mp3_header = (mp3_header<<8) | mp3_header_buf[2]; 453 | mp3_header = (mp3_header<<8) | mp3_header_buf[3]; 454 | 455 | int bitrate = (mp3_header & 0xf000) >> 12; 456 | int padding = (mp3_header & 0x200) >> 9; 457 | int version = (mp3_header & 0x180000) >> 19; 458 | int samplerate = samplerates[version][ (mp3_header & 0xC00) >> 10 ]; 459 | 460 | if ((bitrate > 14) || (version == 1) || (samplerate == 0) || (bitrate == 0))//invalid frame, look for the next one 461 | { 462 | info->data_start = SeekNextFrameMP3(info->handle); 463 | if(info->data_start < 0) 464 | { 465 | eof = 1; 466 | goto end; 467 | } 468 | goto start; 469 | } 470 | 471 | int frame_size = 0; 472 | if (version == 3) //mpeg-1 473 | { 474 | info->sample_per_frame = 1152; 475 | frame_size = 144000*bitrates[bitrate]/samplerate + padding; 476 | }else{ 477 | info->sample_per_frame = 576; 478 | frame_size = 72000*bitrates_v2[bitrate]/samplerate + padding; 479 | } 480 | //int frame_size = 144000*bitrates[bitrate]/info->samplerate + padding; 481 | 482 | //if (info->dataBuffer) 483 | // free(info->dataBuffer); 484 | //info->dataBuffer = (u8*)memalign(64, frame_size); 485 | 486 | VirtualFileSeek(info->handle, info->data_start, PSP_SEEK_SET); //seek back 487 | 488 | if ( VirtualFileRead(info->dataBuffer, frame_size, 1, info->handle) != frame_size) { 489 | eof = 1; 490 | goto end; 491 | } 492 | 493 | info->data_start += frame_size; 494 | 495 | info->codecBuffer[7] = info->codecBuffer[10] = frame_size; 496 | info->codecBuffer[9] = info->sample_per_frame * 4; 497 | 498 | info->codecBuffer[6] = (unsigned long)info->dataBuffer; 499 | info->codecBuffer[8] = (unsigned long)buf; 500 | 501 | int res = sceAudiocodecDecode(info->codecBuffer, decode_type); 502 | if ( res < 0 ){ 503 | info->data_start = SeekNextFrameMP3(info->handle); 504 | if(info->data_start < 0) 505 | { 506 | eof = 1; 507 | goto end; 508 | } 509 | goto start; 510 | } 511 | 512 | end: 513 | if (eof) { 514 | // osl_mp3CleanUp(); 515 | //Efface le channel 516 | return 0; 517 | } 518 | 519 | return 1; 520 | } 521 | 522 | VIRTUAL_FILE **oslAudioCallback_ReactiveSound_ME(OSL_SOUND *s, VIRTUAL_FILE *f) { 523 | MP3_INFO *info = (MP3_INFO*)s->data; 524 | VIRTUAL_FILE **w = &info->handle; 525 | return w; 526 | } 527 | 528 | VIRTUAL_FILE *oslAudioCallback_StandBy_ME(OSL_SOUND *s) { 529 | MP3_INFO *info = (MP3_INFO*)s->data; 530 | return info->handle; 531 | } 532 | 533 | void oslAudioCallback_DeleteSound_ME(OSL_SOUND *s) { 534 | osl_mp3DestroyInfo((MP3_INFO*)s->data); 535 | s->data = NULL; 536 | } 537 | 538 | int oslAudioCallback_AudioCallback_AT3(unsigned int i, void* buf, unsigned int length) { 539 | AT3_INFO *info = (AT3_INFO*)osl_audioVoices[i].data; 540 | int eof = 0; 541 | unsigned long decode_type; 542 | 543 | if (info->at3_type == TYPE_ATRAC3) { 544 | memset(info->dataBuffer, 0, 0x180); 545 | if (VirtualFileRead(info->dataBuffer, info->at3_data_align, 1, info->handle) != info->at3_data_align) { 546 | eof = 1; 547 | goto end; 548 | } 549 | if (info->at3_channel_mode) { 550 | memcpy(info->dataBuffer + info->at3_data_align, info->dataBuffer, info->at3_data_align); 551 | } 552 | decode_type = 0x1001; 553 | } 554 | else { 555 | memset(info->dataBuffer, 0, info->at3_data_align + 8); 556 | info->dataBuffer[0] = 0x0F; 557 | info->dataBuffer[1] = 0xD0; 558 | info->dataBuffer[2] = info->at3_at3plus_flagdata[0]; 559 | info->dataBuffer[3] = info->at3_at3plus_flagdata[1]; 560 | if (VirtualFileRead(info->dataBuffer + 8, info->at3_data_align, 1, info->handle) != info->at3_data_align) { 561 | eof = 1; 562 | goto end; 563 | } 564 | decode_type = 0x1000; 565 | } 566 | 567 | info->codecBuffer[6] = (unsigned long)info->dataBuffer; 568 | info->codecBuffer[8] = (unsigned long)buf; 569 | 570 | int res = sceAudiocodecDecode(info->codecBuffer, decode_type); 571 | if ( res < 0 ) 572 | eof = 1; 573 | 574 | end: 575 | if (eof) { 576 | // osl_mp3CleanUp(); 577 | //Efface le channel 578 | return 0; 579 | } 580 | 581 | return 1; 582 | } 583 | 584 | 585 | static void soundInit(const char *filename, OSL_SOUND *s, MP3_INFO *info) { 586 | s->data = (void*)info; 587 | 588 | s->endCallback = NULL; 589 | s->volumeLeft = s->volumeRight = OSL_VOLUME_MAX; 590 | //No special format 591 | s->format = 0; 592 | //Always stereo output 593 | s->mono = 0; 594 | s->divider = OSL_FMT_44K; 595 | //MP3 files are always streamed for now 596 | s->isStreamed = 1; 597 | int samples_count = info->sample_per_frame; 598 | s->numSamples = samples_count; 599 | 600 | //Streaming special information 601 | if (s->isStreamed) { 602 | if (strlen(filename) < sizeof(s->filename)) 603 | strcpy(s->filename, filename); 604 | s->suspendNumber = osl_suspendNumber; 605 | } 606 | 607 | s->playSound = oslAudioCallback_PlaySound_ME; 608 | s->stopSound = oslAudioCallback_StopSound_ME; 609 | s->standBySound = oslAudioCallback_StandBy_ME; 610 | s->reactiveSound = oslAudioCallback_ReactiveSound_ME; 611 | s->deleteSound = oslAudioCallback_DeleteSound_ME; 612 | } 613 | 614 | OSL_SOUND *oslLoadSoundFileMP3(const char *filename, int stream) { 615 | OSL_SOUND *s = NULL; 616 | MP3_INFO *info; 617 | int success = 0; 618 | 619 | // osl_mp3Init(); 620 | 621 | //Mp3 must be streamed for now 622 | if (stream & OSL_FMT_STREAM) { 623 | 624 | s = (OSL_SOUND*)malloc(sizeof(OSL_SOUND)); 625 | if (s) { 626 | //Never forget that! If any member is added to OSL_SOUND, it is assumed to be zero! 627 | memset(s, 0, sizeof(OSL_SOUND)); 628 | 629 | //Allocate the size of a mp3 info structure 630 | info = osl_mp3CreateInfo(); 631 | 632 | if (info) { 633 | if (osl_mp3Load(filename, info)) { 634 | soundInit(filename, s, info); 635 | s->audioCallback = oslAudioCallback_AudioCallback_MP3; 636 | success = 1; 637 | } 638 | else { 639 | osl_mp3DestroyInfo(info); 640 | info = NULL; 641 | } 642 | } 643 | } 644 | 645 | if (!success) { 646 | if (s) 647 | free(s); 648 | s = NULL; 649 | } 650 | } 651 | 652 | if (!s) 653 | { 654 | //oslHandleLoadNoFailError(filename); 655 | 656 | } 657 | return s; 658 | } 659 | 660 | OSL_SOUND *oslLoadSoundFileAT3(const char *filename, int stream) { 661 | return NULL; 662 | } 663 | 664 | void oslInitAudioME(int formats) { 665 | if (formats & OSL_FMT_AT3) 666 | osl_at3Init(); 667 | if (formats & OSL_FMT_MP3) 668 | osl_mp3Init(); 669 | } 670 | 671 | -------------------------------------------------------------------------------- /src/osl_sound/oslib.h: -------------------------------------------------------------------------------- 1 | #ifndef _OSLIB_H_ 2 | #define _OSLIB_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | //#include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define oslMin(x, y) (((x)<(y))?(x):(y)) 25 | #define oslMax(x, y) (((x)>(y))?(x):(y)) 26 | 27 | 28 | 29 | 30 | #include "VirtualFile.h" 31 | #include "audio.h" 32 | #include "bgm.h" 33 | 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/osl_sound/pgeWav.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "pgeWav.h" 8 | 9 | #define PGE_WAV_MAX_SLOTS 16 10 | 11 | static pgeWav pgeWavInfo[PGE_WAV_MAX_SLOTS]; 12 | static int pgeWavPlaying[PGE_WAV_MAX_SLOTS]; 13 | static int pgeWavId[PGE_WAV_MAX_SLOTS]; 14 | 15 | static short *pgeWavSamples; 16 | static unsigned long pgeWavReq; 17 | static int pgeWavIdFlag = 0; 18 | 19 | static int pgeWavInitFlag = 0; 20 | 21 | #define PGE_NUM_AUDIO_CHANNELS 4 22 | #define PGE_NUM_AUDIO_SAMPLES 1024 23 | #define PGE_VOLUME_MAX 0x8000 24 | 25 | typedef void (* pgeAudioLibCallback)(void *buf, unsigned int reqn, void *pdata); 26 | 27 | typedef struct 28 | { 29 | int threadHandle; 30 | int handle; 31 | int volumeLeft; 32 | int volumeRight; 33 | pgeAudioLibCallback callback; 34 | void *data; 35 | 36 | } pgeAudioLibChannelInfo; 37 | 38 | static int pgeAudioLibReady = 0; 39 | static short pgeAudioLibSoundBuffer[PGE_NUM_AUDIO_CHANNELS][2][PGE_NUM_AUDIO_SAMPLES][2]; 40 | 41 | static pgeAudioLibChannelInfo pgeAudioLibStatus[PGE_NUM_AUDIO_CHANNELS]; 42 | 43 | static volatile int pgeAudioLibTerminate = 0; 44 | 45 | static void pgeAudioLibSetChannelCallback(int channel, pgeAudioLibCallback callback, void *data) 46 | { 47 | volatile pgeAudioLibChannelInfo *pci = &pgeAudioLibStatus[channel]; 48 | 49 | if (callback == 0) 50 | pci->callback = 0; 51 | else 52 | { 53 | pci->callback = callback; 54 | sceKernelWakeupThread(pci->threadHandle); 55 | } 56 | } 57 | 58 | static int pgeAudioLibOutBlocking(unsigned int channel, unsigned int left, unsigned int right, void *data) 59 | { 60 | if (!pgeAudioLibReady) 61 | return(-1); 62 | 63 | if (channel >= PGE_NUM_AUDIO_CHANNELS) 64 | return(-1); 65 | 66 | if (left > PGE_VOLUME_MAX) 67 | left = PGE_VOLUME_MAX; 68 | 69 | if (right > PGE_VOLUME_MAX) 70 | right = PGE_VOLUME_MAX; 71 | 72 | return sceAudioOutputPannedBlocking(pgeAudioLibStatus[channel].handle, left, right, data); 73 | } 74 | 75 | static int pgeAudioLibChannelThread(int args, void *argp) 76 | { 77 | volatile int bufidx = 0; 78 | 79 | int channel = *(int *) argp; 80 | 81 | while (pgeAudioLibTerminate == 0) 82 | { 83 | void *bufptr = &pgeAudioLibSoundBuffer[channel][bufidx]; 84 | pgeAudioLibCallback callback; 85 | callback = pgeAudioLibStatus[channel].callback; 86 | 87 | if (callback) 88 | { 89 | callback(bufptr, PGE_NUM_AUDIO_SAMPLES, pgeAudioLibStatus[channel].data); 90 | pgeAudioLibOutBlocking(channel, pgeAudioLibStatus[channel].volumeLeft, pgeAudioLibStatus[channel].volumeRight, bufptr); 91 | } 92 | else 93 | { 94 | sceKernelSleepThread(); 95 | } 96 | 97 | bufidx = (bufidx ? 0:1); 98 | } 99 | 100 | sceKernelExitThread(0); 101 | 102 | return(0); 103 | } 104 | 105 | static int pgeAudioLibInit(int priority) 106 | { 107 | int i, ret; 108 | int failed = 0; 109 | char str[32]; 110 | 111 | pgeAudioLibTerminate = 0; 112 | pgeAudioLibReady = 0; 113 | 114 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 115 | { 116 | pgeAudioLibStatus[i].handle = -1; 117 | pgeAudioLibStatus[i].threadHandle = -1; 118 | pgeAudioLibStatus[i].volumeRight = PGE_VOLUME_MAX; 119 | pgeAudioLibStatus[i].volumeLeft = PGE_VOLUME_MAX; 120 | pgeAudioLibStatus[i].callback = 0; 121 | pgeAudioLibStatus[i].data = 0; 122 | } 123 | 124 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 125 | { 126 | if ((pgeAudioLibStatus[i].handle = sceAudioChReserve(-1, PGE_NUM_AUDIO_SAMPLES, 0)) < 0) 127 | failed = 1; 128 | } 129 | 130 | if (failed) 131 | { 132 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 133 | { 134 | if (pgeAudioLibStatus[i].handle != -1) 135 | sceAudioChRelease(pgeAudioLibStatus[i].handle); 136 | 137 | pgeAudioLibStatus[i].handle = -1; 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | pgeAudioLibReady = 1; 144 | 145 | strcpy(str, "PgeAudioThread0"); 146 | 147 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 148 | { 149 | str[14] = '0' + i; 150 | pgeAudioLibStatus[i].threadHandle = sceKernelCreateThread(str, (void*)&pgeAudioLibChannelThread, priority, 0x10000, PSP_THREAD_ATTR_USER, NULL); 151 | 152 | if (pgeAudioLibStatus[i].threadHandle < 0) 153 | { 154 | pgeAudioLibStatus[i].threadHandle = -1; 155 | failed = 1; 156 | break; 157 | } 158 | 159 | ret = sceKernelStartThread(pgeAudioLibStatus[i].threadHandle, sizeof(i), &i); 160 | 161 | if (ret != 0) 162 | { 163 | failed = 1; 164 | break; 165 | } 166 | } 167 | 168 | if (failed) 169 | { 170 | pgeAudioLibTerminate = 1; 171 | 172 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 173 | { 174 | if (pgeAudioLibStatus[i].threadHandle != -1) 175 | { 176 | sceKernelDeleteThread(pgeAudioLibStatus[i].threadHandle); 177 | } 178 | 179 | pgeAudioLibStatus[i].threadHandle = -1; 180 | } 181 | 182 | pgeAudioLibReady = 0; 183 | 184 | return 0; 185 | } 186 | 187 | return 1; 188 | } 189 | 190 | static void pgeAudioLibShutdown(void) 191 | { 192 | int i; 193 | pgeAudioLibReady = 0; 194 | pgeAudioLibTerminate = 1; 195 | 196 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 197 | { 198 | if (pgeAudioLibStatus[i].threadHandle != -1) 199 | { 200 | sceKernelDeleteThread(pgeAudioLibStatus[i].threadHandle); 201 | } 202 | 203 | pgeAudioLibStatus[i].threadHandle = -1; 204 | } 205 | 206 | for (i = 0; i < PGE_NUM_AUDIO_CHANNELS; i++) 207 | { 208 | if (pgeAudioLibStatus[i].handle != -1) 209 | { 210 | sceAudioChRelease(pgeAudioLibStatus[i].handle); 211 | pgeAudioLibStatus[i].handle = -1; 212 | } 213 | } 214 | } 215 | 216 | static void wavout_snd_callback(void *_buf, unsigned int _reqn, void *pdata) 217 | { 218 | int i,slot; 219 | pgeWav *wi; 220 | unsigned long ptr, frac; 221 | short *buf = _buf; 222 | 223 | pgeWavSamples = _buf; 224 | pgeWavReq = _reqn; 225 | 226 | for(i = 0; i < _reqn; i++) 227 | { 228 | int outr = 0, outl = 0; 229 | 230 | for(slot = 0; slot < PGE_WAV_MAX_SLOTS; slot++) 231 | { 232 | if(!pgeWavPlaying[slot]) continue; 233 | 234 | wi = &pgeWavInfo[slot]; 235 | frac = wi->playPtr_frac + wi->rateRatio; 236 | wi->playPtr = ptr = wi->playPtr + (frac>>16); 237 | wi->playPtr_frac = (frac & 0xffff); 238 | 239 | if(ptr >= wi->sampleCount) 240 | { 241 | if(wi->loop) 242 | { 243 | wi->playPtr = 0; 244 | wi->playPtr_frac = 0; 245 | ptr = 0; 246 | } 247 | else 248 | { 249 | pgeWavPlaying[slot] = 0; 250 | break; 251 | } 252 | } 253 | 254 | short *src16 = (short *)wi->data; 255 | unsigned char *src8 = (unsigned char *)wi->data; 256 | 257 | if(wi->channels == 1) 258 | { 259 | if(wi->bitPerSample == 8) 260 | { 261 | outl += (src8[ptr] * 256) - 32768 * wi->leftvolume; 262 | outr += (src8[ptr] * 256) - 32768 * wi->rightvolume; 263 | } 264 | else 265 | { 266 | outl += src16[ptr] * wi->leftvolume; 267 | outr += src16[ptr] * wi->rightvolume; 268 | } 269 | } 270 | else 271 | { 272 | if(wi->bitPerSample == 8) 273 | { 274 | outl += (src8[ptr*2] * 256) - 32768 * wi->leftvolume; 275 | outr += (src8[ptr*2+1] * 256) - 32768 * wi->rightvolume; 276 | } 277 | else 278 | { 279 | outl += src16[ptr*2] * wi->leftvolume; 280 | outr += src16[ptr*2+1] * wi->rightvolume; 281 | } 282 | } 283 | } 284 | 285 | if(outl < -32768) 286 | outl = -32768; 287 | else if (outl > 32767) 288 | outl = 32767; 289 | 290 | if(outr < -32768) 291 | outr = -32768; 292 | else if (outr > 32767) 293 | outr = 32767; 294 | 295 | *(buf++) = outl; 296 | *(buf++) = outr; 297 | } 298 | } 299 | 300 | int pgeWavInit(void) 301 | { 302 | int i; 303 | 304 | pgeAudioLibInit(0x12); 305 | 306 | pgeAudioLibSetChannelCallback(0, wavout_snd_callback, 0); 307 | 308 | for(i = 0; i < PGE_WAV_MAX_SLOTS; i++) 309 | pgeWavPlaying[i] = 0; 310 | 311 | pgeWavInitFlag = 1; 312 | 313 | return(1); 314 | } 315 | 316 | void pgeWavShutdown(void) 317 | { 318 | if(pgeWavInitFlag) 319 | pgeAudioLibShutdown(); 320 | } 321 | 322 | void pgeWavStop(pgeWav *wav) 323 | { 324 | int i; 325 | 326 | for(i = 0; i < PGE_WAV_MAX_SLOTS; i++) 327 | { 328 | if(wav->id == pgeWavId[i]) 329 | pgeWavPlaying[i] = 0; 330 | } 331 | } 332 | 333 | void pgeWavStopAll(void) 334 | { 335 | int i; 336 | 337 | for(i = 0; i < PGE_WAV_MAX_SLOTS; i++) 338 | pgeWavPlaying[i] = 0; 339 | } 340 | 341 | void pgeWavLoop(pgeWav *wav, unsigned int loop) 342 | { 343 | wav->loop = loop; 344 | } 345 | 346 | void pgeWavVolume(pgeWav *wav, unsigned char leftvolume, unsigned char rightvolume) 347 | { 348 | if(leftvolume > 100) 349 | leftvolume = 100; 350 | 351 | if(rightvolume > 100) 352 | rightvolume = 100; 353 | 354 | wav->leftvolume = leftvolume/100.0f; 355 | wav->rightvolume = rightvolume/100.0f; 356 | } 357 | 358 | void pgeWavPitch(pgeWav *wav, float pitch) 359 | { 360 | wav->rateRatio = ((wav->sampleRate*0x4000)/11025) * pitch; 361 | 362 | if(wav->rateRatio < (2000*0x4000)/11025) 363 | wav->rateRatio = (2000*0x4000)/11025; 364 | else if(wav->rateRatio > (100000*0x4000)/11025) 365 | wav->rateRatio = (100000*0x4000)/11025; 366 | } 367 | 368 | int pgeWavPlay(pgeWav *wav) 369 | { 370 | if(!pgeWavInitFlag) 371 | return(0); 372 | 373 | int i; 374 | 375 | pgeWav *wid; 376 | 377 | for(i = 0;i < PGE_WAV_MAX_SLOTS;i++) 378 | { 379 | if(pgeWavPlaying[i] == 0) 380 | break; 381 | } 382 | 383 | if(i == PGE_WAV_MAX_SLOTS) 384 | return(0); 385 | 386 | wid = &pgeWavInfo[i]; 387 | wid->channels = wav->channels; 388 | wid->sampleRate = wav->sampleRate; 389 | wid->sampleCount = wav->sampleCount; 390 | wid->dataLength = wav->dataLength; 391 | wid->data = wav->data; 392 | wid->rateRatio = wav->rateRatio; 393 | wid->playPtr = 0; 394 | wid->playPtr_frac = 0; 395 | wid->loop = wav->loop; 396 | wid->id = wav->id; 397 | wid->leftvolume = wav->leftvolume; 398 | wid->rightvolume = wav->rightvolume; 399 | pgeWavPlaying[i] = 1; 400 | pgeWavId[i] = wav->id; 401 | wid->bitPerSample = wav->bitPerSample; 402 | 403 | return(1); 404 | } 405 | 406 | static pgeWav *pgeWavLoadInternal(pgeWav *wav, unsigned char *wavfile, int size) 407 | { 408 | unsigned long channels; 409 | unsigned long samplerate; 410 | unsigned long blocksize; 411 | unsigned long bitpersample; 412 | unsigned long datalength; 413 | unsigned long samplecount; 414 | 415 | if(memcmp(wavfile, "RIFF", 4) != 0) 416 | { 417 | free(wav); 418 | return NULL; 419 | } 420 | 421 | channels = *(short *)(wavfile+0x16); 422 | samplerate = *(long *)(wavfile+0x18); 423 | blocksize = *(short *)(wavfile+0x20); 424 | bitpersample = *(short *)(wavfile+0x22); 425 | 426 | int i; 427 | 428 | for(i = 0; memcmp(wavfile + 0x24 + i, "data", 4) != 0; i++) 429 | { 430 | if(i == 0xFF) 431 | { 432 | free(wav); 433 | return NULL; 434 | } 435 | } 436 | 437 | datalength = *(unsigned long *)(wavfile + 0x28 + i); 438 | 439 | if(datalength + 0x2c > size) 440 | { 441 | free(wav); 442 | return NULL; 443 | } 444 | 445 | if(channels != 2 && channels != 1) 446 | { 447 | free(wav); 448 | return NULL; 449 | } 450 | 451 | if(samplerate > 100000 || samplerate < 2000) 452 | { 453 | free(wav); 454 | return NULL; 455 | } 456 | 457 | if(channels == 2) 458 | { 459 | samplecount = datalength/(bitpersample>>2); 460 | } 461 | else 462 | { 463 | samplecount = datalength/((bitpersample>>2)>>1); 464 | } 465 | 466 | if(samplecount <= 0) 467 | { 468 | free(wav); 469 | return NULL; 470 | } 471 | 472 | wav->channels = channels; 473 | wav->sampleRate = samplerate; 474 | wav->sampleCount = samplecount; 475 | wav->dataLength = datalength; 476 | wav->data = wavfile + 0x2c; 477 | wav->rateRatio = (samplerate*0x4000)/11025; 478 | wav->playPtr = 0; 479 | wav->playPtr_frac= 0; 480 | wav->loop = 0; 481 | pgeWavIdFlag++; 482 | wav->id = pgeWavIdFlag; 483 | wav->bitPerSample = bitpersample; 484 | wav->leftvolume = 1.0f; 485 | wav->rightvolume = 1.0f; 486 | 487 | return wav; 488 | } 489 | 490 | pgeWav *pgeWavLoad(const char *filename) 491 | { 492 | unsigned long filelen; 493 | 494 | unsigned char *wavfile; 495 | pgeWav *wav; 496 | 497 | int fd = sceIoOpen(filename, PSP_O_RDONLY, 0777); 498 | 499 | if(fd < 0) 500 | return NULL; 501 | 502 | long lSize; 503 | 504 | lSize = sceIoLseek32(fd, 0, PSP_SEEK_END); 505 | sceIoLseek32(fd, 0, PSP_SEEK_SET); 506 | 507 | wav = malloc(lSize + sizeof(wav)); 508 | wavfile = (unsigned char*)(wav) + sizeof(wav); 509 | 510 | filelen = sceIoRead(fd, wavfile, lSize); 511 | 512 | sceIoClose(fd); 513 | 514 | return(pgeWavLoadInternal(wav, wavfile, filelen)); 515 | } 516 | 517 | pgeWav *pgeWavLoadMemory(const unsigned char *buffer, int size) 518 | { 519 | unsigned char *wavfile; 520 | pgeWav *wav; 521 | 522 | wav = malloc(size + sizeof(wav)); 523 | wavfile = (unsigned char*)(wav) + sizeof(wav); 524 | 525 | memcpy(wavfile, (unsigned char*)buffer, size); 526 | 527 | return(pgeWavLoadInternal(wav, wavfile, size)); 528 | } 529 | 530 | void pgeWavUnload(pgeWav *wav) 531 | { 532 | if(wav != NULL) 533 | free(wav); 534 | } 535 | -------------------------------------------------------------------------------- /src/osl_sound/pgeWav.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pgeWav.h: Header for WAV audio playback 3 | * This file is part of the "Phoenix Game Engine". 4 | * 5 | * Copyright (C) 2007 Phoenix Game Engine 6 | * Copyright (C) 2007 InsertWittyName 7 | * 8 | */ 9 | 10 | #ifndef __PGEWAV_H__ 11 | #define __PGEWAV_H__ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** @defgroup pgeWav WAV Library 18 | * @{ 19 | */ 20 | 21 | /** 22 | * A WAV file struct 23 | */ 24 | typedef struct 25 | { 26 | unsigned long channels; /**< Number of channels */ 27 | unsigned long sampleRate; /**< Sample rate */ 28 | unsigned long sampleCount; /**< Sample count */ 29 | unsigned long dataLength; /**< Data length */ 30 | unsigned long rateRatio; /**< Rate ratio (sampleRate / 44100 * 0x10000) */ 31 | unsigned long playPtr; /**< Internal */ 32 | unsigned long playPtr_frac; /**< Internal */ 33 | unsigned int loop; /**< Loop flag */ 34 | unsigned char *data; /**< A pointer to the actual WAV data */ 35 | unsigned long id; /**< The ID of the WAV */ 36 | unsigned int bitPerSample; /**< The bit rate of the WAV */ 37 | float leftvolume; /**< The left volume of the WAV */ 38 | float rightvolume; /**< The right volume of the WAV */ 39 | 40 | } pgeWav; 41 | 42 | /** 43 | * Initialise the WAV playback 44 | * 45 | * @returns 1 on success. 46 | */ 47 | int pgeWavInit(void); 48 | 49 | /** 50 | * Shutdown WAV playback 51 | */ 52 | void pgeWavShutdown(void); 53 | 54 | /** 55 | * Load a WAV file 56 | * 57 | * @param filename - Path of the file to load. 58 | * 59 | * @returns A pointer to a ::pgeWav struct or NULL on error. 60 | */ 61 | pgeWav *pgeWavLoad(const char *filename); 62 | 63 | /** 64 | * Load a WAV file from memory 65 | * 66 | * @param buffer - Buffer that contains the WAV data. 67 | * 68 | * @param size - Size of the buffer. 69 | * 70 | * @returns A pointer to a ::pgeWav struct or NULL on error. 71 | */ 72 | pgeWav *pgeWavLoadMemory(const unsigned char *buffer, int size); 73 | 74 | /** 75 | * Unload a previously loaded WAV file 76 | * 77 | * @param wav - A valid ::pgeWav 78 | */ 79 | void pgeWavUnload(pgeWav *wav); 80 | 81 | /** 82 | * Start playing a loaded WAV file 83 | * 84 | * @param wav A pointer to a valid ::pgeWav struct. 85 | * 86 | * @returns 1 on success 87 | */ 88 | int pgeWavPlay(pgeWav *wav); 89 | 90 | /** 91 | * Stop playing a loaded WAV 92 | * 93 | * @param wav A pointer to a valid ::pgeWav struct. 94 | * 95 | * @returns 1 on success 96 | */ 97 | void pgeWavStop(pgeWav *wav); 98 | 99 | /** 100 | * Stop playing all WAVs 101 | */ 102 | void pgeWavStopAll(void); 103 | 104 | /** 105 | * Set the loop of the WAV playback 106 | * 107 | * @param wav - A pointer to a valid ::pgeWav struct. 108 | * 109 | * @param loop - Set to 1 to loop, 0 to playback once. 110 | */ 111 | void pgeWavLoop(pgeWav *wav, unsigned int loop); 112 | 113 | /** 114 | * Set the volume of the WAV 115 | * 116 | * @param wav - A pointer to a valid ::pgeWav struct. 117 | * 118 | * @param leftvolume - Set to 0 for no volume, 100 for full volume. 119 | * 120 | * @param rightvolume - Set to 0 for no volume, 100 for full volume. 121 | */ 122 | void pgeWavVolume(pgeWav *wav, unsigned char leftvolume, unsigned char rightvolume); 123 | 124 | /** 125 | * Set the pitch of the WAV 126 | * 127 | * @param wav - A pointer to a valid ::pgeWav struct. 128 | * 129 | * @param pitch - The new pitch. 130 | */ 131 | void pgeWavPitch(pgeWav *wav, float pitch); 132 | 133 | /** @} */ 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif // __cplusplus 138 | 139 | #endif // __PGEWAV_H__ 140 | -------------------------------------------------------------------------------- /src/osl_sound/readwav.h: -------------------------------------------------------------------------------- 1 | //NOT A HEADER FILE! 2 | #ifndef READWAV_H 3 | #define READWAV_H 4 | 5 | #include 6 | #include 7 | 8 | /* WAVE READING CODE ***********************************************/ 9 | 10 | typedef struct WAVE_FMT 11 | { 12 | unsigned short int format; 13 | unsigned short int channels; 14 | unsigned int sample_rate; 15 | unsigned int bytes_sec; 16 | unsigned short int frame_size; 17 | unsigned short int bits_sample; 18 | } WAVE_FMT; 19 | 20 | 21 | typedef struct WAVE_SRC 22 | { 23 | WAVE_FMT fmt; 24 | VIRTUAL_FILE *fp; 25 | size_t chunk_left, chunk_base; 26 | int cur_chn; 27 | unsigned char *data, *database; 28 | int basefp; 29 | unsigned char *streambuffer; 30 | int stream; 31 | } WAVE_SRC; 32 | 33 | int open_wave_src(WAVE_SRC *wav, const char *filename); 34 | int get_next_wav_sample(WAVE_SRC *wav); 35 | void close_wave_src(WAVE_SRC *wav); 36 | 37 | int osl_fgetc(VIRTUAL_FILE *f) { 38 | char c; 39 | VirtualFileRead(&c, 1, 1, f); 40 | return c; 41 | } 42 | 43 | unsigned int fgetu16(VIRTUAL_FILE *fp) 44 | { 45 | unsigned char a = osl_fgetc(fp); 46 | unsigned char b = osl_fgetc(fp); 47 | 48 | return a | (b << 8); 49 | } 50 | 51 | unsigned long fgetu32(VIRTUAL_FILE *fp) 52 | { 53 | unsigned char a = osl_fgetc(fp); 54 | unsigned char b = osl_fgetc(fp); 55 | unsigned char c = osl_fgetc(fp); 56 | unsigned char d = osl_fgetc(fp); 57 | 58 | return a | (b << 8) | (c << 16) | (d << 24); 59 | } 60 | 61 | 62 | /* get_fmt() *************************** 63 | Reads a format chunk from a wav file. 64 | Returns 0 for success or negative for failure. 65 | */ 66 | int get_fmt(WAVE_FMT *format, VIRTUAL_FILE *fp) 67 | { 68 | unsigned int fmt_len = fgetu32(fp); 69 | 70 | if(fmt_len < 16) 71 | return -3; 72 | 73 | format->format = fgetu16(fp); 74 | format->channels = fgetu16(fp); 75 | format->sample_rate = fgetu32(fp); 76 | format->bytes_sec = fgetu32(fp); 77 | format->frame_size = fgetu16(fp); 78 | format->bits_sample = fgetu16(fp); 79 | 80 | VirtualFileSeek(fp, fmt_len - 16, SEEK_CUR); 81 | return 0; 82 | } 83 | 84 | 85 | void close_wave_src(WAVE_SRC *wav) 86 | { 87 | VirtualFileClose(wav->fp); 88 | wav->fp = 0; 89 | } 90 | 91 | /* open_wave_src() ********************* 92 | Opens a RIFF WAVE (.wav) file for reading through 93 | get_next_wav_sample(). Returns the following error codes: 94 | -1 could not open; details are in errno 95 | -2 bad signature 96 | -3 bad format metadata or no format metadata before sample data 97 | -4 no sample data 98 | */ 99 | int open_wave_src(WAVE_SRC *wav, const char *filename) 100 | { 101 | char buf[256]; 102 | int got_fmt = 0; 103 | 104 | /* open the file */ 105 | wav->fp = VirtualFileOpen((void*)filename, 0, VF_AUTO, VF_O_READ); 106 | if(wav->fp < 0) 107 | return -1; 108 | 109 | /* read the header */ 110 | if(VirtualFileRead(buf, 12, 1, wav->fp) < 12) 111 | { 112 | close_wave_src(wav); 113 | return -2; 114 | } 115 | 116 | /* check for RIFF/WAVE signature */ 117 | if(memcmp("RIFF", buf, 4) || memcmp("WAVE", buf + 8, 4)) 118 | { 119 | close_wave_src(wav); 120 | return -2; 121 | } 122 | 123 | /* parse chunks */ 124 | while(VirtualFileRead(buf, 4, 1, wav->fp)) 125 | { 126 | if(!memcmp("fmt ", buf, 4)) 127 | { 128 | int errc = get_fmt(&(wav->fmt), wav->fp); 129 | if(errc < 0) 130 | { 131 | close_wave_src(wav); 132 | return -3; 133 | } 134 | got_fmt = 1; 135 | } 136 | else if(!memcmp("data", buf, 4)) 137 | { 138 | if(!got_fmt) 139 | { 140 | close_wave_src(wav); 141 | return -3; 142 | } 143 | 144 | wav->chunk_left = fgetu32(wav->fp); 145 | if(wav->chunk_left == 0) 146 | { 147 | close_wave_src(wav); 148 | return -4; 149 | } 150 | 151 | /* at this point, we have success */ 152 | wav->cur_chn = 0; 153 | return 0; 154 | } 155 | else /* skip unrecognized chunk type */ 156 | { 157 | unsigned long chunk_size = fgetu32(wav->fp); 158 | 159 | VirtualFileSeek(wav->fp, chunk_size, SEEK_CUR); 160 | } 161 | } 162 | /* we've come to the end of all the chunks and found no data */ 163 | close_wave_src(wav); 164 | return -4; 165 | } 166 | 167 | 168 | /* get_next_wav_sample() *************** 169 | Get the next sample from a wav file. 170 | */ 171 | int get_next_wav_sample(WAVE_SRC *wav) 172 | { 173 | int cur_sample = 0; 174 | int i; 175 | 176 | if(wav->chunk_left == 0) 177 | return 0; 178 | 179 | for(i = 0; i < wav->fmt.bits_sample && wav->chunk_left > 0; i += 8) 180 | { 181 | int c; 182 | if (wav->stream) 183 | c=*wav->streambuffer++; 184 | else 185 | c=*wav->data++; 186 | 187 | cur_sample >>= 8; 188 | cur_sample |= (c & 0xff) << 8; 189 | wav->chunk_left--; 190 | } 191 | 192 | if(wav->fmt.bits_sample <= 8) /* handle unsigned samples */ 193 | cur_sample -= 32768; 194 | cur_sample = (signed short)cur_sample; /* sign-extend */ 195 | 196 | if(++wav->cur_chn >= wav->fmt.channels) 197 | wav->cur_chn = 0; 198 | 199 | return cur_sample; 200 | } 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /src/osl_sound/vfsFile.c: -------------------------------------------------------------------------------- 1 | #include "oslib.h" 2 | 3 | /* 4 | SOURCE VFS: file 5 | */ 6 | 7 | #define FLAG_EOF 1 8 | 9 | int VF_FILE = -1; 10 | 11 | #define _file_ ((SceUID)f->ioPtr) 12 | 13 | int vfsFileOpen(void *param1, int param2, int type, int mode, VIRTUAL_FILE* f) { 14 | int stdMode = PSP_O_RDONLY; 15 | if (mode == VF_O_WRITE) 16 | stdMode = PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC; 17 | else if (mode == VF_O_READWRITE) 18 | stdMode = PSP_O_RDWR; 19 | 20 | f->ioPtr = (void*)sceIoOpen((char*)param1, stdMode, 0777); 21 | return (s32)f->ioPtr >= 0; 22 | } 23 | 24 | int vfsFileClose(VIRTUAL_FILE *f) { 25 | sceIoClose(_file_); 26 | return 1; 27 | } 28 | 29 | int vfsFileWrite(const void *ptr, size_t size, size_t n, VIRTUAL_FILE* f) { 30 | return sceIoWrite(_file_, ptr, size * n); 31 | } 32 | 33 | int vfsFileRead(void *ptr, size_t size, size_t n, VIRTUAL_FILE* f) { 34 | int readSize = sceIoRead(_file_, ptr, size * n); 35 | if (readSize < size * n) 36 | f->userData |= FLAG_EOF; 37 | return readSize; 38 | } 39 | 40 | int vfsFileGetc(VIRTUAL_FILE *f) { 41 | /* unsigned char car = -1; 42 | sceIoRead(_file_, &car, sizeof(car)); 43 | return (int)car;*/ 44 | return vfsMemGetc(f); 45 | } 46 | 47 | int vfsFilePutc(int caractere, VIRTUAL_FILE *f) { 48 | /* unsigned char car = (unsigned char)caractere; 49 | return sceIoWrite(_file_, &car, sizeof(car));*/ 50 | return vfsMemPutc(caractere, f); 51 | } 52 | 53 | char *vfsFileGets(char *str, int maxLen, VIRTUAL_FILE *f) { 54 | return vfsMemGets(str, maxLen, f); 55 | } 56 | 57 | void vfsFilePuts(const char *s, VIRTUAL_FILE *f) { 58 | return vfsMemPuts(s, f); 59 | } 60 | 61 | void vfsFileSeek(VIRTUAL_FILE *f, int offset, int whence) { 62 | // int oldOffset = sceIoLseek32(_file_, 0, SEEK_CUR); 63 | // if (!(offset == 0 && whence == SEEK_CUR)) 64 | // sceIoLseek32(_file_, offset, whence); 65 | sceIoLseek32(_file_, offset, whence); 66 | f->userData &= ~FLAG_EOF; 67 | } 68 | 69 | int vfsFileTell(VIRTUAL_FILE *f) { 70 | return sceIoLseek32(_file_, 0, SEEK_CUR); 71 | } 72 | 73 | int vfsFileEof(VIRTUAL_FILE *f) { 74 | return f->userData & FLAG_EOF; 75 | } 76 | 77 | VIRTUAL_FILE_SOURCE vfsFile = { 78 | vfsFileOpen, 79 | vfsFileClose, 80 | vfsFileRead, 81 | vfsFileWrite, 82 | vfsFileGetc, 83 | vfsFilePutc, 84 | vfsFileGets, 85 | vfsFilePuts, 86 | vfsFileSeek, 87 | vfsFileTell, 88 | vfsFileEof, 89 | }; 90 | 91 | int oslInitVfsFile() { 92 | VF_FILE = VirtualFileRegisterSource(&vfsFile); 93 | return VF_FILE; 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /stbi/stbi.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "stb_image.h" 3 | --------------------------------------------------------------------------------