Compare commits

...

5 Commits

Author SHA1 Message Date
19890802a6 Manually scale textures to correct sizes 2025-06-09 10:49:09 +02:00
e1c6091450 Try to port renderer 2025-06-09 10:39:40 +02:00
fca47a059c Integrate Hall and riscv target 2025-06-09 10:15:44 +02:00
5b8b3ed7cd Remove SDL dependency 2025-06-09 09:59:49 +02:00
f1e6d48e31 Prepare port 2025-06-09 09:59:49 +02:00
35 changed files with 1246 additions and 300 deletions

View File

@@ -1,25 +1,12 @@
cmake_minimum_required(VERSION 3.24)
project(HansTheGatherer)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/riscv-toolchain.cmake)
project(HansTheGatherer C CXX ASM)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Option to switch real platform vs. SDL implementation...
include(FetchContent)
FetchContent_Declare(
SDL3
URL https://github.com/libsdl-org/SDL/releases/download/release-3.2.14/SDL3-3.2.14.tar.gz
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(SDL3)
FetchContent_Declare(
SDL3_ttf
URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-3.2.2/SDL3_ttf-3.2.2.tar.gz
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(SDL3_ttf)
FetchContent_Declare(
entt
@@ -27,11 +14,23 @@ FetchContent_Declare(
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(entt)
find_package(entt CONFIG REQUIRED)
find_package(SDL3 CONFIG REQUIRED)
find_package(SDL3_ttf CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
add_library(RISCV_Options INTERFACE)
target_compile_options(RISCV_Options INTERFACE
-fno-exceptions
-fno-unwind-tables
-fno-rtti
-fno-pic # PIC?
-mno-relax
-march=rv32im
-mabi=ilp32
-std=c++20
)
add_subdirectory(lib)
add_subdirectory(wuehans)
add_executable(HansTheGatherer
src/main.cpp
@@ -42,6 +41,20 @@ add_executable(HansTheGatherer
src/render.cpp
)
target_link_libraries(HansTheGatherer SDL3::SDL3 SDL3_ttf::SDL3_ttf EnTT spdlog::spdlog)
target_link_options(HansTheGatherer PRIVATE
-nostartfiles
-Wl,--gc-sections
-march=rv32im
-mabi=ilp32
-lstdc++
-lc
-lgcc
-mcmodel=medany
)
set_property(TARGET HansTheGatherer PROPERTY CXX_STANDARD 20)
target_link_libraries(HansTheGatherer PRIVATE
RISCV_Options
WueHans
Hall
EnTT
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 912 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,19 @@
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR riscv)
set(RISCV_PREFIX riscv32-unknown-elf)
set(CMAKE_C_COMPILER ${RISCV_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${RISCV_PREFIX}-g++)
set(CMAKE_ASM_COMPILER ${RISCV_PREFIX}-gcc)
set(CMAKE_FIND_ROOT_PATH root)
# Don't run the linker on compiler check
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

1
lib/CMakeLists.txt Normal file
View File

@@ -0,0 +1 @@
add_subdirectory(Hall)

11
lib/Hall/CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
add_library(Hall STATIC
source/Audio.cpp
source/System.cpp
source/Video.cpp
)
target_include_directories(Hall
PUBLIC SYSTEM include
)
target_link_libraries(Hall PRIVATE RISCV_Options)

2
lib/Hall/README.md Normal file
View File

@@ -0,0 +1,2 @@
# Hall
Hallo, hier ist Hall

View File

@@ -0,0 +1,63 @@
#pragma once
namespace Hall
{
/// <summary>
/// Sets the global volume. 0 is muted, 128 is the default, 255 is max
/// </summary>
/// <param name="volume">0 is muted, 128 is the default, 255 is max</param>
void SetGlobalVolume(unsigned char volume);
/// <summary>
/// Prepares a channel to be used to play a non-looped single-channel audio
/// </summary>
/// <param name="channelID">The ID of the channel. Must be within [0,7]</param>
/// <param name="data">A pointer to the first sample of the audio data</param>
/// <param name="sampleCount">The total number of samples in the audio data</param>
/// <param name="volume">The channel's local volume. 0 is muted, 128 is the default, 255 is max</param>
void SetupMono(int channelID, short* data, int sampleCount, unsigned char volume = 128);
/// <summary>
/// Prepares a channel to be used to play a looped single-channel audio
/// </summary>
/// <param name="channelID">The ID of the channel. Must be within [0,7]</param>
/// <param name="data">A pointer to the first sample of the audio data</param>
/// <param name="sampleCount">The total number of samples in the audio data</param>
/// <param name="loopStart">The index of the first sample of the loop (inclusive)</param>
/// <param name="loopEnd">The index of the last sample of the loop (exclusive?)</param>
/// <param name="volume">The channel's local volume. 0 is muted, 128 is the default, 255 is max</param>
void SetupMono(int channelID, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume = 128);
/// <summary>
/// Prepares two channels to be used to play a non-looped stereo audio
/// </summary>
/// <param name="channelID_left">The ID of the channel for the left audio. Must be within [0,7]</param>
/// <param name="channelID_right">The ID of the channel for the right audio. Must be within [0,7]</param>
/// <param name="data">A pointer to the first sample of the audio data</param>
/// <param name="sampleCount">The total number of samples PER CHANNEL in the audio data</param>
/// <param name="volume">The channel's local volume. 0 is muted, 128 is the default, 255 is max</param>
void SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned char volume = 128);
/// <summary>
/// Prepares two channels to be used to play a looped stereo audio
/// </summary>
/// <param name="channelID_left">The ID of the channel for the left audio. Must be within [0,7]</param>
/// <param name="channelID_right">The ID of the channel for the right audio. Must be within [0,7]</param>
/// <param name="data">A pointer to the first sample of the audio data</param>
/// <param name="sampleCount">The total number of samples PER CHANNEL in the audio data</param>
/// <param name="loopStart">The index of the first sample of the loop (inclusive)</param>
/// <param name="loopEnd">The index of the last sample of the loop (exclusive?)</param>
/// <param name="volume">The channel's local volume. 0 is muted, 128 is the default, 255 is max</param>
void SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume = 128);
void Play(unsigned char channelSelect);
void Pause(unsigned char channelSelect);
void SetData(unsigned char channelSelect, short* data);
void SetLoop(unsigned char channelSelect, bool isLooping);
void SetLoopBoundaries(unsigned char channelSelect, int start, int end);
void SetPosition(unsigned char channelSelect, int position);
void SetSample(unsigned char channelSelect, short sample);
void SetVolume(unsigned char channelSelect, unsigned char volume);
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <Hall/System.h>
#include <Hall/Video.h>
#include <Hall/Audio.h>

View File

@@ -0,0 +1,36 @@
#pragma once
namespace Hall
{
static const int SYSTEM_CLK_FREQUENCY = 50000000; //50 MHz
static void* const SDRAM_START = 0x0; //Start of the RAM. It is 32 MB large
static void* const BOOTLOADER_START = (void* const) 0x02010000; //Start of the bootloader. It is 32 kB large
static void* const SD_CARD_START = (void* const) 0x80000000; //Start of the SD-Card
/// <summary>
/// Returns an 8-Byte int, that represents the number of ticks that passed since the system's boot-up. Divide by SYSTEM_CLK_FREQUENCY to get seconds
/// </summary>
/// <returns>The system time since boot-up in ticks</returns>
unsigned long long GetSystemTime();
/// <summary>
/// Returns the state of all buttons on the controller.
/// </summary>
/// <param name="id">Must be in the range [0,1]</param>
/// <returns>Each button corresponds to one bit: 0 - B, 1 - Y, 2 - Select, 3 - Start, 4 - Up, 5 - Down, 6 - Left, 7 - Right, 8 - A, 9 - X, 10 - L, 11 - R</returns>
unsigned short GetController(int id);
unsigned int GetSystemTimeExcerpt(int precision);
bool GetA(unsigned short controller);
bool GetB(unsigned short controller);
bool GetX(unsigned short controller);
bool GetY(unsigned short controller);
bool GetStart(unsigned short controller);
bool GetSelect(unsigned short controller);
bool GetUp(unsigned short controller);
bool GetDown(unsigned short controller);
bool GetLeft(unsigned short controller);
bool GetRight(unsigned short controller);
bool GetL(unsigned short controller);
bool GetR(unsigned short controller);
}

View File

@@ -0,0 +1,43 @@
#pragma once
namespace Hall
{
const int SCREEN_HEIGHT = 240;
const int SCREEN_WIDTH = 400;
/// <summary>
/// Draws an excerpt of the given data onto the screen. All coordinates describe the top-left corner
/// </summary>
/// <param name="data">A pointer to the first pixel of an image</param>
/// <param name="xOffset">The x offset within that image</param>
/// <param name="yOffset">The y offset within that image</param>
/// <param name="screenX">The x position on screen.</param>
/// <param name="screenY">The y position on screen.</param>
/// <param name="width">The width of the excerpt</param>
/// <param name="height">The height of the excerpt</param>
/// <param name="dataWidth">The width of the image (NOT EXCERPT)</param>
void Draw(unsigned short* data, unsigned short xOffset, unsigned short yOffset, unsigned short screenX, unsigned short screenY, unsigned short width, unsigned short height, unsigned short dataWidth);
/// <summary>
/// Clears the whole screen with the given color
/// </summary>
/// <param name="color">The format is R5G5B5A1, with the alpha bit being lsb</param>
void Clear(unsigned short color);
void SetData(unsigned short* data);
void SetXOffset(unsigned short xOffset);
void SetYOffset(unsigned short yOffset);
void SetImageWidth(unsigned short imageWidth);
void SetWidth(unsigned short width);
void SetHeight(unsigned short height);
void SetScreenX(unsigned short x);
void SetScreenY(unsigned short y);
void SetClearColor(unsigned short color);
void SetCommandDraw();
void SetCommandClear();
void SetCommandSwapBuffers();
bool GetIsGPUBusy();
bool GetVSync();
bool GetHSync();
}

132
lib/Hall/source/Audio.cpp Normal file
View File

@@ -0,0 +1,132 @@
#include <Hall/Audio.h>
volatile char* AUDIO_START = (char*) 0x2000100;
volatile short** AUDIO_START_ADDRESS = (volatile short**)(AUDIO_START + 4);
volatile int* AUDIO_SAMPLE_COUNT = (volatile int*)(AUDIO_START + 8);
volatile int* AUDIO_LOOP_START = (volatile int*)(AUDIO_START + 12);
volatile int* AUDIO_LOOP_END = (volatile int*)(AUDIO_START + 16);
volatile int* AUDIO_CURRENT_POSITION = (volatile int*)(AUDIO_START + 20);
volatile short* AUDIO_LAST_SAMPLE = (volatile short*)(AUDIO_START + 24);
volatile unsigned char* AUDIO_VOLUME = (volatile unsigned char*)(AUDIO_START + 28);
volatile bool* AUDIO_IS_LOOPING = (volatile bool*)(AUDIO_START + 32);
volatile bool* AUDIO_IS_PLAYING = (volatile bool*)(AUDIO_START + 36);
volatile bool* AUDIO_IS_MONO = (volatile bool*)(AUDIO_START + 40);
volatile bool* AUDIO_IS_RIGHT = (volatile bool*)(AUDIO_START + 44);
unsigned char* AUDIO_GLOBAL_VOLUME = (unsigned char*)(AUDIO_START + 48); //I think we can skip volatile for these two
unsigned char* AUDIO_CHANNEL_SELECT = (unsigned char*)(AUDIO_START + 52); //Because they will never change and always address the same value
void Hall::SetGlobalVolume(unsigned char volume)
{
*AUDIO_GLOBAL_VOLUME = volume;
}
void Hall::SetupMono(int channelID, short* data, int sampleCount, unsigned char volume)
{
*AUDIO_CHANNEL_SELECT = 1 << channelID;
*AUDIO_START_ADDRESS = data;
*AUDIO_SAMPLE_COUNT = sampleCount;
*AUDIO_CURRENT_POSITION = 0;
*AUDIO_VOLUME = volume;
*AUDIO_IS_LOOPING = false;
*AUDIO_IS_PLAYING = false;
*AUDIO_IS_MONO = true;
}
void Hall::SetupMono(int channelID, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume)
{
*AUDIO_CHANNEL_SELECT = 1 << channelID;
*AUDIO_START_ADDRESS = data;
*AUDIO_SAMPLE_COUNT = sampleCount;
*AUDIO_CURRENT_POSITION = 0;
*AUDIO_VOLUME = volume;
*AUDIO_IS_LOOPING = true;
*AUDIO_LOOP_START = loopStart;
*AUDIO_LOOP_END = loopEnd;
*AUDIO_IS_PLAYING = false;
*AUDIO_IS_MONO = true;
}
void Hall::SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned char volume)
{
*AUDIO_CHANNEL_SELECT = (1 << channelID_left) | (1 << channelID_right);
*AUDIO_START_ADDRESS = data;
*AUDIO_SAMPLE_COUNT = sampleCount;
*AUDIO_CURRENT_POSITION = 0;
*AUDIO_VOLUME = volume;
*AUDIO_IS_LOOPING = false;
*AUDIO_IS_PLAYING = false;
*AUDIO_IS_MONO = false;
*AUDIO_IS_RIGHT = false;
*AUDIO_CHANNEL_SELECT = 1 << channelID_right;
*AUDIO_IS_RIGHT = true;
}
void Hall::SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume)
{
*AUDIO_CHANNEL_SELECT = (1 << channelID_left) | (1 << channelID_right);
*AUDIO_START_ADDRESS = data;
*AUDIO_SAMPLE_COUNT = sampleCount;
*AUDIO_CURRENT_POSITION = 0;
*AUDIO_VOLUME = volume;
*AUDIO_LOOP_START = loopStart;
*AUDIO_LOOP_END = loopEnd;
*AUDIO_IS_LOOPING = true;
*AUDIO_IS_PLAYING = false;
*AUDIO_IS_MONO = false;
*AUDIO_IS_RIGHT = false;
*AUDIO_CHANNEL_SELECT = 1 << channelID_right;
*AUDIO_IS_RIGHT = true;
}
void Hall::Play(unsigned char channelSelect)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_IS_PLAYING = true;
}
void Hall::Pause(unsigned char channelSelect)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_IS_PLAYING = false;
}
void Hall::SetData(unsigned char channelSelect, short* data)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_START_ADDRESS = data;
}
void Hall::SetLoop(unsigned char channelSelect, bool isLooping)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_IS_LOOPING;
}
void Hall::SetLoopBoundaries(unsigned char channelSelect, int start, int end)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_LOOP_START = start;
*AUDIO_LOOP_END = end;
}
void Hall::SetPosition(unsigned char channelSelect, int position)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_CURRENT_POSITION = position;
}
void Hall::SetSample(unsigned char channelSelect, short sample)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_LAST_SAMPLE = sample;
}
void Hall::SetVolume(unsigned char channelSelect, unsigned char volume)
{
*AUDIO_CHANNEL_SELECT = channelSelect;
*AUDIO_VOLUME = volume;
}

View File

@@ -0,0 +1,90 @@
#include <Hall/System.h>
volatile char* SYSTEM_TIME = (volatile char*)0x02000300;
volatile unsigned int* SYSTEM_TIME_0 = (volatile unsigned int*)(SYSTEM_TIME + 0); //Least precise
volatile unsigned int* SYSTEM_TIME_1 = (volatile unsigned int*)(SYSTEM_TIME + 4);
volatile unsigned int* SYSTEM_TIME_2 = (volatile unsigned int*)(SYSTEM_TIME + 8);
volatile unsigned int* SYSTEM_TIME_3 = (volatile unsigned int*)(SYSTEM_TIME + 12);
volatile unsigned int* SYSTEM_TIME_4 = (volatile unsigned int*)(SYSTEM_TIME + 16);//Most precise
volatile char* CONTROLLER_START = (volatile char*)0x02000200;
volatile unsigned short* CONTROLLER_0 = (volatile unsigned short*)(CONTROLLER_START + 0);
volatile unsigned short* CONTROLLER_1 = (volatile unsigned short*)(CONTROLLER_START + 2);
unsigned long long Hall::GetSystemTime()
{
unsigned long long result = *SYSTEM_TIME_0;
result = result << 32;
result += *SYSTEM_TIME_4;
return result;
}
unsigned int Hall::GetSystemTimeExcerpt(int precision)
{
return *(SYSTEM_TIME_0 + precision);
}
unsigned short Hall::GetController(int id)
{
return *(CONTROLLER_0 + id);
}
bool Hall::GetA(unsigned short controller)
{
return controller & (1 << 8);
}
bool Hall::GetB(unsigned short controller)
{
return controller & (1 << 0);
}
bool Hall::GetX(unsigned short controller)
{
return controller & (1 << 9);
}
bool Hall::GetY(unsigned short controller)
{
return controller & (1 << 1);
}
bool Hall::GetStart(unsigned short controller)
{
return controller & (1 << 3);
}
bool Hall::GetSelect(unsigned short controller)
{
return controller & (1 << 2);
}
bool Hall::GetUp(unsigned short controller)
{
return controller & (1 << 4);
}
bool Hall::GetDown(unsigned short controller)
{
return controller & (1 << 5);
}
bool Hall::GetLeft(unsigned short controller)
{
return controller & (1 << 6);
}
bool Hall::GetRight(unsigned short controller)
{
return controller & (1 << 7);
}
bool Hall::GetL(unsigned short controller)
{
return controller & (1 << 10);
}
bool Hall::GetR(unsigned short controller)
{
return controller & (1 << 11);
}

117
lib/Hall/source/Video.cpp Normal file
View File

@@ -0,0 +1,117 @@
#include <Hall/Video.h>
volatile char* GPU_START = (char*)0x02000000;
volatile unsigned short** GPU_IMAGE_START = (volatile unsigned short**)(GPU_START + 0);
volatile unsigned short* GPU_IMAGE_X_OFFSET = (volatile unsigned short*)(GPU_START + 4);
volatile unsigned short* GPU_IMAGE_Y_OFFSET = (volatile unsigned short*)(GPU_START + 8);
volatile unsigned short* GPU_IMAGE_WIDTH = (volatile unsigned short*)(GPU_START + 12);
volatile unsigned short* GPU_WIDTH = (volatile unsigned short*)(GPU_START + 16);
volatile unsigned short* GPU_HEIGHT = (volatile unsigned short*)(GPU_START + 20);
volatile unsigned short* GPU_SCREEN_X = (volatile unsigned short*)(GPU_START + 24);
volatile unsigned short* GPU_SCREEN_Y = (volatile unsigned short*)(GPU_START + 28);
volatile unsigned short* GPU_CLEAR_COLOR = (volatile unsigned short*)(GPU_START + 32);
volatile bool* GPU_COMMAND_DRAW = (volatile bool*)(GPU_START + 36);
volatile bool* GPU_COMMAND_CLEAR = (volatile bool*)(GPU_START + 40);
volatile bool* GPU_IS_BUSY = (volatile bool*)(GPU_START + 44);
volatile bool* GPU_VSYNC = (volatile bool*)(GPU_START + 48);
volatile bool* GPU_HSYNC = (volatile bool*)(GPU_START + 52);
volatile bool* GPU_COMMAND_SWAP_BUFFERS = (volatile bool*)(GPU_START + 56);
volatile bool* VSYNC_BUFFER_SWAP = (volatile bool*)(GPU_START + 60);
void Hall::Draw(unsigned short* data, unsigned short xOffset, unsigned short yOffset, unsigned short screenX, unsigned short screenY, unsigned short width, unsigned short height, unsigned short dataWidth)
{
*GPU_IMAGE_START = data;
*GPU_IMAGE_X_OFFSET = xOffset;
*GPU_IMAGE_Y_OFFSET = yOffset;
*GPU_IMAGE_WIDTH = dataWidth;
*GPU_WIDTH = width;
*GPU_HEIGHT = height;
*GPU_SCREEN_X = screenX;
*GPU_SCREEN_Y = screenY;
*GPU_COMMAND_DRAW = true;
}
void Hall::Clear(unsigned short color)
{
*GPU_CLEAR_COLOR = color;
*GPU_COMMAND_CLEAR = true;
}
void Hall::SetData(unsigned short* data)
{
*GPU_IMAGE_START = data;
}
void Hall::SetXOffset(unsigned short xOffset)
{
*GPU_IMAGE_X_OFFSET = xOffset;
}
void Hall::SetYOffset(unsigned short yOffset)
{
*GPU_IMAGE_Y_OFFSET = yOffset;
}
void Hall::SetImageWidth(unsigned short imageWidth)
{
*GPU_IMAGE_WIDTH = imageWidth;
}
void Hall::SetWidth(unsigned short width)
{
*GPU_WIDTH = width;
}
void Hall::SetHeight(unsigned short height)
{
*GPU_HEIGHT = height;
}
void Hall::SetScreenX(unsigned short x)
{
*GPU_SCREEN_X = x;
}
void Hall::SetScreenY(unsigned short y)
{
*GPU_SCREEN_Y = y;
}
void Hall::SetClearColor(unsigned short color)
{
*GPU_CLEAR_COLOR = color;
}
void Hall::SetCommandDraw()
{
*GPU_COMMAND_DRAW = true;
}
void Hall::SetCommandClear()
{
*GPU_COMMAND_CLEAR = true;
}
void Hall::SetCommandSwapBuffers()
{
*GPU_COMMAND_SWAP_BUFFERS = true;
}
bool Hall::GetIsGPUBusy()
{
return *GPU_IS_BUSY;
}
bool Hall::GetVSync()
{
return *GPU_VSYNC;
}
bool Hall::GetHSync()
{
return *GPU_HSYNC;
}

View File

@@ -1,9 +1,7 @@
#include "assets.hpp"
#include "audio.hpp"
#include "definitions.hpp"
#include <cstdint>
#include <spdlog/spdlog.h>
static constexpr uint8_t BACKGROUND_DATA[] = {
#embed "../assets/images/jungle.bmp"
@@ -37,79 +35,90 @@ static constexpr uint8_t DEFAULT_FONT_DATA[] = {
#embed "../assets/fonts/OpenTTD-Sans.ttf"
};
SDL_Texture* load_texture(uint8_t const* data, size_t size, SDL_Renderer* renderer)
{
auto* iostream = SDL_IOFromConstMem(data, size);
SDL_Surface* surface = SDL_LoadBMP_IO(iostream, false);
if (surface == nullptr)
{
spdlog::error("Failed to load SDL surface!\nCause: {}", SDL_GetError());
}
// SDL_Texture* load_texture(uint8_t const* data, size_t size, SDL_Renderer* renderer)
// {
// auto* iostream = SDL_IOFromConstMem(data, size);
// SDL_Surface* surface = SDL_LoadBMP_IO(iostream, false);
// if (surface == nullptr)
// {
// // spdlog::error("Failed to load SDL surface!\nCause: {}", SDL_GetError());
// }
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
if (texture == nullptr)
{
spdlog::error("Failed to create texture from surface!\nCause: {}", SDL_GetError());
}
// SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
// if (texture == nullptr)
// {
// // spdlog::error("Failed to create texture from surface!\nCause: {}", SDL_GetError());
// }
return texture;
}
// return texture;
// }
AudioAsset load_audio(uint8_t const* data, size_t size)
{
AudioAsset audio_asset;
// AudioAsset load_audio(uint8_t const* data, size_t size)
// {
// AudioAsset audio_asset;
auto* iostream = SDL_IOFromConstMem(data, size);
bool res = SDL_LoadWAV_IO(
iostream, false, &audio_asset.spec, &audio_asset.buffer, &audio_asset.buffer_length);
if (!res)
{
spdlog::error("Failed to load audio file!\nCause: {}", SDL_GetError());
}
return audio_asset;
}
// auto* iostream = SDL_IOFromConstMem(data, size);
// bool res = SDL_LoadWAV_IO(
// iostream, false, &audio_asset.spec, &audio_asset.buffer, &audio_asset.buffer_length);
// if (!res)
// {
// // spdlog::error("Failed to load audio file!\nCause: {}", SDL_GetError());
// }
// return audio_asset;
// }
FontAsset load_font(uint8_t const* data, size_t size)
{
FontAsset font_asset;
// FontAsset load_font(uint8_t const* data, size_t size)
// {
// FontAsset font_asset;
auto* iostream = SDL_IOFromConstMem(data, size);
auto* ttf = TTF_OpenFontIO(iostream, false, 20);
// auto* iostream = SDL_IOFromConstMem(data, size);
// auto* ttf = TTF_OpenFontIO(iostream, false, 20);
font_asset.font = ttf;
// font_asset.font = ttf;
return font_asset;
}
// return font_asset;
// }
AssetModule::AssetModule(entt::registry& registry)
{
auto renderer = registry.ctx().get<SdlHandles>().renderer;
// auto renderer = registry.ctx().get<SdlHandles>().renderer;
auto* background = load_texture(BACKGROUND_DATA, sizeof(BACKGROUND_DATA), renderer);
TextureAtlasLayout background_layout = {.width = 866, .height = 510, .rows = 1, .columns = 1};
// auto* background = load_texture(BACKGROUND_DATA, sizeof(BACKGROUND_DATA), renderer);
TextureAtlasLayout background_layout = {.width = 400, .height = 240, .rows = 1, .columns = 1};
auto* fruits = load_texture(FRUITS_DATA, sizeof(FRUITS_DATA), renderer);
TextureAtlasLayout fruits_layout = {.width = 16, .height = 16, .rows = 6, .columns = 38};
// auto* fruits = load_texture(FRUITS_DATA, sizeof(FRUITS_DATA), renderer);
TextureAtlasLayout fruits_layout = {.width = 32, .height = 32, .rows = 6, .columns = 38};
auto* spiders = load_texture(SPIDERS_DATA, sizeof(SPIDERS_DATA), renderer);
TextureAtlasLayout spiders_layout = {.width = 16, .height = 16, .rows = 2, .columns = 4};
// auto* spiders = load_texture(SPIDERS_DATA, sizeof(SPIDERS_DATA), renderer);
TextureAtlasLayout spiders_layout = {.width = 32, .height = 32, .rows = 2, .columns = 4};
auto* basket = load_texture(BASKET_DATA, sizeof(BASKET_DATA), renderer);
TextureAtlasLayout basket_layout = {.width = 16, .height = 16, .rows = 1, .columns = 1};
// auto* basket = load_texture(BASKET_DATA, sizeof(BASKET_DATA), renderer);
TextureAtlasLayout basket_layout = {.width = 64, .height = 32, .rows = 1, .columns = 1};
registry.ctx().emplace<TextureAssets>(TextureAssets{
.background = Texture{.sdl_texture = background, .texture_atlas_layout = background_layout},
.fruits = Texture{.sdl_texture = fruits, .texture_atlas_layout = fruits_layout},
.spiders = Texture{.sdl_texture = spiders, .texture_atlas_layout = spiders_layout},
.basket = Texture{.sdl_texture = basket, .texture_atlas_layout = basket_layout}});
// registry.ctx().emplace<TextureAssets>(TextureAssets{
// .background = Texture{.sdl_texture = background, .texture_atlas_layout =
// background_layout}, .fruits = Texture{.sdl_texture = fruits, .texture_atlas_layout =
// fruits_layout}, .spiders = Texture{.sdl_texture = spiders, .texture_atlas_layout =
// spiders_layout}, .basket = Texture{.sdl_texture = basket, .texture_atlas_layout =
// basket_layout}});
registry.ctx().emplace<TextureAssets>(
TextureAssets{.background = Texture{.texture_atlas_layout = background_layout},
.fruits = Texture{.texture_atlas_layout = fruits_layout},
.spiders = Texture{.texture_atlas_layout = spiders_layout},
.basket = Texture{.texture_atlas_layout = basket_layout}});
auto background_music = load_audio(BACKGROUND_MUSIC_DATA, sizeof(BACKGROUND_MUSIC_DATA));
auto pickup_sound = load_audio(PICKUP_SOUND_DATA, sizeof(PICKUP_SOUND_DATA));
auto hit_sound = load_audio(HIT_SOUND_DATA, sizeof(HIT_SOUND_DATA));
registry.ctx().emplace<AudioAssets>(AudioAssets{.background_music = background_music,
.pickup_sound = pickup_sound,
.hit_sound = hit_sound});
// auto background_music = load_audio(BACKGROUND_MUSIC_DATA, sizeof(BACKGROUND_MUSIC_DATA));
// auto pickup_sound = load_audio(PICKUP_SOUND_DATA, sizeof(PICKUP_SOUND_DATA));
// auto hit_sound = load_audio(HIT_SOUND_DATA, sizeof(HIT_SOUND_DATA));
registry.ctx().emplace<AudioAssets>(AudioAssets{
.background_music = {.buffer = BACKGROUND_MUSIC_DATA,
.buffer_length = sizeof(BACKGROUND_MUSIC_DATA)},
.pickup_sound = {.buffer = PICKUP_SOUND_DATA, .buffer_length = sizeof(PICKUP_SOUND_DATA)},
.hit_sound = {.buffer = HIT_SOUND_DATA, .buffer_length = sizeof(HIT_SOUND_DATA)}});
// registry.ctx().emplace<AudioAssets>(AudioAssets{.background_music = background_music,
// .pickup_sound = pickup_sound,
// .hit_sound = hit_sound});
auto font = load_font(DEFAULT_FONT_DATA, sizeof(DEFAULT_FONT_DATA));
registry.ctx().emplace<FontAssets>(FontAssets{.default_font = font});
// auto font = load_font(DEFAULT_FONT_DATA, sizeof(DEFAULT_FONT_DATA));
// registry.ctx().emplace<FontAssets>(FontAssets{.default_font = font});
}

View File

@@ -2,8 +2,6 @@
#include "audio.hpp"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <entt/entt.hpp>
struct TextureAtlasLayout
@@ -16,7 +14,8 @@ struct TextureAtlasLayout
struct Texture
{
SDL_Texture* sdl_texture;
uint8_t* data;
uint32_t data_length;
TextureAtlasLayout texture_atlas_layout;
};
@@ -35,15 +34,15 @@ struct AudioAssets
AudioAsset hit_sound;
};
struct FontAsset
{
TTF_Font* font;
};
// struct FontAsset
// {
// TTF_Font* font;
// };
struct FontAssets
{
FontAsset default_font;
};
// struct FontAssets
// {
// FontAsset default_font;
// };
struct AssetModule
{

View File

@@ -3,29 +3,29 @@
AudioModule::AudioModule(entt::registry& registry)
{
auto const& audio_assets = registry.ctx().get<AudioAssets>();
auto* music_stream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_assets.background_music.spec, NULL, NULL);
auto* sound_stream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_assets.pickup_sound.spec, NULL, NULL);
// auto const& audio_assets = registry.ctx().get<AudioAssets>();
// auto* music_stream = SDL_OpenAudioDeviceStream(
// SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_assets.background_music.spec, NULL, NULL);
// auto* sound_stream = SDL_OpenAudioDeviceStream(
// SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_assets.pickup_sound.spec, NULL, NULL);
SDL_ResumeAudioStreamDevice(music_stream);
SDL_ResumeAudioStreamDevice(sound_stream);
// SDL_ResumeAudioStreamDevice(music_stream);
// SDL_ResumeAudioStreamDevice(sound_stream);
registry.ctx().emplace<AudioStreams>(
AudioStreams{.music_stream = music_stream, .sound_stream = sound_stream});
// registry.ctx().emplace<AudioStreams>(
// AudioStreams{.music_stream = music_stream, .sound_stream = sound_stream});
}
void AudioModule::FeedAudioStreams(entt::registry& registry)
{
auto audio_streams = registry.ctx().get<AudioStreams>();
auto audio_assets = registry.ctx().get<AudioAssets>();
// void AudioModule::FeedAudioStreams(entt::registry& registry)
// {
// auto audio_streams = registry.ctx().get<AudioStreams>();
// auto audio_assets = registry.ctx().get<AudioAssets>();
if (SDL_GetAudioStreamQueued(audio_streams.music_stream) <
static_cast<int>(audio_assets.background_music.buffer_length))
{
SDL_PutAudioStreamData(audio_streams.music_stream,
audio_assets.background_music.buffer,
audio_assets.background_music.buffer_length);
}
}
// if (SDL_GetAudioStreamQueued(audio_streams.music_stream) <
// static_cast<int>(audio_assets.background_music.buffer_length))
// {
// SDL_PutAudioStreamData(audio_streams.music_stream,
// audio_assets.background_music.buffer,
// audio_assets.background_music.buffer_length);
// }
// }

View File

@@ -1,24 +1,23 @@
#pragma once
#include <SDL3/SDL.h>
#include <cstdint>
#include <entt/entt.hpp>
struct AudioAsset
{
SDL_AudioSpec spec;
uint8_t* buffer;
// SDL_AudioSpec spec;
uint8_t const* buffer;
uint32_t buffer_length;
};
struct AudioStreams
{
SDL_AudioStream* music_stream;
SDL_AudioStream* sound_stream;
};
// struct AudioStreams
// {
// SDL_AudioStream* music_stream;
// SDL_AudioStream* sound_stream;
// };
struct AudioModule
{
AudioModule(entt::registry& registry);
static void FeedAudioStreams(entt::registry& registry);
// static void FeedAudioStreams(entt::registry& registry);
};

View File

@@ -1,18 +1,16 @@
#pragma once
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <random>
static constexpr int WINDOW_WIDTH = 400;
static constexpr int WINDOW_HEIGHT = 240;
struct SdlHandles
{
SDL_Window* window;
SDL_Renderer* renderer;
TTF_TextEngine* text_engine;
};
// struct SdlHandles
// {
// SDL_Window* window;
// SDL_Renderer* renderer;
// TTF_TextEngine* text_engine;
// };
struct Game
{

View File

@@ -1,11 +1,10 @@
#pragma once
#include <SDL3/SDL.h>
#include <set>
struct ButtonInput
{
std::set<SDL_Keycode> pressed;
std::set<SDL_Keycode> just_pressed;
std::set<SDL_Keycode> just_released;
std::set<int> pressed;
std::set<int> just_pressed;
std::set<int> just_released;
};

View File

@@ -1,7 +1,34 @@
#include "level.hpp"
#include "input.hpp"
#include <spdlog/spdlog.h>
LevelModule::LevelModule(entt::registry& registry)
{
auto const* texture_assets = &registry.ctx().get<TextureAssets>();
auto background = registry.create();
registry.emplace<Position>(background, Position{.x = 0, .y = 0});
registry.emplace<Sprite>(
background, Sprite{.texture = &texture_assets->background, .texture_atlas_index = 0});
registry.emplace<Size>(background, Size{.w = WINDOW_WIDTH, .h = WINDOW_HEIGHT});
registry.emplace<Background>(background);
auto basket = registry.create();
registry.emplace<WorldPosition>(basket);
registry.emplace<Position>(basket,
Position{.x = WINDOW_WIDTH / 2 - 32, .y = WINDOW_HEIGHT - 32});
registry.emplace<Sprite>(basket,
Sprite{.texture = &texture_assets->basket, .texture_atlas_index = 0});
registry.emplace<Size>(basket, Size{.w = 64, .h = 32});
registry.emplace<Basket>(basket);
auto basket_cb = registry.create();
registry.emplace<WorldPosition>(basket_cb);
registry.emplace<Position>(basket_cb, Position{.x = 0, .y = 16});
registry.emplace<Size>(basket_cb, Size{.w = 64, .h = 16});
registry.emplace<CollisionBox>(basket_cb);
registry.emplace<Parent>(basket_cb, basket);
registry.emplace<BasketCollisionBox>(basket_cb);
}
void LevelModule::MoveBasket(entt::registry& registry)
{
@@ -10,14 +37,14 @@ void LevelModule::MoveBasket(entt::registry& registry)
for (auto [entity, pos, size, sprite] : basket_view.each())
{
if (input.pressed.contains(SDLK_LEFT))
{
pos.x -= 5;
}
if (input.pressed.contains(SDLK_RIGHT))
{
pos.x += 5;
}
// if (input.pressed.contains(SDLK_LEFT))
// {
// pos.x -= 5;
// }
// if (input.pressed.contains(SDLK_RIGHT))
// {
// pos.x += 5;
// }
pos.x = pos.x < 0 ? 0 : pos.x;
pos.x = pos.x > WINDOW_WIDTH - size.w ? WINDOW_WIDTH - size.w : pos.x;
@@ -79,7 +106,7 @@ void LevelModule::SpawnFruits(entt::registry& registry)
void LevelModule::CollectFruit(entt::registry& registry)
{
auto& game = registry.ctx().get<Game>();
auto const& audio_streams = registry.ctx().get<AudioStreams>();
// auto const& audio_streams = registry.ctx().get<AudioStreams>();
auto const& audio_assets = registry.ctx().get<AudioAssets>();
auto view = registry.view<Position, Fruit, Collided>();
@@ -89,16 +116,16 @@ void LevelModule::CollectFruit(entt::registry& registry)
pos.x += 1000;
// registry.destroy(entity);
SDL_PutAudioStreamData(audio_streams.sound_stream,
audio_assets.pickup_sound.buffer,
audio_assets.pickup_sound.buffer_length);
// SDL_PutAudioStreamData(audio_streams.sound_stream,
// audio_assets.pickup_sound.buffer,
// audio_assets.pickup_sound.buffer_length);
}
}
void LevelModule::CollectSpider(entt::registry& registry)
{
auto& game = registry.ctx().get<Game>();
auto const& audio_streams = registry.ctx().get<AudioStreams>();
// auto const& audio_streams = registry.ctx().get<AudioStreams>();
auto const& audio_assets = registry.ctx().get<AudioAssets>();
auto view = registry.view<Position, Spider, Collided>();
@@ -108,9 +135,9 @@ void LevelModule::CollectSpider(entt::registry& registry)
pos.x += 1000;
// registry.destroy(entity);
SDL_PutAudioStreamData(audio_streams.sound_stream,
audio_assets.hit_sound.buffer,
audio_assets.hit_sound.buffer_length);
// SDL_PutAudioStreamData(audio_streams.sound_stream,
// audio_assets.hit_sound.buffer,
// audio_assets.hit_sound.buffer_length);
};
}

View File

@@ -5,7 +5,6 @@
#include "sprite.hpp"
#include <entt/entt.hpp>
#include <spdlog/spdlog.h>
struct Background
{
@@ -33,34 +32,7 @@ struct BasketCollisionBox
struct LevelModule
{
LevelModule(entt::registry& registry)
{
auto const* texture_assets = &registry.ctx().get<TextureAssets>();
auto background = registry.create();
registry.emplace<Position>(background, Position{.x = 0, .y = 0});
registry.emplace<Sprite>(
background, Sprite{.texture = &texture_assets->background, .texture_atlas_index = 0});
registry.emplace<Size>(background, Size{.w = WINDOW_WIDTH, .h = WINDOW_HEIGHT});
registry.emplace<Background>(background);
auto basket = registry.create();
registry.emplace<WorldPosition>(basket);
registry.emplace<Position>(basket,
Position{.x = WINDOW_WIDTH / 2 - 32, .y = WINDOW_HEIGHT - 32});
registry.emplace<Sprite>(
basket, Sprite{.texture = &texture_assets->basket, .texture_atlas_index = 0});
registry.emplace<Size>(basket, Size{.w = 64, .h = 32});
registry.emplace<Basket>(basket);
auto basket_cb = registry.create();
registry.emplace<WorldPosition>(basket_cb);
registry.emplace<Position>(basket_cb, Position{.x = 0, .y = 16});
registry.emplace<Size>(basket_cb, Size{.w = 64, .h = 16});
registry.emplace<CollisionBox>(basket_cb);
registry.emplace<Parent>(basket_cb, basket);
registry.emplace<BasketCollisionBox>(basket_cb);
}
LevelModule(entt::registry& registry);
static void MoveBasket(entt::registry& registry);
static void SpawnFruits(entt::registry& registry);

View File

@@ -5,61 +5,26 @@
#include "physics.hpp"
#include "render.hpp"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <Hall/Hall.h>
#include <entt/entt.hpp>
#include <spdlog/spdlog.h>
void increment_ticks(entt::registry& registry)
{
auto& game = registry.ctx().get<Game>();
// auto& translate_system = registry.ctx().get<TranslateSystem>();
game.ticks += 1;
if (game.ticks % 60 == 0)
if (game.ticks % 60 == 0 && game.time > 0)
{
game.time = std::max(0, game.time - 1);
game.time--;
}
// if (game.time == 0)
// translate_system.translate_system.disable();
}
int main()
{
spdlog::info("Initialize SDL...");
SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO))
{
spdlog::critical("Failed to initialize SDL!\nCause: {}", SDL_GetError());
std::terminate();
}
TTF_Init();
auto* window = SDL_CreateWindow("HansTheGatherer", WINDOW_WIDTH, WINDOW_HEIGHT, 0);
if (window == nullptr)
{
spdlog::critical("Failed to create SDL window!\nCause: {}", SDL_GetError());
}
auto* renderer = SDL_CreateRenderer(window, nullptr);
if (renderer == nullptr)
{
spdlog::critical("Failed to create SDL renderer!\nCause: {}", SDL_GetError());
}
auto* text_engine = TTF_CreateRendererTextEngine(renderer);
entt::registry registry;
registry.ctx().emplace<Game>(Game{.ticks = 0, .time = 60, .score = 0, .random_engine = {}});
registry.ctx().emplace<ButtonInput>(ButtonInput{});
auto sdl_handles = registry.ctx().emplace<SdlHandles>(
SdlHandles{.window = window, .renderer = renderer, .text_engine = text_engine});
AssetModule asset_module(registry);
AudioModule audio_module(registry);
@@ -77,56 +42,56 @@ int main()
input->just_released.clear();
// Input
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_EVENT_QUIT:
exit_gameloop = true;
break;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE)
{
exit_gameloop = true;
}
if (input->pressed.insert(event.key.key).second)
{
input->just_pressed.insert(event.key.key);
}
break;
case SDL_EVENT_KEY_UP:
if (input->pressed.erase(event.key.key) != 0)
{
input->just_released.insert(event.key.key);
}
break;
}
}
// SDL_Event event;
// while (SDL_PollEvent(&event))
// {
// switch (event.type)
// {
// case SDL_EVENT_QUIT:
// exit_gameloop = true;
// break;
// case SDL_EVENT_KEY_DOWN:
// if (event.key.key == SDLK_ESCAPE)
// {
// exit_gameloop = true;
// }
// if (input->pressed.insert(event.key.key).second)
// {
// input->just_pressed.insert(event.key.key);
// }
// break;
// case SDL_EVENT_KEY_UP:
// if (input->pressed.erase(event.key.key) != 0)
// {
// input->just_released.insert(event.key.key);
// }
// break;
// }
// }
AudioModule::FeedAudioStreams(registry);
// AudioModule::FeedAudioStreams(registry);
if (registry.ctx().get<Game>().time != 0)
{
increment_ticks(registry);
LevelModule::MoveBasket(registry);
LevelModule::SpawnFruits(registry);
LevelModule::CollectFruit(registry);
LevelModule::CollectSpider(registry);
// LevelModule::DespawnItems(registry);
}
PhysicsModule::TranslatePhysicsObject(registry);
PhysicsModule::PropagatePosition(registry);
PhysicsModule::RemoveCollisionMarker(registry);
PhysicsModule::CollisionCheck(registry);
SDL_RenderClear(sdl_handles.renderer);
Hall::Clear(0);
RenderModule::RenderSprites(registry);
RenderModule::RenderScore(registry);
SDL_RenderPresent(sdl_handles.renderer);
Hall::SetCommandSwapBuffers();
Hall::GetVSync();
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@@ -1,8 +1,6 @@
#include "physics.hpp"
#include "level.hpp"
#include <spdlog/spdlog.h>
void PhysicsModule::TranslatePhysicsObject(entt::registry& registry)
{
auto view = registry.view<Position, Velocity const>();

View File

@@ -5,6 +5,8 @@
#include "physics.hpp"
#include "sprite.hpp"
#include <Hall/Hall.h>
#include <format>
RenderModule::RenderModule(entt::registry& registry)
@@ -13,60 +15,42 @@ RenderModule::RenderModule(entt::registry& registry)
void RenderModule::RenderSprites(entt::registry& registry)
{
auto const& sdl_handles = registry.ctx().get<SdlHandles>();
auto const& game = registry.ctx().get<Game>();
auto sprites_view =
registry.view<Position const, Size const, Sprite const>(entt::exclude<Background>);
auto background_view = registry.view<Position const, Size const, Sprite const, Background>();
for (auto [entity, pos, size, sprite] : background_view.each())
{
auto render_sprite = [](entt::entity entity, Position const& pos, Size const& size, Sprite const&sprite){
TextureAtlasLayout layout = sprite.texture->texture_atlas_layout;
uint8_t row = sprite.texture_atlas_index / layout.columns;
uint8_t column = sprite.texture_atlas_index % layout.columns;
SDL_FRect srcrect{static_cast<float>(column * layout.width),
static_cast<float>(row * layout.height),
static_cast<float>(layout.width),
static_cast<float>(layout.height)};
// Problemchen: Wir können die Sprites nicht strecken... hat keiner Interpolation
// implementiert?
Hall::Draw(reinterpret_cast<unsigned short*>(sprite.texture->data), // Das ist 100% UB
column * layout.width,
row * layout.height,
pos.x,
pos.y,
layout.width,
layout.height,
sprite.texture->data_length);
};
SDL_FRect dstrect{static_cast<float>(pos.x),
static_cast<float>(pos.y),
static_cast<float>(size.w),
static_cast<float>(size.h)};
SDL_RenderTexture(sdl_handles.renderer, sprite.texture->sdl_texture, &srcrect, &dstrect);
}
for (auto [entity, pos, size, sprite] : sprites_view.each())
{
TextureAtlasLayout layout = sprite.texture->texture_atlas_layout;
uint8_t row = sprite.texture_atlas_index / layout.columns;
uint8_t column = sprite.texture_atlas_index % layout.columns;
SDL_FRect srcrect{static_cast<float>(column * layout.width),
static_cast<float>(row * layout.height),
static_cast<float>(layout.width),
static_cast<float>(layout.height)};
SDL_FRect dstrect{static_cast<float>(pos.x),
static_cast<float>(pos.y),
static_cast<float>(size.w),
static_cast<float>(size.h)};
SDL_RenderTexture(sdl_handles.renderer, sprite.texture->sdl_texture, &srcrect, &dstrect);
}
background_view.each(render_sprite);
sprites_view.each(render_sprite);
}
void RenderModule::RenderScore(entt::registry& registry)
{
auto const& sdl_handles = registry.ctx().get<SdlHandles>();
// auto const& sdl_handles = registry.ctx().get<SdlHandles>();
auto const& game = registry.ctx().get<Game>();
auto const& font_assets = registry.ctx().get<FontAssets>();
// auto const& font_assets = registry.ctx().get<FontAssets>();
auto score_string = std::format("Score: {}\nTime: {}", game.score, game.time);
auto text = TTF_CreateText(sdl_handles.text_engine,
font_assets.default_font.font,
score_string.c_str(),
score_string.length());
TTF_DrawRendererText(text, 0.0, 0.0);
TTF_DestroyText(text);
// auto text = TTF_CreateText(sdl_handles.text_engine,
// font_assets.default_font.font,
// score_string.c_str(),
// score_string.length());
// TTF_DrawRendererText(text, 0.0, 0.0);
// TTF_DestroyText(text);
}

View File

@@ -2,7 +2,6 @@
#include "assets.hpp"
#include <SDL3/SDL.h>
#include <cstdint>
struct Sprite

11
wuehans/CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
add_library(WueHans STATIC start.S syscall.c)
target_include_directories(WueHans PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(WueHans PRIVATE RISCV_Options)
target_link_options(WueHans PUBLIC
-T ${CMAKE_CURRENT_SOURCE_DIR}/wuehans.ld
)

54
wuehans/start.S Normal file
View File

@@ -0,0 +1,54 @@
.section .text.startSection
.global _Z4mainv
.global _start
_start:
/* zero-initialize all registers */
addi x1, zero, 0
addi x2, zero, 0
addi x3, zero, 0
addi x4, zero, 0
addi x5, zero, 0
addi x6, zero, 0
addi x7, zero, 0
addi x8, zero, 0
addi x9, zero, 0
addi x10, zero, 0
addi x11, zero, 0
addi x12, zero, 0
addi x13, zero, 0
addi x14, zero, 0
addi x15, zero, 0
addi x16, zero, 0
addi x17, zero, 0
addi x18, zero, 0
addi x19, zero, 0
addi x20, zero, 0
addi x21, zero, 0
addi x22, zero, 0
addi x23, zero, 0
addi x24, zero, 0
addi x25, zero, 0
addi x26, zero, 0
addi x27, zero, 0
addi x28, zero, 0
addi x29, zero, 0
addi x30, zero, 0
addi x31, zero, 0
/* set stack pointer */
.equ STACK_START, 0x02000000
lui sp, %hi(STACK_START)
addi sp, sp, %lo(STACK_START)
/* push zeros on the stack for argc and argv */
/* (stack is aligned to 16 bytes in riscv calling convention) */
addi sp,sp,-16
sw zero,0(sp)
sw zero,4(sp)
sw zero,8(sp)
sw zero,12(sp)
/* jump to libc init */
// j _Z4mainv
j main

186
wuehans/syscall.c Normal file
View File

@@ -0,0 +1,186 @@
// #include <fatfs/ff.h>
#include <wuehans_config.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
/* #include <DebugHelper.h> */
void *__dso_handle = 0;
// const TCHAR STDOUT[30] = "stdout.txt";
// const TCHAR STDERR[30] = "stderr.txt";
// FIL fp = {0};
// FATFS FatFs = {0};
// BYTE is_mounted = 0;
// struct FD_Data {
// FIL fp;
// BYTE mode;
// BYTE is_open;
// } FD_Data_default = {NULL, FA_READ, 0};
// typedef struct FD_Data FD_Data;
// #define FILE_AMOUNT 4
// static FD_Data fd_data[FILE_AMOUNT];
// int _write(int fd, char *ptr, int len) {
// if (is_mounted == 0) {
// f_mount(&FatFs, "", 0);
// is_mounted = 1;
// }
// // stdout
// if (fd == 1) {
// FIL write_file;
// FRESULT fr = f_open(&write_file, STDOUT, FA_OPEN_APPEND | FA_WRITE);
// if (fr != 0) {
// return fr;
// }
// UINT written = 0;
// fr = f_write(&write_file, ptr, len, &written);
// if (fr != 0) {
// // TODO: Set errno
// return -1;
// }
// f_close(&write_file);
// // HACK: Hackermann
// volatile int* x = (int*) 0x80000000;
// int b = *x;
// // HACK: Hackermann Ende
// return written;
// } else if (fd > 2 && (fd-3) < FILE_AMOUNT && fd_data[fd-3].is_open) {
// UINT bw = 0;
// f_write(&fd_data[fd-3].fp, ptr, len, &bw);
// return bw;
// }
// return -1;
// }
// int _read(int fd, char *ptr, int len) {
// if (fd > 2 && (fd-3) < FILE_AMOUNT && fd_data[fd-3].is_open) {
// UINT bytesRead = 0;
// FRESULT result = f_read(&fd_data[fd-3].fp, ptr, len, &bytesRead);
// /* ScreenPrint("_read"); */
// return bytesRead;
// }
// return -1;
// }
// void *_sbrk(int incr) {
// extern int heap_begin;
// static unsigned char *heap = NULL;
// unsigned char *prev_heap;
// if (heap == NULL) {
// heap = (unsigned char *)&heap_begin;
// }
// prev_heap = heap;
// if ((int)heap + incr < heap_begin + HEAP_SIZE) {
// heap += incr;
// }
// /* ScreenPrint("_sbrk"); */
// return prev_heap;
// }
// int _fstat(int fd, struct stat *st) {
// st->st_mode = S_IFREG;
// /* ScreenPrint("_fstat"); */
// return 0;
// }
// int _lseek(int fd, int offset, int whence) {
// // Invalid lseek call
// if (fd <= 2 || (fd-3) >= FILE_AMOUNT || !fd_data[fd-3].is_open) {
// return -1;
// }
// // Calculate the correct offset for f_lseek()
// FSIZE_t ofs = 0;
// switch (whence) {
// // Set to offset directly
// case SEEK_SET:
// ofs = offset;
// break;
// // Increment by offset
// case SEEK_CUR:
// ofs = f_tell(&fd_data[fd-3].fp) + offset;
// break;
// // Append offset to EOF
// case SEEK_END:
// ofs = f_size(&fd_data[fd-3].fp) + offset;
// break;
// default:
// return -1;
// }
// FRESULT result = f_lseek(&fd_data[fd-3].fp, ofs);
// return result;
// }
// int _open(const char *name, int flags, int mode) {
// if (is_mounted == 0) {
// f_mount(&FatFs, "", 0);
// is_mounted = 1;
// }
// int i;
// for (i = 0; i < FILE_AMOUNT; i++) {
// if (fd_data[i].is_open == 1) {
// // Entry already in use
// continue;
// }
// mode = FA_READ;
// FRESULT fr = f_open(&fd_data[i].fp, name, mode);
// if (fr != FR_OK) {
// return -1;
// }
// fd_data[i].mode = mode;
// fd_data[i].is_open = 1;
// // Exclude stdout, stderr, stdin
// return i+3;
// }
// return -1;
// }
// int _close(int fd) {
// if (fd > 2 && (fd-3) < FILE_AMOUNT) {
// fd_data[fd-3].is_open = 0;
// return 0;
// }
// return -1;
// }
int _isatty(int fd)
{
errno = ENOTTY;
/* ScreenPrint("isatty"); */
return 0;
}
void _exit(int status) {
while (1) {
}
}
int _kill(int pid, int sig) {
errno = EINVAL;
return -1;
}
int _getpid() { return -1; }

207
wuehans/wuehans.ld Normal file
View File

@@ -0,0 +1,207 @@
/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */
/* Default linker script, for normal executables */
/* Copyright (C) 2014-2017 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
"elf32-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
.startSection :
{
KEEP(*(.text.startSection))
}
.text :
{
*(.text)
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
}
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) }
.iplt : { *(.iplt) }
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 :
{
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
}
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
.gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges
.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
. = DATA_SEGMENT_RELRO_END (0, .);
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
.got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata :
{
__global_pointer$ = . + 0x800;
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
*(.sdata .sdata.* .gnu.linkonce.s.*)
}
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.sbss :
{
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
}
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
PROVIDE(_bss_end = .);
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
. = ALIGN(32 / 8);
. = SEGMENT_START("ldata-segment", .);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
PROVIDE(heap_begin = .);
}

7
wuehans/wuehans_config.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef WUEHANS_CONFIG_H
#define WUEHANS_CONFIG_H
// Heap Size in Bytes
#define HEAP_SIZE 0x800000
#endif