started to implement audio mixer, game working but no audio yet

This commit is contained in:
Dennis Eichhorn 2024-11-29 22:56:22 +01:00
parent 2943d418e2
commit faf5f98914
47 changed files with 2381 additions and 1210 deletions

198
asset/AssetArchive.h Normal file
View File

@ -0,0 +1,198 @@
/**
* Jingga
*
* @copyright Jingga
* @license License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_ASSET_ARCHIVE_H
#define TOS_ASSET_ARCHIVE_H
#include "../stdlib/Types.h"
#include "../utils/StringUtils.h"
#include "../utils/EndianUtils.h"
#include "../utils/Utils.h"
#include "../stdlib/simd/SIMD_I32.h"
#include "../memory/RingMemory.h"
#include "../memory/BufferMemory.h"
#include "AssetManagementSystem.h"
#if _WIN32
#include <windows.h>
#include "../platform/win32/FileUtils.cpp"
#elif __linux__
#include "../platform/win32/FileUtils.cpp"
#endif
struct AssetArchiveElement {
int32 type;
int32 start;
int32 length;
int32 dependency_start; // actual index for asset_dependencies
int32 dependency_count;
};
struct AssetArchiveHeader {
int32 version;
uint32 asset_count;
uint32 asset_dependency_count;
AssetArchiveElement* asset_element; // is not the owner of the data
int32* asset_dependencies; // is not the owner of the data
};
struct AssetArchive {
AssetArchiveHeader header;
byte* data; // owner of the data
FileHandler fd;
};
// Calculates how large the header memory has to be to hold all its information
int32 asset_archive_header_size(AssetArchive* archive, byte* data)
{
data += sizeof(archive->header.version);
int32 asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(archive->header.asset_count);
int32 asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(archive->header.asset_dependency_count);
return sizeof(archive->header.version)
+ sizeof(archive->header.asset_count)
+ sizeof(archive->header.asset_dependency_count)
+ asset_count * sizeof(AssetArchiveElement)
+ asset_dependency_count * sizeof(int32);
}
void asset_archive_header_load(AssetArchiveHeader* header, byte* data, int32 steps = 8)
{
header->version = SWAP_ENDIAN_LITTLE(*((int32 *) data));
data += sizeof(header->version);
header->asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(header->asset_count);
header->asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(header->asset_dependency_count);
memcpy(header->asset_element, data, header->asset_count * sizeof(AssetArchiveElement));
data += header->asset_count * sizeof(AssetArchiveElement);
SWAP_ENDIAN_LITTLE_SIMD(
(int32 *) header->asset_element,
(int32 *) header->asset_element,
header->asset_count * sizeof(AssetArchiveElement) / 4, // everything is 4 bytes -> super easy to swap
steps
);
header->asset_dependencies = (int32 *) ((byte *) header->asset_element + header->asset_count * sizeof(AssetArchiveElement));
memcpy(header->asset_dependencies, data, header->asset_dependency_count * sizeof(int32));
SWAP_ENDIAN_LITTLE_SIMD(
(int32 *) header->asset_dependencies,
(int32 *) header->asset_dependencies,
header->asset_count * header->asset_dependency_count, // everything is 4 bytes -> super easy to swap
steps
);
}
inline
AssetArchiveElement* asset_archive_element_find(const AssetArchive* archive, int32 id)
{
return &archive->header.asset_element[id];
}
void asset_archive_load(AssetArchive* archive, const char* path, BufferMemory* buf, RingMemory* ring, int32 steps = 8)
{
// Get file handle
archive->fd = file_read_async_handle(path);
FileBody file;
file.size = 64;
// Find header size
file.content = ring_get_memory(ring, file.size);
file_read(archive->fd, &file, 0, file.size);
file.size = asset_archive_header_size(archive, file.content);
// Reserve memory for the header
archive->data = buffer_get_memory(
buf,
file.size
- sizeof(archive->header.version)
- sizeof(archive->header.asset_count)
- sizeof(archive->header.asset_dependency_count),
4
);
// Read entire header
file.content = ring_get_memory(ring, file.size);
file_read(archive->fd, &file, 0, file.size);
asset_archive_header_load(&archive->header, file.content, steps);
}
// @performance This can probably be done much faster by handling the loading of dependencies faster
void asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetManagementSystem* ams_array, RingMemory* ring)
{
AssetArchiveElement* element = &archive->header.asset_element[id];
AssetManagementSystem* ams = element->type > 0
? &ams_array[element->type]
: &ams_array[0];
uint64 hash = hash_djb2((const char *) &id);
// @performance I think we could optimize the ams_reserver_asset in a way so we don't have to lock it the entire time
pthread_mutex_lock(&ams->mutex);
// @bug this is not how this function works
if (hashmap_get_entry(&ams->hash_map, (const char *) &id, hash)) {
pthread_mutex_unlock(&ams->mutex);
}
if (element->type == 0) {
// @bug We can't just do this, this won't work. Check if we might want to change the asset management directly to hash indices or at least int values
Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length));
asset->self = (byte *) (asset + 1);
FileBody file = {};
file.content = asset->self;
// We are directly reading into the correct destination
file_read(archive->fd, &file, element->start, element->length);
} else {
// We are reading into temp memory since we have to perform transformations on the data
FileBodyAsync file = {};
file_read_async(archive->fd, &file, element->start, element->length, ring);
// This happens while the file system loads the data
Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length));
asset->self = (byte *) (asset + 1);
byte* data = ring_get_memory(ring, element->length, 64);
size_t data_size = 0;
// @todo create platform wrapper
GetOverlappedResult(archive->fd, &file.ov, NULL, true);
switch (element->type) {
case 1: {
} break;
default: {
}
}
memcpy(asset->self, data, data_size);
}
pthread_mutex_unlock(&ams->mutex);
// @performance maybe do in worker threads?
for (int32 i = 0; i < element->dependency_count; ++i) {
asset_archive_asset_load(archive, id, ams, ring);
}
}
#endif

View File

@ -1,58 +0,0 @@
/**
* Jingga
*
* @copyright Jingga
* @license License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_ASSET_FILE_H
#define TOS_ASSET_FILE_H
#include "../stdlib/Types.h"
enum AssetType {
ASSET_TYPE_RAW_IMG,
ASSET_TYPE_PNG,
ASSET_TYPE_RAW_SOUND,
ASSET_TYPE_WAV,
ASSET_TYPE_OBJBIN,
ASSET_TYPE_MATBIN,
ASSET_TYPE_ANIBIN,
ASSET_TYPE_RAW_TEXTURE, // may include bump map, normal map, ...
};
struct AssetFileTableOfContent {
uint32 asset_id;
AssetType asset_type;
uint64 asset_offset;
};
struct AssetFileHeader {
uint32 version;
uint32 asset_count;
AssetFileTableOfContent* table_of_content;
};
struct AssetFile {
AssetFileHeader header;
byte* data;
};
void asset_file_load_header(void* fp, AssetFileHeader* header) {}
void asset_file_write_header() {}
// add asset at end (automatically) or in pos
void asset_file_add_asset(void* fp, AssetFile* asset_file, byte* asset, int64 pos = -1) {}
// remove asset by id or pos
void asset_file_remove_asset(void* fp, AssetFile* asset_file, int64 id, int64 pos = -1) {}
// load the toc info of an asset by id or pos
AssetFileTableOfContent* asset_file_load_asset_toc(AssetFileHeader* header, int64 id, int64 pos = -1) {}
#endif

View File

@ -39,8 +39,11 @@ struct AssetManagementSystem {
// Actual asset data
ChunkMemory asset_data_memory;
// @performance Do we really need the linked list, the ChunkMemory should allow us to do some smart stuff
Asset* first;
Asset* last;
pthread_mutex_t mutex;
};
void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 chunk_size, int32 count)
@ -56,6 +59,8 @@ void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 chunk_size,
ams->first = NULL;
ams->last = NULL;
pthread_mutex_init(&ams->mutex, NULL);
}
// WARNING: buf size see ams_get_buffer_size
@ -84,10 +89,17 @@ void ams_create(AssetManagementSystem* ams, byte* buf, int chunk_size, int count
ams->first = NULL;
ams->last = NULL;
pthread_mutex_init(&ams->mutex, NULL);
}
void ams_free(AssetManagementSystem* ams)
{
pthread_mutex_destroy(&ams->mutex);
}
inline
int32 ams_calculate_chunks(AssetManagementSystem* ams, int32 byte_size)
int32 ams_calculate_chunks(const AssetManagementSystem* ams, int32 byte_size)
{
return (int32) CEIL_DIV(byte_size, ams->asset_data_memory.chunk_size);
}
@ -201,8 +213,34 @@ Asset* ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 index)
return entry ? (Asset *) entry->value : NULL;
}
// @performance We could probably avoid locking by adding a atomic flag to indicate if the value is valid
Asset* threaded_ams_get_asset(AssetManagementSystem* ams, uint64 element) {
pthread_mutex_lock(&ams->mutex);
Asset* asset = ams_get_asset(ams, element);
pthread_mutex_unlock(&ams->mutex);
return asset;
}
Asset* threaded_ams_get_asset(AssetManagementSystem* ams, const char* key) {
pthread_mutex_lock(&ams->mutex);
Asset* asset = ams_get_asset(ams, key);
pthread_mutex_unlock(&ams->mutex);
return asset;
}
Asset* threaded_ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 index) {
pthread_mutex_lock(&ams->mutex);
Asset* asset = ams_get_asset(ams, key, index);
pthread_mutex_unlock(&ams->mutex);
return asset;
}
// @todo implement defragment command to optimize memory layout since the memory layout will become fragmented over time
// @performance This function is VERY important, check if we can optimize it
Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 elements = 1)
{
int64 free_asset = chunk_reserve(&ams->asset_memory, elements, true);
@ -224,7 +262,7 @@ Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 el
chunk_reserve_index(&ams->asset_data_memory, free_asset, elements, true);
asset->self = chunk_get_element(&ams->asset_data_memory, free_asset);
asset->size = elements; // Curcial for freeing
asset->size = elements; // Crucial for freeing
asset->ram_size = (ams->asset_memory.chunk_size + ams->asset_data_memory.chunk_size) * elements;
DEBUG_MEMORY_RESERVE((uint64) asset->self, elements * ams->asset_data_memory.chunk_size, 180);

View File

@ -10,6 +10,7 @@
#define TOS_ASSET_TYPE_H
enum AssetType {
ASSET_TYPE_GENERAL,
ASSET_TYPE_OBJ,
ASSET_TYPE_TEXTURE,
ASSET_TYPE_AUDIO,

View File

@ -13,9 +13,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
#include "Audio.h"
@ -32,92 +32,4 @@ void audio_from_file(RingMemory* ring, const char* path, Audio* audio)
}
}
inline
void audio_fill_buffer(
AudioSetting* setting, uint32 to_fill, const Audio* sound,
int16* buffer1, int32 buffer1_size,
int16* buffer2 = NULL, int32 buffer2_size = 0
)
{
uint32 limit = to_fill / setting->sample_size;
buffer1_size /= setting->sample_size;
buffer2_size /= setting->sample_size;
uint32 sample_count = sound->size / sound->sample_size;
f32 volume_scale = setting->volume * setting->volume;
const int16* data = (int16*) sound->data;
uint32 sample_index = setting->sample_index;
uint32 i;
for (i = 0; i < limit && i < buffer1_size; ++i) {
if (sample_index >= sample_count) {
sample_index = 0;
// @question why are we doing this?
setting->sample_index = 0;
}
buffer1[i * 2] = (int16) (data[sample_index * 2] * volume_scale);
buffer1[i * 2 + 1] = (int16) (data[sample_index * 2 + 1] * volume_scale);
++sample_index;
}
for (; i < limit && buffer2_size; ++i) {
if (sample_index >= sample_count) {
sample_index = 0;
// @question why are we doing this?
setting->sample_index = 0;
}
buffer2[i * 2] = (int16) (data[sample_index * 2] * volume_scale);
buffer2[i * 2 + 1] = (int16) (data[sample_index * 2 + 1] * volume_scale);
++sample_index;
}
++setting->sample_output;
setting->sample_buffer_size = to_fill;
}
// @bug This is slower
/*
inline
void fill_audio_buffer(AudioSetting* audio_handle, uint32 to_fill, Audio* sound, int32 steps = 8)
{
uint32 limit = to_fill / audio_handle->sample_size;
uint32 sample_count = sound->size / sound->sample_size;
uint32 sample_index = audio_handle->sample_index;
// Copy all data until end
int32 offset;
memcpy(
audio_handle->buffer,
sound->data + audio_handle->sample_index * audio_handle->sample_size,
(offset = OMS_MIN(sample_count - sample_index, limit)) * audio_handle->sample_size
);
// If we are wrapping (play sound on loop)
// @todo we still need a setting if we actually want to do this
if (offset != limit) {
audio_handle->sample_index = 0;
memcpy(
audio_handle->buffer + offset * audio_handle->sample_size,
sound->data,
(limit - offset) * audio_handle->sample_size
);
}
simd_mult(
(int16 *) audio_handle->buffer,
(f32) audio_handle->volume / 100.0f,
(int16 *) audio_handle->buffer, limit * 2,
steps
);
++audio_handle->sample_output;
audio_handle->sample_buffer_size = to_fill;
}
*/
#endif

View File

@ -11,6 +11,7 @@
#include "../stdlib/Types.h"
// This represents the audio file
struct Audio {
// bits per sample
// usually 48000 or 44100

347
audio/AudioMixer.h Normal file
View File

@ -0,0 +1,347 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_AUDIO_MIXER_H
#define TOS_AUDIO_MIXER_H
#include "../stdlib/Types.h"
#include "Audio.h"
#include "AudioSetting.h"
#include "../utils/MathUtils.h"
#include "../memory/ChunkMemory.h"
#include "../math/matrix/MatrixFloat32.h"
#if DIRECT_SOUND
#include "../platform/win32/audio/DirectSound.h"
#elif XAUDIO2
#include "../platform/win32/audio/XAudio2.h"
#endif
enum AudioEffect {
AUDIO_EFFECT_NONE,
AUDIO_EFFECT_ECHO = 1,
AUDIO_EFFECT_REVERB = 2,
AUDIO_EFFECT_UNDERWATER = 4,
AUDIO_EFFECT_CAVE = 8,
AUDIO_EFFECT_LOWPASS = 16,
AUDIO_EFFECT_HIGHPASS = 32,
AUDIO_EFFECT_FLANGER = 64,
AUDIO_EFFECT_TREMOLO = 128,
AUDIO_EFFECT_DISTORTION = 256,
AUDIO_EFFECT_CHORUS = 512,
AUDIO_EFFECT_PITCH_SHIFT = 1024,
AUDIO_EFFECT_GRANULAR_DELAY = 2048,
AUDIO_EFFECT_FM = 4096,
AUDIO_EFFECT_STEREO_PANNING = 8192,
AUDIO_EFFECT_EASE_IN = 16384,
AUDIO_EFFECT_EASE_OUT = 32768,
};
struct AudioInstance {
int64 id;
AudioLocationSetting origin;
uint32 audio_size;
byte* audio_data;
};
struct AudioMixer {
ChunkMemory audio_instances;
bool is_active;
uint64 effect;
AudioSetting settings;
AudioLocationSetting camera;
#if DIRECT_SOUND
DirectSoundSetting api_setting;
#elif XAUDIO2
XAudio2Setting api_setting;
#endif
int16* buffer_temp;
// @todo add mutex for locking and create threaded functions
// do we need a condition or semaphore?
};
void audio_mixer_add(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin)
{
int64 index = chunk_reserve(&mixer->audio_instances, 1);
if (index < 0) {
return;
}
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, index);
instance->id = id;
instance->audio_size = audio->size;
instance->audio_data = audio->data;
if (origin) {
memcpy(&instance->origin, origin, sizeof(AudioLocationSetting));
}
}
void audio_mixer_add_unique(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin)
{
for (int32 i = 0; i < mixer->audio_instances.count; ++i) {
// @performance We are not really utilizing chunk memory.
// Maybe a simple array would be better
// Or we need to use more chunk functions / maybe even create a chunk_iterate() function?
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
if (instance->id == id) {
return;
}
}
audio_mixer_add(mixer, id, audio, origin);
}
void audio_mixer_remove(AudioMixer* mixer, int64 id)
{
for (int32 i = 0; i < mixer->audio_instances.count; ++i) {
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
if (instance->id == id) {
instance->id = 0;
chunk_free_element(&mixer->audio_instances, i);
// No return, since we want to remove all instances
}
}
}
void apply_echo(int16* buffer, uint16 buffer_size, f32 delay, f32 feedback, int32 sample_rate) {
int32 delay_samples = (int32) (delay * sample_rate);
for (int32 i = delay_samples; i < buffer_size; ++i) {
buffer[i] += (int16) (buffer[i - delay_samples] * feedback);
}
}
void apply_reverb(int16* buffer, uint16 buffer_size, f32 intensity) {
intensity *= 0.5f;
for (int32 i = 1; i < buffer_size; ++i) {
buffer[i] += (int16) (buffer[i - 1] * intensity); // Simple reverb with decay
}
}
void apply_cave(int16* buffer, uint16 buffer_size, int32 sample_rate) {
f32 echo_delay = 0.1f; // Echo delay in seconds
f32 feedback = 0.3f; // Echo feedback level
apply_echo(buffer, buffer_size, echo_delay, feedback, sample_rate);
apply_reverb(buffer, buffer_size, 0.4f); // Add mild reverb
}
void apply_underwater(int16* buffer, uint16 buffer_size) {
for (int32 i = 0; i < buffer_size; ++i) {
buffer[i] = (int16) sinf(buffer[i] * 0.5f); // Dampen + distortion
}
}
void apply_flanger(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) {
int32 delay_samples = (int32) (depth * sample_rate);
f32 temp = OMS_TWO_PI * rate / sample_rate;
for (int32 i = 0; i < buffer_size; ++i) {
int32 delay = (int32) (delay_samples * (0.5f + 0.5f * sinf(i * temp)));
if (i >= delay) {
buffer[i] += (int16) (buffer[i - delay] * 0.5f);
}
}
}
void apply_tremolo(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) {
f32 temp = OMS_TWO_PI * rate / sample_rate;
f32 temp2 = (1.0f - depth) + depth;
for (int32 i = 0; i < buffer_size; ++i) {
f32 mod = temp2 * (0.5f + 0.5f * sinf(i * temp));
buffer[i] = (int16) (buffer[i] * mod);
}
}
void apply_distortion(int16* buffer, uint16 buffer_size, f32 gain) {
for (int32 i = 0; i < buffer_size; ++i) {
buffer[i] = (int16) tanh(buffer[i] * gain);
}
}
void apply_chorus(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) {
f32 temp = OMS_TWO_PI * rate / sample_rate;
int32 max_delay = (int32) (depth * sample_rate);
for (int32 i = 0; i < buffer_size; ++i) {
int32 delay = (int32) (max_delay * (0.5f + 0.5f * sinf(i * temp)));
if (i >= delay) {
buffer[i] += (int16) (buffer[i - delay] * 0.5f);
}
}
}
void apply_pitch_shift(int16* buffer, uint16 buffer_size, f32 pitch_factor) {
for (int32 i = 0; i < buffer_size; ++i) {
buffer[i] = (int16) (buffer[i] * pitch_factor);
}
}
void apply_granular_delay(int16* buffer, uint16 buffer_size, f32 delay, f32 granularity, int32 sample_rate) {
int32 delay_samples = (int32) (delay * sample_rate);
int32 limit = (int32) (granularity * sample_rate);
for (int32 i = 0; i < buffer_size; ++i) {
if (i % limit == 0 && i >= delay_samples) {
buffer[i] += (int16) (buffer[i - delay_samples] * 0.6f);
}
}
}
void apply_frequency_modulation(int16* buffer, uint16 buffer_size, f32 mod_freq, f32 mod_depth, int32 sample_rate) {
f32 temp = OMS_TWO_PI * mod_freq / sample_rate;
for (int32 i = 0; i < buffer_size; ++i) {
buffer[i] = (int16) (buffer[i] * sinf(i * temp) * mod_depth);
}
}
void apply_stereo_panning(int16* buffer, int32 buffer_size, f32 pan) {
f32 left_gain = 1.0f - pan;
f32 right_gain = pan;
for (int32 i = 0; i < buffer_size; ++i) {
buffer[i] = (int16) (buffer[i] * left_gain);
buffer[i + 1] = (int16) (buffer[i + 1] * right_gain);
}
}
void apply_highpass(int16* buffer, uint16 buffer_size, f32 cutoff, int32 sample_rate) {
f32 rc = 1.0f / (OMS_TWO_PI * cutoff);
f32 dt = 1.0f / sample_rate;
f32 alpha = rc / (rc + dt);
f32 previous = buffer[0];
f32 previous_output = buffer[0];
for (int32 i = 1; i < buffer_size; ++i) {
f32 current = buffer[i];
buffer[i] = (int16) (alpha * (previous_output + current - previous));
previous = current;
previous_output = buffer[i];
}
}
void apply_lowpass(int16* buffer, uint16 buffer_size, f32 cutoff, int32 sample_rate) {
f32 rc = 1.0f / (OMS_TWO_PI * cutoff);
f32 dt = 1.0f / sample_rate;
f32 alpha = dt / (rc + dt);
f32 previous = buffer[0];
for (int32 i = 1; i < buffer_size; ++i) {
buffer[i] = (int16) (previous + alpha * (buffer[i] - previous));
previous = buffer[i];
}
}
void audio_mixer_mix(AudioMixer *mixer) {
uint16 limit = (uint16) (mixer->settings.sample_buffer_size / mixer->settings.sample_size);
for (int32 i = 0; i < mixer->audio_instances.count; ++i) {
AudioInstance* sound = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
if (sound->id == 0) {
continue;
}
// Compute the vector from the player to the sound's origin
v3_f32 to_sound;
vec3_sub(&to_sound, &sound->origin.audio_location, &mixer->camera.audio_location);
f32 distance = vec3_length(&to_sound);
f32 distance_attenuation = OMS_MAX(0.0f, 1.0f - (distance / 50.0f));
vec3_normalize(&to_sound);
f32 alignment = vec3_dot(&mixer->camera.audio_lookat, &to_sound);
f32 directional_attenuation = OMS_MAX(0.0f, alignment);
f32 total_attenuation = distance_attenuation * directional_attenuation;
// Temporary buffer for effects processing
// @performance If there are situations where only one file exists in the mixer that should be played we could directly write to
// the output buffer improving the performance. Some of those mixers are: music, cinematic, ui
// Careful, NOT voice since we will probably manually layer them according to their position?
for (int32 j = 0; j < limit; ++j) {
// @todo if repeat handle here
mixer->buffer_temp[j] = (int16) (sound->audio_data[j * 2] * mixer->settings.master_volume * total_attenuation);
mixer->buffer_temp[j + 1] = (int16) (sound->audio_data[j * 2 + 2] * mixer->settings.master_volume * total_attenuation);
// @performance Some adjustments could be made right here the question is if this is faster.
// Probably depends on how likely the adjustment is to happen.
}
// Apply effects based on sound's effect type
if (mixer->effect) {
if (mixer->effect & AUDIO_EFFECT_ECHO) {
apply_echo(mixer->buffer_temp, limit, 0.2f, 0.4f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_REVERB) {
apply_reverb(mixer->buffer_temp, limit, 0.3f);
}
if (mixer->effect & AUDIO_EFFECT_UNDERWATER) {
apply_underwater(mixer->buffer_temp, limit);
}
if (mixer->effect & AUDIO_EFFECT_CAVE) {
apply_cave(mixer->buffer_temp, limit, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_LOWPASS) {
apply_lowpass(mixer->buffer_temp, limit, 500.0f, mixer->settings.sample_rate); // Cutoff frequency 500
}
if (mixer->effect & AUDIO_EFFECT_HIGHPASS) {
apply_highpass(mixer->buffer_temp, limit, 2000.0f, mixer->settings.sample_rate); // Cutoff frequency 2 kHz
}
if (mixer->effect & AUDIO_EFFECT_FLANGER) {
apply_flanger(mixer->buffer_temp, limit, 0.25f, 0.005f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_TREMOLO) {
apply_tremolo(mixer->buffer_temp, limit, 5.0f, 0.8f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_DISTORTION) {
apply_distortion(mixer->buffer_temp, limit, 10.0f);
}
if (mixer->effect & AUDIO_EFFECT_CHORUS) {
apply_chorus(mixer->buffer_temp, limit, 0.25f, 0.005f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_PITCH_SHIFT) {
apply_pitch_shift(mixer->buffer_temp, limit, 1.2f); // Slight pitch increase
}
if (mixer->effect & AUDIO_EFFECT_GRANULAR_DELAY) {
apply_granular_delay(mixer->buffer_temp, limit, 0.1f, 0.2f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_FM) {
apply_frequency_modulation(mixer->buffer_temp, limit, 2.0f, 0.5f, mixer->settings.sample_rate);
}
if (mixer->effect & AUDIO_EFFECT_STEREO_PANNING) {
apply_stereo_panning(mixer->buffer_temp, limit, 0.5f);
}
}
// Add the processed sound to the output buffer
for (int32 j = 0; j < limit; j++) {
mixer->settings.buffer[j] += mixer->buffer_temp[j];
}
}
}
#endif

View File

@ -10,11 +10,22 @@
#define TOS_AUDIO_SETTING_H
#include "../stdlib/Types.h"
#include "../math/matrix/MatrixFloat32.h"
#define SOUND_API_DIRECT_SOUND 0
#define SOUND_API_XAUDIO2 1
struct AudioSetting {
// position in the audio data
// WARNING: not the byte position, but the index based on the sample size
uint32 sample_index;
// @todo add more settings e.g. repeat etc
uint32 latency;
f32 master_volume;
// bits per sample
// usually 48000 or 44100
uint32 sample_rate;
@ -24,18 +35,9 @@ struct AudioSetting {
// usually 2 * 16 = 4
uint32 sample_size;
// position in the audio data
// WARNING: not the byte position, but the index based on the sample size
uint32 sample_index;
uint32 latency;
// how often has the audio_play been called (required for xaudio)
uint32 sample_output;
// 0.0 - 1.0
f32 volume;
// max buffer content/size
uint32 buffer_size;
@ -44,10 +46,20 @@ struct AudioSetting {
uint32 sample_buffer_size;
int16* buffer;
bool is_playing = false;
byte type = SOUND_API_DIRECT_SOUND;
// @todo add more settings e.g. repeat etc
};
struct AudioLocationSetting {
v3_f32 audio_location;
v3_f32 audio_lookat;
v3_f32 audio_moveto;
f32 audio_velocity;
};
inline
void update_audio_location_settings(AudioLocationSetting* settings)
{
vec3_normalize(&settings->audio_lookat);
}
#endif

View File

@ -2,30 +2,53 @@
#define TOS_ENVIRONMENT_GLOBE_H
#include "../stdlib/Types.h"
#include "../utils/MathUtils.h"
#include <math.h>
// day_length in seconds
/**
* Calculates the time of the day
*
* @param f64 time Can be any reference value, as long as it increments every second
* @param f32 day_length The length of a day in seconds
*
* @return f32 A day time ranging from 0.0 to 1.0
*/
f32 time_of_day(f64 time, f32 day_length) {
f64 t = time;
t = t / day_length;
t = t - (int32) t;
t /= day_length;
t -= (int32) t;
return (f32) t;
}
// time can either be actual time in seconds OR relative time to progra start
/**
* Calculates a daylight intensity based on the time of the day
*
* @param f64 time Can be any reference value, as long as it increments every second
* @param f32 day_length The length of a day in seconds
*
* @return f32 Light intensity
*/
f32 daylight_get(f64 time, f32 day_length) {
f32 timer = time_of_day(time, day_length);
if (timer < 0.5) {
f32 t = (timer - 0.25) * 100;
if (timer < 0.5f) {
f32 t = (timer - 0.25f) * 100.0f;
return 1 / (1 + powf(2, -t));
return 1.0f / (1.0f + powf(2.0f, -t));
}
f32 t = (timer - 0.85) * 100;
f32 t = (timer - 0.85f) * 100.0f;
return 1 - 1 / (1 + powf(2, -t));
return 1.0f - 1.0f / (1.0f + powf(2.0f, -t));
}
void sun_position_get(v3_f32* sun_position, f32 time, f32 radius, f32 day_duration) {
f32 angle = (time / day_duration) * 2.0f * OMS_PI;
sun_position->x = radius * cosf(angle);
sun_position->y = radius * sinf(angle);
sun_position->z = 0.0f;
}
#endif

View File

@ -8,9 +8,9 @@
#include "../stdlib/simd/SIMD_I32.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
struct GlyphMetrics {

View File

@ -19,10 +19,13 @@
#include "../RenderUtils.h"
#include "Opengl.h"
#ifdef _WIN32
#if _WIN32
#include <windows.h>
#include "../../platform/win32/UtilsWin32.h"
#include "../../platform/win32/FileUtils.cpp"
#include "../../platform/win32/Window.h"
#elif __linux__
#include "../../platform/linux/FileUtils.cpp"
#include "../../platform/linux/Window.h"
#endif
inline

View File

@ -13,9 +13,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
#include "Image.h"

View File

@ -5,9 +5,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
struct Language {

View File

@ -95,6 +95,18 @@ f32 vec2_dot(const v2_f32* a, const v2_f32* b) {
return a->x * b->x + a->y * b->y;
}
inline
f32 vec3_length(f32 x, f32 y, f32 z)
{
return sqrtf(x * x + y * y + z * z);
}
inline
f32 vec3_length(v3_f32* vec)
{
return sqrtf(vec->x * vec->x + vec->y * vec->y + vec->z * vec->z);
}
inline
void vec3_normalize(f32* __restrict x, f32* __restrict y, f32* __restrict z)
{

View File

@ -23,6 +23,12 @@
#include "../platform/linux/Allocator.h"
#endif
#if _WIN32
#include "../platform/win32/threading/Thread.h"
#elif __linux__
#include "../platform/linux/threading/Thread.h"
#endif
struct ChunkMemory {
byte* memory;
@ -37,6 +43,23 @@ struct ChunkMemory {
uint64* free;
};
struct ThreadedChunkMemory {
byte* memory;
uint64 count;
uint64 size;
uint64 chunk_size;
int64 last_pos;
int32 alignment;
// length = count
// free describes which locations are used and which are free
uint64* free;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
inline
void chunk_alloc(ChunkMemory* buf, uint64 count, uint64 chunk_size, int32 alignment = 64)
{

View File

@ -15,21 +15,21 @@
typedef RingMemory Queue;
inline
void queue_alloc(Queue* queue, uint64 size, uint32 element_size, int32 alignment = 64)
void queue_alloc(Queue* queue, uint64 element_count, uint32 element_size, int32 alignment = 64)
{
ring_alloc(queue, size, alignment);
ring_alloc(queue, element_count * element_size, alignment);
}
inline
void queue_init(Queue* queue, BufferMemory* buf, uint64 size, uint32 element_size, int32 alignment = 64)
void queue_init(Queue* queue, BufferMemory* buf, uint64 element_count, uint32 element_size, int32 alignment = 64)
{
ring_init(queue, buf, size, alignment);
ring_init(queue, buf, element_count * element_size, alignment);
}
inline
void queue_init(Queue* queue, byte* buf, uint64 size, uint32 element_size, int32 alignment = 64)
void queue_init(Queue* queue, byte* buf, uint64 element_count, uint32 element_size, int32 alignment = 64)
{
ring_init(queue, buf, size, alignment);
ring_init(queue, buf, element_count * element_size, alignment);
}
inline

View File

@ -45,14 +45,6 @@ struct RingMemory {
uint64 size;
int32 alignment;
int32 element_alignment;
// We support both conditional locking and semaphore locking
// These values are not initialized and not used unless you use the queue
pthread_mutex_t mutex;
pthread_cond_t cond;
sem_t empty;
sem_t full;
};
// @bug alignment should also include the end point, not just the start

View File

@ -9,6 +9,8 @@
#ifndef TOS_MEMORY_QUEUE_H
#define TOS_MEMORY_QUEUE_H
#include "../stdlib/Types.h"
#include "../utils/Utils.h"
#include "RingMemory.h"
#if _WIN32
@ -19,12 +21,35 @@
#include "../platform/linux/threading/Semaphore.h"
#endif
typedef RingMemory ThreadedQueue;
struct ThreadedQueue {
byte* memory;
byte* end;
byte* head;
// This variable is usually only used by single producer/consumer code mostly found in threads.
// One thread inserts elements -> updates head
// The other thread reads elements -> updates tail
// This code itself doesn't change this variable
byte* tail;
uint64 size;
int32 alignment;
int32 element_alignment;
// We support both conditional locking and semaphore locking
// These values are not initialized and not used unless you use the queue
pthread_mutex_t mutex;
pthread_cond_t cond;
sem_t empty;
sem_t full;
};
inline
void threaded_queue_alloc(ThreadedQueue* queue, uint64 size, uint32 element_count, int32 alignment = 64)
void threaded_queue_alloc(ThreadedQueue* queue, uint32 element_count, uint64 element_size, int32 alignment = 64)
{
ring_alloc(queue, size, alignment);
ring_alloc((RingMemory *) queue, element_count * element_size, alignment);
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->cond, NULL);
@ -34,9 +59,9 @@ void threaded_queue_alloc(ThreadedQueue* queue, uint64 size, uint32 element_coun
}
inline
void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint64 size, uint32 element_count, int32 alignment = 64)
void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint32 element_count, uint64 element_size, int32 alignment = 64)
{
ring_init(queue, buf, size, alignment);
ring_init((RingMemory *) queue, buf, element_count * element_size, alignment);
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->cond, NULL);
@ -46,9 +71,9 @@ void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint64 size, u
}
inline
void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint64 size, uint32 element_count, int32 alignment = 64)
void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint32 element_count, uint64 element_size, int32 alignment = 64)
{
ring_init(queue, buf, size, alignment);
ring_init((RingMemory *) queue, buf, element_count * element_size, alignment);
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->cond, NULL);
@ -60,24 +85,56 @@ void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint64 size, uint32 el
inline
void threaded_queue_free(ThreadedQueue* queue)
{
ring_free(queue);
ring_free((RingMemory *) queue);
sem_destroy(&queue->empty);
sem_destroy(&queue->full);
pthread_mutex_destroy(&queue->mutex);
pthread_cond_destroy(&queue->cond);
}
// @todo Create enqueue_unique
inline
void threaded_queue_enqueue_unique(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0)
{
ASSERT_SIMPLE((uint64_t) data % 4 == 0);
pthread_mutex_lock(&queue->mutex);
byte* tail = queue->tail;
while (tail != queue->tail) {
ASSERT_SIMPLE((uint64_t) tail % 4 == 0);
// @performance we could probably make this faster since we don't need to compare the entire range
if (is_equal_aligned(tail, data, size) == 0) {
pthread_mutex_unlock(&queue->mutex);
return;
}
ring_move_pointer((RingMemory *) queue, &tail, size, aligned);
}
while (!ring_commit_safe((RingMemory *) queue, size)) {
pthread_cond_wait(&queue->cond, &queue->mutex);
}
byte* mem = ring_get_memory((RingMemory *) queue, size, aligned);
memcpy(mem, data, size);
pthread_cond_signal(&queue->cond);
pthread_mutex_unlock(&queue->mutex);
}
// Conditional Lock
inline
void threaded_queue_enqueue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0)
{
pthread_mutex_lock(&queue->mutex);
while (!ring_commit_safe(queue, size)) {
while (!ring_commit_safe((RingMemory *) queue, size)) {
pthread_cond_wait(&queue->cond, &queue->mutex);
}
byte* mem = ring_get_memory(queue, size, aligned);
byte* mem = ring_get_memory((RingMemory *) queue, size, aligned);
memcpy(mem, data, size);
pthread_cond_signal(&queue->cond);
@ -89,11 +146,11 @@ byte* threaded_queue_enqueue_start(ThreadedQueue* queue, uint64 size, byte align
{
pthread_mutex_lock(&queue->mutex);
while (!ring_commit_safe(queue, size, aligned)) {
while (!ring_commit_safe((RingMemory *) queue, size, aligned)) {
pthread_cond_wait(&queue->cond, &queue->mutex);
}
return ring_get_memory(queue, size, aligned);
return ring_get_memory((RingMemory *) queue, size, aligned);
}
inline
@ -104,7 +161,7 @@ void threaded_queue_enqueue_end(ThreadedQueue* queue)
}
inline
byte* threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0)
void threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0)
{
pthread_mutex_lock(&queue->mutex);
@ -113,7 +170,7 @@ byte* threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte
}
memcpy(data, queue->tail, size);
ring_move_pointer(queue, &queue->tail, size, aligned);
ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned);
pthread_cond_signal(&queue->cond);
pthread_mutex_unlock(&queue->mutex);
@ -134,7 +191,7 @@ byte* threaded_queue_dequeue_start(ThreadedQueue* queue)
inline
void threaded_queue_dequeue_end(ThreadedQueue* queue, uint64 size, byte aligned = 0)
{
ring_move_pointer(queue, &queue->tail, size, aligned);
ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned);
pthread_cond_signal(&queue->cond);
pthread_mutex_unlock(&queue->mutex);
@ -147,7 +204,7 @@ void threaded_queue_enqueue_sem(ThreadedQueue* queue, byte* data, uint64 size, b
sem_wait(&queue->empty);
pthread_mutex_lock(&queue->mutex);
byte* mem = ring_get_memory(queue, size, aligned);
byte* mem = ring_get_memory((RingMemory *) queue, size, aligned);
memcpy(mem, data, size);
pthread_mutex_unlock(&queue->mutex);
@ -160,7 +217,7 @@ byte* threaded_queue_enqueue_start_sem(ThreadedQueue* queue, uint64 size, byte a
sem_wait(&queue->empty);
pthread_mutex_lock(&queue->mutex);
return ring_get_memory(queue, size, aligned);
return ring_get_memory((RingMemory *) queue, size, aligned);
}
inline
@ -177,7 +234,7 @@ byte* threaded_queue_dequeue_sem(ThreadedQueue* queue, byte* data, uint64 size,
pthread_mutex_lock(&queue->mutex);
memcpy(data, queue->tail, size);
ring_move_pointer(queue, &queue->tail, size, aligned);
ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned);
pthread_mutex_unlock(&queue->mutex);
sem_post(&queue->empty);
@ -195,7 +252,7 @@ byte* threaded_queue_dequeue_start_sem(ThreadedQueue* queue)
inline
void threaded_queue_dequeue_end_sem(ThreadedQueue* queue, uint64 size, byte aligned = 0)
{
ring_move_pointer(queue, &queue->tail, size, aligned);
ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned);
pthread_mutex_unlock(&queue->mutex);
sem_post(&queue->empty);

View File

@ -2,9 +2,13 @@
#define TOS_MODULE_MANAGER_H
#include "Module.h"
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/FileUtils.cpp"
#include "../platform/win32/UtilsWin32.h"
#elif __linux__
#include "../platform/linux/FileUtils.cpp"
#endif
struct ModuleManager {

View File

@ -13,9 +13,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
struct Skeleton {

View File

@ -13,9 +13,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
struct Hitbox {

View File

@ -13,9 +13,9 @@
#include "../memory/RingMemory.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
struct Material {

View File

@ -13,9 +13,9 @@
#include "../stdlib/Types.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
#include "../memory/RingMemory.h"
@ -27,7 +27,7 @@
// @todo how to handle different objects and groups?
// maybe make a mesh hold other meshes?
// @todo handle vertice arrays where for example no texture coordinates are defined/used
// @todo handle vertices arrays where for example no texture coordinates are defined/used
struct Mesh {
byte* data; // memory owner that subdevides into the pointers below
@ -406,7 +406,7 @@ void mesh_from_file_txt(
mesh->vertex_count = face_count * 3;
// Every face consists of 3 vertecies -> *3
// Every face consists of 3 vertices -> *3
for (int32 i = 0; i < face_count * 3; ++i) {
const f32* vertex_pos = &vertices[faces[i * face_size] * 3];
memcpy(
@ -415,7 +415,7 @@ void mesh_from_file_txt(
3 * sizeof(f32)
);
// Normals come before texture coordinates since we most likely need them more than texture (e.g. pre-copmuting of shadows on cpu etc.)
// Normals come before texture coordinates since we most likely need them more than texture (e.g. pre-computing of shadows on cpu etc.)
if (mesh->vertex_type & VERTEX_TYPE_NORMAL) {
const f32* normal_pos = &normals[faces[i * face_size + 1 + normal_offset] * 3];
@ -486,7 +486,7 @@ int32 mesh_from_file(
#if !_WIN32 && !__LITTLE_ENDIAN
mesh->version = endian_swap(mesh->version);
mesh->vertex_type = endian_swap(mesh->vertex_type);
mesh->verted_count = endian_swap(mesh->verted_count);
mesh->vertex_count = endian_swap(mesh->vertex_count);
#endif
int32 vertex_size = 0;

View File

@ -0,0 +1,259 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_PLATFORM_LINUX_FILE_UTILS_C
#define TOS_PLATFORM_LINUX_FILE_UTILS_C
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/limits.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include "../../stdlib/Types.h"
#include "../../utils/Utils.h"
#include "../../utils/TestUtils.h"
#include "../../memory/RingMemory.h"
#ifndef MAX_PATH
#define MAX_PATH PATH_MAX
#endif
typedef int32 FileHandler;
inline
void relative_to_absolute(const char* rel, char* path)
{
char self_path[MAX_PATH];
int32 self_path_length = readlink("/proc/self/exe", self_path, MAX_PATH - 1);
if (self_path_length == -1) {
return;
}
const char* temp = rel;
if (temp[0] == '.' && temp[1] == '/') {
temp += 2;
}
char* last = self_path + self_path_length;
while (*last != '/' && self_path_length > 0) {
--last;
--self_path_length;
}
++self_path_length;
memcpy(path, self_path, self_path_length);
strcpy(path + self_path_length, temp);
}
// @todo implement relative path support, similar to UtilsWin32
inline
uint64 file_size(const char* filename) {
struct stat buffer;
if (stat(filename, &buffer) != 0) {
return 0;
}
return buffer.st_size;
}
inline
uint64 file_last_modified(const char* filename)
{
struct stat buffer;
stat(filename, &buffer);
return (uint64) buffer.st_mtime;
}
inline
FileHandler file_append_handle(const char* path) {
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = open(full_path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
} else {
fp = open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
}
return fp;
}
inline
bool file_exists(const char* path) {
struct stat buffer;
const char* full_path = path;
char abs_path[MAX_PATH];
if (*path == '.') {
relative_to_absolute(path, abs_path);
full_path = abs_path;
}
return stat(full_path, &buffer) == 0;
}
inline
bool file_copy(const char* src, const char* dst) {
char src_full_path[MAX_PATH];
char dst_full_path[MAX_PATH];
if (*src == '.') {
relative_to_absolute(src, src_full_path);
src = src_full_path;
}
if (*dst == '.') {
relative_to_absolute(dst, dst_full_path);
dst = dst_full_path;
}
int32 src_fd = open(src, O_RDONLY);
if (src_fd < 0) {
return false;
}
int32 dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd < 0) {
close(src_fd);
return false;
}
char buffer[8192];
ssize_t bytes_read, bytes_written;
bool success = true;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
success = false;
break;
}
}
if (bytes_read < 0) {
success = false;
}
close(src_fd);
close(dst_fd);
return success;
}
inline
void file_read(const char* path, FileBody* file, RingMemory* ring) {
char full_path[MAX_PATH];
const char* abs_path = path;
if (*path == '.') {
relative_to_absolute(path, full_path);
abs_path = full_path;
}
int32 fp = open(abs_path, O_RDONLY);
if (fp < 0) {
file->size = 0;
file->content = NULL;
return;
}
struct stat file_stat;
if (fstat(fp, &file_stat) == -1) {
close(fp);
file->size = 0;
file->content = NULL;
return;
}
if (file_stat.st_size > MAX_UINT32) {
close(fp);
file->size = 0;
file->content = NULL;
return;
}
if (ring != NULL) {
file->content = ring_get_memory(ring, file_stat.st_size);
}
ssize_t bytes_read = read(fp, file->content, file_stat.st_size);
if (bytes_read != file_stat.st_size) {
close(fp);
file->content = NULL;
file->size = 0;
return;
}
file->content[bytes_read] = '\0';
file->size = bytes_read;
close(fp);
}
inline
bool file_write(const char* path, const FileBody* file) {
int32 fd;
char full_path[PATH_MAX];
if (*path == '.') {
relative_to_absolute(path, full_path);
path = full_path;
}
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
return false;
}
ASSERT_SIMPLE(file->size < MAX_UINT32);
ssize_t written = write(fd, file->content, file->size);
if (written < 0 || (size_t) written != file->size) {
close(fd);
return false;
}
if (close(fd) < 0) {
return false;
}
return true;
}
inline
void close_handle(FileHandler fp)
{
close(fp);
}
inline
void self_path(char* path) {
size_t len = readlink("/proc/self/exe", path, PATH_MAX);
if (len > 0) {
path[len] = '\0';
} else {
path[0] = '\0';
}
}
#endif

View File

@ -43,7 +43,7 @@ bool library_load(Library* lib)
memcpy(dst + dst_len - (sizeof(".so") - 1), "_temp", sizeof("_temp") - 1);
memcpy(dst + dst_len - (sizeof(".so") - 1) + (sizeof("_temp") - 1), ".so", sizeof(".so"));
lib->last_load = last_modified(src);
lib->last_load = file_last_modified(src);
file_copy(src, dst);
#endif

View File

@ -11,26 +11,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/limits.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include "../../stdlib/Types.h"
#include "../../utils/Utils.h"
#include "../../utils/TestUtils.h"
#include "../../memory/RingMemory.h"
#ifndef MAX_PATH
#define MAX_PATH PATH_MAX
#endif
int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
int result;
int32 sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
int32 result;
va_list args;
if (buffer == NULL || format == NULL || sizeOfBuffer == 0) {
@ -38,12 +24,10 @@ int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
}
va_start(args, format);
result = vsnprintf(buffer, sizeOfBuffer, format, args);
va_end(args);
if (result >= 0 && (size_t)result >= sizeOfBuffer) {
if (result >= 0 && (size_t) result >= sizeOfBuffer) {
buffer[sizeOfBuffer - 1] = '\0';
return 80;
}
@ -52,228 +36,4 @@ int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
return result;
}
inline
void relative_to_absolute(const char* rel, char* path)
{
char self_path[MAX_PATH];
int32 self_path_length = readlink("/proc/self/exe", self_path, MAX_PATH - 1);
if (self_path_length == -1) {
return;
}
const char* temp = rel;
if (temp[0] == '.' && temp[1] == '/') {
temp += 2;
}
char* last = self_path + self_path_length;
while (*last != '/' && self_path_length > 0) {
--last;
--self_path_length;
}
++self_path_length;
memcpy(path, self_path, self_path_length);
strcpy(path + self_path_length, temp);
}
// @todo implement relative path support, similar to UtilsWin32
inline
uint64 file_size(const char* filename) {
struct stat buffer;
if (stat(filename, &buffer) != 0) {
return 0;
}
return buffer.st_size;
}
inline
uint64 last_modified(const char* filename)
{
struct stat buffer;
stat(filename, &buffer);
return (uint64) buffer.st_mtime;
}
inline
int32 get_append_handle(const char* path) {
int32 fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = open(full_path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
} else {
fp = open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
}
return fp;
}
inline
bool file_exists(const char* path) {
struct stat buffer;
const char* full_path = path;
char abs_path[MAX_PATH];
if (*path == '.') {
relative_to_absolute(path, abs_path);
full_path = abs_path;
}
return stat(full_path, &buffer) == 0;
}
inline
bool file_copy(const char* src, const char* dst) {
char src_full_path[MAX_PATH];
char dst_full_path[MAX_PATH];
if (*src == '.') {
relative_to_absolute(src, src_full_path);
src = src_full_path;
}
if (*dst == '.') {
relative_to_absolute(dst, dst_full_path);
dst = dst_full_path;
}
int32 src_fd = open(src, O_RDONLY);
if (src_fd < 0) {
return false;
}
int32 dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd < 0) {
close(src_fd);
return false;
}
char buffer[8192];
ssize_t bytes_read, bytes_written;
bool success = true;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
success = false;
break;
}
}
if (bytes_read < 0) {
success = false;
}
close(src_fd);
close(dst_fd);
return success;
}
inline
void file_read(const char* path, FileBody* file, RingMemory* ring) {
char full_path[MAX_PATH];
const char* abs_path = path;
if (*path == '.') {
relative_to_absolute(path, full_path);
abs_path = full_path;
}
int32 fp = open(abs_path, O_RDONLY);
if (fp < 0) {
file->size = 0;
file->content = NULL;
return;
}
struct stat file_stat;
if (fstat(fp, &file_stat) == -1) {
close(fp);
file->size = 0;
file->content = NULL;
return;
}
if (file_stat.st_size > MAX_INT32) {
close(fp);
file->size = 0;
file->content = NULL;
return;
}
if (ring != NULL) {
file->content = ring_get_memory(ring, file_stat.st_size);
}
ssize_t bytes_read = read(fp, file->content, file_stat.st_size);
if (bytes_read != file_stat.st_size) {
close(fp);
file->content = NULL;
file->size = 0;
return;
}
file->content[bytes_read] = '\0';
file->size = bytes_read;
close(fp);
}
inline
bool file_write(const char* path, const FileBody* file) {
int32 fd;
char full_path[PATH_MAX];
if (*path == '.') {
relative_to_absolute(path, full_path);
path = full_path;
}
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
return false;
}
ASSERT_SIMPLE(file->size < MAX_INT32);
ssize_t written = write(fd, file->content, file->size);
if (written < 0 || (size_t) written != file->size) {
close(fd);
return false;
}
if (close(fd) < 0) {
return false;
}
return true;
}
inline
void close_handle(int32 fp)
{
close(fp);
}
inline
void self_path(char* path) {
size_t len = readlink("/proc/self/exe", path, PATH_MAX);
if (len > 0) {
path[len] = '\0';
} else {
path[0] = '\0';
}
}
#endif

View File

@ -9,7 +9,7 @@
#ifndef TOS_PLATFORM_LINUX_THREADING_ATOMIC_H
#define TOS_PLATFORM_LINUX_THREADING_ATOMIC_H
#include <pthread.h>
#include <stdatomic.h>
#include "../../../stdlib/Types.h"
inline
@ -18,12 +18,30 @@ void atomic_set(volatile int32* value, int32 new_value)
__atomic_store_n(value, new_value, __ATOMIC_SEQ_CST);
}
inline
void atomic_get(volatile byte* value, byte data[16])
{
__atomic_store((volatile __int128 *) value, (__int128 *) data, __ATOMIC_SEQ_CST);
}
inline
int32 atomic_get(volatile int32* value)
{
return __atomic_load_n((int32 *) value, __ATOMIC_SEQ_CST);
}
inline
int64 atomic_get(volatile int64* value)
{
return __atomic_load_n((int64 *) value, __ATOMIC_SEQ_CST);
}
inline
void atomic_get(volatile byte* value, byte data[16])
{
__atomic_load((volatile __int128 *) value, (__int128 *) data, __ATOMIC_SEQ_CST);
}
inline
void atomic_increment(volatile int32* value) {
__atomic_fetch_add(value, 1, __ATOMIC_SEQ_CST);
@ -44,4 +62,20 @@ int32 atomic_subtract(volatile int32* value, int32 decrement) {
return __atomic_fetch_sub(value, decrement, __ATOMIC_SEQ_CST);
}
inline
int32 atomic_compare_exchange_weak(volatile int32* value, int32* expected, int32 desired) {
__atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return *expected;
}
inline
int32 atomic_fetch_add(volatile int32* value, int32 operand) {
return __atomic_fetch_add(value, operand, __ATOMIC_SEQ_CST);
}
inline
int32 atomic_fetch_sub(volatile int32* value, int32 operand) {
return __atomic_fetch_sub(value, operand, __ATOMIC_SEQ_CST);
}
#endif

View File

@ -9,7 +9,6 @@
#ifndef TOS_PLATFORM_LINUX_THREADING_SEMAPHORE_H
#define TOS_PLATFORM_LINUX_THREADING_SEMAPHORE_H
#include <pthread.h>
#include <semaphore.h>
#endif

View File

@ -9,7 +9,7 @@
#ifndef TOS_PLATFORM_LINUX_THREADING_SPINLOCK_H
#define TOS_PLATFORM_LINUX_THREADING_SPINLOCK_H
#include <pthread.h>
#include <stdatomic.h>
#include "../../../stdlib/Types.h"
typedef volatile int32 spinlock32;

View File

@ -9,11 +9,144 @@
#ifndef TOS_PLATFORM_LINUX_THREADING_THREAD_H
#define TOS_PLATFORM_LINUX_THREADING_THREAD_H
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include "../../../stdlib/Types.h"
#include "../Allocator.h"
#include "ThreadDefines.h"
uint32 pcthread_get_num_procs()
int32 pthread_create(pthread_t* thread, void *(*start_routine)(void *), void* arg) {
thread->stack = platform_alloc_aligned(1 * MEGABYTE, 64);
if (!thread->stack) {
return -1;
}
thread->stack = clone(
(int32 (*)(void *)) start_routine,
stack + 1 * MEGABYTE,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD,
arg
);
if (thread->stack == -1) {
platform_aligned_free(thread->stack);
return -1;
}
return 0;
}
int32 pthread_join(pthread_t thread, void** retval) {
int32 status;
if (waitpid(thread->id, &status, 0) == -1) {
platform_aligned_free(thread->stack);
return -1;
}
if (retval) {
*retval = (void *) status;
}
platform_aligned_free(thread->stack);
return 0;
}
int32 pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t*) {
atomic_set(mutex, 0);
return 0;
}
int32 pthread_mutex_lock(pthread_mutex_t* mutex) {
int32 expected = 0;
while (!atomic_compare_exchange_weak(mutex, &expected, 1)) {
syscall(SYS_futex, mutex, FUTEX_WAIT, 1, NULL, NULL, 0);
expected = 0;
}
return 0;
}
int32 pthread_mutex_unlock(pthread_mutex_t* mutex) {
atomic_set(mutex, 0);
syscall(SYS_futex, mutex, FUTEX_WAKE, 1, NULL, NULL, 0);
return 0;
}
int32 pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t*) {
atomic_set(cond, 0);
return 0;
}
int32 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
pthread_mutex_unlock(mutex);
syscall(SYS_futex, cond, FUTEX_WAIT, atomic_get(cond), NULL, NULL, 0);
pthread_mutex_lock(mutex);
return 0;
}
int32 pthread_cond_signal(pthread_cond_t* cond) {
atomic_fetch_add(cond, 1);
syscall(SYS_futex, cond, FUTEX_WAKE, 1, NULL, NULL, 0);
return 0;
}
int32 pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t*) {
atomic_set((int64 *) &rwlock->readers, 0);
//atomic_set(&rwlock->writer, 0);
return 0;
}
int32 pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
while (atomic_get(&rwlock->writer)) {}
atomic_fetch_add(&rwlock->readers, 1);
return 0;
}
int32 pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
while (!atomic_compare_exchange_weak(&rwlock->writer, 0, 1)) {}
return 0;
}
int32 pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
if (atomic_get(&rwlock->writer)) {
atomic_set(&rwlock->writer, 0);
} else {
atomic_fetch_sub(&rwlock->readers, 1);
}
return 0;
}
int32 pthread_detach(pthread_t) {
// For detached threads, the OS will clean up automatically. We do nothing here.
// Optionally, mark this thread as detached in your data structure if tracking threads.
return 0;
}
int32 pthread_rwlock_destroy(pthread_rwlock_t*)
{
return 0;
}
uint32 pthread_get_num_procs()
{
return (uint32) sysconf(_SC_NPROCESSORS_ONLN);
}

View File

@ -9,11 +9,39 @@
#ifndef TOS_PLATFORM_LINUX_THREADING_THREAD_DEFINES_H
#define TOS_PLATFORM_LINUX_THREADING_THREAD_DEFINES_H
#include <pthread.h>
#include <unistd.h>
// #include <pthread.h>
// #include <unistd.h>
#include "../../../stdlib/Types.h"
typedef void* (*ThreadJobFunc)(void*);
#define THREAD_RETURN void*
typedef volatile int32 pthread_mutex_t;
typedef volatile int32 pthread_cond_t;
struct pthread_t {
int32 id;
void* stack;
};
/*
struct pthread_mutex_t {
volatile int32 lock;
};
struct pthread_cond_t {
volatile int32 futex;
};
*/
struct pthread_rwlock_t {
volatile int32 readers;
volatile int32 writer;
};
typedef void pthread_mutexattr_t;
typedef void pthread_condattr_t;
typedef void pthread_rwlockattr_t;
#endif

View File

@ -0,0 +1,801 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_PLATFORM_WIN32_FILE_UTILS_C
#define TOS_PLATFORM_WIN32_FILE_UTILS_C
#include <stdio.h>
#include <windows.h>
#include <string.h>
#ifdef _MSC_VER
#include <io.h>
#endif
#include "../../stdlib/Types.h"
#include "../../utils/Utils.h"
#include "../../utils/TestUtils.h"
#include "../../memory/RingMemory.h"
typedef HANDLE FileHandler;
struct FileBodyAsync {
// doesn't include null termination (same as strlen)
uint64 size;
byte* content;
OVERLAPPED ov;
};
// @todo Consider to implement directly mapped files (CreateFileMapping) for certain files (e.g. map data or texture data, ...)
inline
void relative_to_absolute(const char* rel, char* path)
{
char self_path[MAX_PATH];
int32 self_path_length = GetModuleFileNameA(NULL, self_path, MAX_PATH);
if (self_path_length == 0) {
return;
}
const char* temp = rel;
if (temp[0] == '.' && temp[1] == '/') {
temp += 2;
}
char* last = self_path + self_path_length;
while (*last != '\\' && self_path_length > 0) {
--last;
--self_path_length;
}
++self_path_length;
memcpy(path, self_path, self_path_length);
strcpy(path + self_path_length, temp);
}
inline uint64
file_size(const char* path)
{
// @performance Profile against fseek strategy
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return 0;
}
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
}
CloseHandle(fp);
return size.QuadPart;
}
inline
bool file_exists(const char* path)
{
DWORD file_attr;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
file_attr = GetFileAttributesA(full_path);
} else {
file_attr = GetFileAttributesA(path);
}
return file_attr != INVALID_FILE_ATTRIBUTES;
}
inline void
file_read(const char* path, FileBody* file, RingMemory* ring = NULL)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
file->size = 0;
return;
}
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
file->content = NULL;
return;
}
if (ring != NULL) {
file->content = ring_get_memory(ring, size.QuadPart);
}
DWORD bytes;
ASSERT_SIMPLE(size.QuadPart < MAX_UINT32);
if (!ReadFile(fp, file->content, (uint32) size.QuadPart, &bytes, NULL)) {
CloseHandle(fp);
file->content = NULL;
return;
}
CloseHandle(fp);
file->content[bytes] = '\0';
file->size = size.QuadPart;
}
inline
void file_read(const char* path, FileBody* file, uint64 offset, uint64 length = MAX_UINT64, RingMemory* ring = NULL)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
file->size = 0;
return;
}
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
file->content = NULL;
return;
}
// Ensure the offset and length do not exceed the file size
uint64 file_size = size.QuadPart;
if (offset >= file_size) {
file->size = 0;
file->content = NULL;
CloseHandle(fp);
return;
}
// Adjust the length to read so that it does not exceed the file size
uint64 read_length = OMS_MIN(length, file_size - offset);
if (ring != NULL) {
file->content = ring_get_memory(ring, read_length);
}
// Move the file pointer to the offset position
LARGE_INTEGER li;
li.QuadPart = offset;
if (SetFilePointerEx(fp, li, NULL, FILE_BEGIN) == 0) {
CloseHandle(fp);
file->content = NULL;
return;
}
DWORD bytes;
ASSERT_SIMPLE(read_length < MAX_UINT32);
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) {
CloseHandle(fp);
file->content = NULL;
return;
}
CloseHandle(fp);
file->content[bytes] = '\0';
file->size = bytes;
}
inline
void file_read(FileHandler fp, FileBody* file, uint64 offset = 0, uint64 length = MAX_UINT64, RingMemory* ring = NULL)
{
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
file->content = NULL;
return;
}
// Ensure the offset and length do not exceed the file size
uint64 file_size = size.QuadPart;
if (offset >= file_size) {
file->size = 0;
file->content = NULL;
CloseHandle(fp);
return;
}
// Adjust the length to read so that it does not exceed the file size
uint64 read_length = OMS_MIN(length, file_size - offset);
if (ring != NULL) {
file->content = ring_get_memory(ring, read_length);
}
// Move the file pointer to the offset position
LARGE_INTEGER li;
li.QuadPart = offset;
if (SetFilePointerEx(fp, li, NULL, FILE_BEGIN) == 0) {
CloseHandle(fp);
file->content = NULL;
return;
}
DWORD bytes;
ASSERT_SIMPLE(read_length < MAX_UINT32);
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) {
CloseHandle(fp);
file->content = NULL;
return;
}
CloseHandle(fp);
file->content[bytes] = '\0';
file->size = bytes;
}
inline uint64
file_read_struct(const char* path, void* file, uint32 size)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return 0;
}
LARGE_INTEGER fsize;
if (!GetFileSizeEx(fp, &fsize)) {
CloseHandle(fp);
return 0;
}
DWORD read;
ASSERT_SIMPLE(fsize.QuadPart > size);
if (!ReadFile(fp, file, (uint32) size, &read, NULL)) {
CloseHandle(fp);
return 0;
}
CloseHandle(fp);
return read;
}
inline bool
file_write(const char* path, const FileBody* file)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) file->size;
ASSERT_SIMPLE(file->size < MAX_UINT32);
if (!WriteFile(fp, file->content, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_write_struct(const char* path, const void* file, uint32 size)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
DWORD written;
ASSERT_SIMPLE(size < MAX_UINT32);
if (!WriteFile(fp, file, size, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline void
file_copy(const char* src, const char* dst)
{
if (*src == '.') {
char src_full_path[MAX_PATH];
relative_to_absolute(src, src_full_path);
if (*dst == '.') {
char dst_full_path[MAX_PATH];
relative_to_absolute(dst, dst_full_path);
CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst_full_path, false);
} else {
CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst, false);
}
} else if (*dst == '.') {
char dst_full_path[MAX_PATH];
relative_to_absolute(dst, dst_full_path);
CopyFileA((LPCSTR) src, (LPCSTR) dst_full_path, false);
} else {
CopyFileA((LPCSTR) src, (LPCSTR) dst, false);
}
}
inline
void close_handle(FileHandler fp)
{
CloseHandle(fp);
}
inline
HANDLE file_append_handle(const char* path)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return NULL;
}
return fp;
}
inline
bool file_read_async(
FileHandler fp,
FileBodyAsync* file,
uint64_t offset = 0,
uint64_t length = MAXUINT64,
RingMemory* ring = NULL
) {
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
file->content = NULL;
return false;
}
// Ensure the offset and length do not exceed the file size
uint64_t file_size = size.QuadPart;
if (offset >= file_size) {
file->size = 0;
file->content = NULL;
CloseHandle(fp);
return false;
}
// Adjust the length to read so that it does not exceed the file size
uint64 read_length = OMS_MIN(length, file_size - offset);
// Allocate memory for the content
if (ring != NULL) {
file->content = ring_get_memory(ring, read_length);
}
if (!file->content) {
CloseHandle(fp);
return false;
}
file->ov.Offset = (DWORD)(offset & 0xFFFFFFFF);
file->ov.OffsetHigh = (DWORD)(offset >> 32);
// Auto-reset event
file->ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD bytes_read = 0;
ASSERT_SIMPLE(read_length < MAXDWORD);
if (!ReadFile(fp, file->content, (DWORD) read_length, &bytes_read, &file->ov)) {
DWORD error = GetLastError();
if (error != ERROR_IO_PENDING) {
CloseHandle(fp);
free(file->content);
file->content = NULL;
CloseHandle(&file->ov.hEvent);
return false;
}
}
file->size = read_length;
return true;
}
inline
FileHandler file_read_handle(const char* path)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return NULL;
}
return fp;
}
inline
FileHandler file_read_async_handle(const char* path)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return NULL;
}
return fp;
}
bool file_append(const char* path, const char* file)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data?
ASSERT_SIMPLE(length < MAX_UINT32);
if (!WriteFile(fp, file, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_append(FileHandler fp, const char* file)
{
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data?
ASSERT_SIMPLE(length < MAX_UINT32);
if (!WriteFile(fp, file, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_append(FileHandler fp, const char* file, size_t length)
{
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
if (!WriteFile(fp, file, (uint32) length, &written, NULL)) {
CloseHandle(fp);
return false;
}
return true;
}
inline bool
file_append(const char* path, const FileBody* file)
{
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD bytes;
DWORD length = (DWORD) file->size;
ASSERT_SIMPLE(file->size < MAX_UINT32);
if (!WriteFile(fp, file->content, length, &bytes, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline
uint64 file_last_modified(const char* path)
{
WIN32_FIND_DATA find_data;
FileHandler fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = FindFirstFileA(full_path, (LPWIN32_FIND_DATAA) &find_data);
} else {
fp = FindFirstFileA(path, (LPWIN32_FIND_DATAA) &find_data);
}
FILETIME modified = {};
if(fp != INVALID_HANDLE_VALUE) {
modified = find_data.ftLastWriteTime;
FindClose(fp);
}
ULARGE_INTEGER ull;
ull.LowPart = modified.dwLowDateTime;
ull.HighPart = modified.dwHighDateTime;
return ull.QuadPart;
}
inline void self_path(char* path)
{
GetModuleFileNameA(NULL, (LPSTR) path, MAX_PATH);
}
#endif

View File

@ -14,7 +14,7 @@
#include <string.h>
#include "../../stdlib/Types.h"
#include "UtilsWin32.h"
#include "FileUtils.cpp"
#include "../../utils/StringUtils.h"
#include "../Library.h"
@ -41,7 +41,7 @@ bool library_load(Library* lib)
memcpy(dst + dst_len - (sizeof(".dll") - 1), "_temp", sizeof(".temp") - 1);
memcpy(dst + dst_len - (sizeof(".dll") - 1) + (sizeof(".temp") - 1), ".dll", sizeof(".dll"));
lib->last_load = last_modified(src);
lib->last_load = file_last_modified(src);
file_copy(src, dst);
#endif

View File

@ -0,0 +1,91 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_PLATFORM_WIN32_TIME_UTILS_H
#define TOS_PLATFORM_WIN32_TIME_UTILS_H
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include "../../log/Debug.cpp"
void usleep(uint64 microseconds)
{
if ((microseconds % 1000) == 0) {
Sleep((DWORD) (microseconds / 1000));
return;
}
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
LARGE_INTEGER start, end;
QueryPerformanceCounter(&start);
long long target = start.QuadPart + (microseconds * frequency.QuadPart) / 1000000;
do {
QueryPerformanceCounter(&end);
} while (end.QuadPart < target);
}
inline
time_t system_time()
{
SYSTEMTIME systemTime;
FILETIME fileTime;
ULARGE_INTEGER largeInt;
GetLocalTime(&systemTime);
SystemTimeToFileTime(&systemTime, &fileTime);
// Convert FILETIME to a 64-bit integer
largeInt.LowPart = fileTime.dwLowDateTime;
largeInt.HighPart = fileTime.dwHighDateTime;
return ((time_t) (largeInt.QuadPart / 10000000ULL)) - ((time_t) 11644473600ULL);
}
// doesn't return clock time, only to return time since program start
// -> can be used for profiling
inline
uint64 time_mu()
{
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (counter.QuadPart * 1000000) / debug_container->performance_count_frequency;
}
inline
time_t unix_epoch_s()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
ULARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
time_t seconds_since_epoch = (li.QuadPart - 116444736000000000ULL) / 10000000ULL;
return seconds_since_epoch;
}
static DWORD timespec_to_ms(const timespec* abstime)
{
if (abstime == NULL) {
return INFINITE;
}
time_t seconds_since_epoch = unix_epoch_s();
DWORD t = (DWORD) (((abstime->tv_sec - seconds_since_epoch) * 1000) + (abstime->tv_nsec / 1000000));
return t < 0 ? 1 : t;
}
#endif

View File

@ -12,580 +12,7 @@
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <time.h>
#ifdef _MSC_VER
#include <io.h>
#endif
#include "../../stdlib/Types.h"
#include "../../utils/Utils.h"
#include "../../utils/TestUtils.h"
#include "../../memory/RingMemory.h"
#include "../../log/Debug.cpp"
#define strtok_r strtok_s
void usleep(uint64 microseconds)
{
if ((microseconds % 1000) == 0) {
Sleep((DWORD) (microseconds / 1000));
return;
}
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
LARGE_INTEGER start, end;
QueryPerformanceCounter(&start);
long long target = start.QuadPart + (microseconds * frequency.QuadPart) / 1000000;
do {
QueryPerformanceCounter(&end);
} while (end.QuadPart < target);
}
inline
time_t system_time()
{
SYSTEMTIME systemTime;
FILETIME fileTime;
ULARGE_INTEGER largeInt;
GetLocalTime(&systemTime);
SystemTimeToFileTime(&systemTime, &fileTime);
// Convert FILETIME to a 64-bit integer
largeInt.LowPart = fileTime.dwLowDateTime;
largeInt.HighPart = fileTime.dwHighDateTime;
return ((time_t) (largeInt.QuadPart / 10000000ULL)) - ((time_t) 11644473600ULL);
}
// doesn't return clock time, only to return time since program start
// -> can be used for profiling
inline
uint64 time_mu()
{
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (counter.QuadPart * 1000000) / debug_container->performance_count_frequency;
}
inline
time_t unix_epoch_s()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
ULARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
time_t seconds_since_epoch = (li.QuadPart - 116444736000000000ULL) / 10000000ULL;
return seconds_since_epoch;
}
// @todo Consider to implement directly mapped files (CreateFileMapping) for certain files (e.g. map data or texture data, ...)
inline
void relative_to_absolute(const char* rel, char* path)
{
char self_path[MAX_PATH];
int32 self_path_length = GetModuleFileNameA(NULL, self_path, MAX_PATH);
if (self_path_length == 0) {
return;
}
const char* temp = rel;
if (temp[0] == '.' && temp[1] == '/') {
temp += 2;
}
char* last = self_path + self_path_length;
while (*last != '\\' && self_path_length > 0) {
--last;
--self_path_length;
}
++self_path_length;
memcpy(path, self_path, self_path_length);
strcpy(path + self_path_length, temp);
}
// @todo Move file code to FileUtils.h
inline uint64
file_size(const char* path)
{
// @performance Profile against fseek strategy
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return 0;
}
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
}
CloseHandle(fp);
return size.QuadPart;
}
inline
bool file_exists(const char* path)
{
DWORD file_attr;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
file_attr = GetFileAttributesA(full_path);
} else {
file_attr = GetFileAttributesA(path);
}
return file_attr != INVALID_FILE_ATTRIBUTES;
}
inline void
file_read(const char* path, FileBody* file, RingMemory* ring = NULL)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
file->size = 0;
return;
}
LARGE_INTEGER size;
if (!GetFileSizeEx(fp, &size)) {
CloseHandle(fp);
file->content = NULL;
return;
}
if (ring != NULL) {
file->content = ring_get_memory(ring, size.QuadPart);
}
DWORD bytes;
ASSERT_SIMPLE(size.QuadPart < MAX_INT32);
if (!ReadFile(fp, file->content, (uint32) size.QuadPart, &bytes, NULL)) {
CloseHandle(fp);
file->content = NULL;
return;
}
CloseHandle(fp);
file->content[bytes] = '\0';
file->size = size.QuadPart;
}
inline uint64
file_read_struct(const char* path, void* file, uint32 size)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return 0;
}
LARGE_INTEGER fsize;
if (!GetFileSizeEx(fp, &fsize)) {
CloseHandle(fp);
return 0;
}
DWORD read;
ASSERT_SIMPLE(fsize.QuadPart > size);
if (!ReadFile(fp, file, (uint32) size, &read, NULL)) {
CloseHandle(fp);
return 0;
}
CloseHandle(fp);
return read;
}
inline bool
file_write(const char* path, const FileBody* file)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) file->size;
ASSERT_SIMPLE(file->size < MAX_INT32);
if (!WriteFile(fp, file->content, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_write_struct(const char* path, const void* file, uint32 size)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
DWORD written;
ASSERT_SIMPLE(size < MAX_INT32);
if (!WriteFile(fp, file, size, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline void
file_copy(const char* src, const char* dst)
{
if (*src == '.') {
char src_full_path[MAX_PATH];
relative_to_absolute(src, src_full_path);
if (*dst == '.') {
char dst_full_path[MAX_PATH];
relative_to_absolute(dst, dst_full_path);
CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst_full_path, false);
} else {
CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst, false);
}
} else if (*dst == '.') {
char dst_full_path[MAX_PATH];
relative_to_absolute(dst, dst_full_path);
CopyFileA((LPCSTR) src, (LPCSTR) dst_full_path, false);
} else {
CopyFileA((LPCSTR) src, (LPCSTR) dst, false);
}
}
inline
void close_handle(HANDLE fp)
{
CloseHandle(fp);
}
inline
HANDLE get_append_handle(const char* path)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return NULL;
}
return fp;
}
bool file_append(const char* path, const char* file)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data?
ASSERT_SIMPLE(length < MAX_INT32);
if (!WriteFile(fp, file, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_append(HANDLE fp, const char* file)
{
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data?
ASSERT_SIMPLE(length < MAX_INT32);
if (!WriteFile(fp, file, length, &written, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline bool
file_append(HANDLE fp, const char* file, size_t length)
{
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written;
if (!WriteFile(fp, file, (uint32) length, &written, NULL)) {
CloseHandle(fp);
return false;
}
return true;
}
inline bool
file_append(const char* path, const FileBody* file)
{
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = CreateFileA((LPCSTR) full_path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
fp = CreateFileA((LPCSTR) path,
FILE_APPEND_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (fp == INVALID_HANDLE_VALUE) {
return false;
}
DWORD bytes;
DWORD length = (DWORD) file->size;
ASSERT_SIMPLE(file->size < MAX_INT32);
if (!WriteFile(fp, file->content, length, &bytes, NULL)) {
CloseHandle(fp);
return false;
}
CloseHandle(fp);
return true;
}
inline
uint64 last_modified(const char* path)
{
WIN32_FIND_DATA find_data;
HANDLE fp;
if (*path == '.') {
char full_path[MAX_PATH];
relative_to_absolute(path, full_path);
fp = FindFirstFileA(full_path, (LPWIN32_FIND_DATAA) &find_data);
} else {
fp = FindFirstFileA(path, (LPWIN32_FIND_DATAA) &find_data);
}
FILETIME modified = {};
if(fp != INVALID_HANDLE_VALUE) {
modified = find_data.ftLastWriteTime;
FindClose(fp);
}
ULARGE_INTEGER ull;
ull.LowPart = modified.dwLowDateTime;
ull.HighPart = modified.dwHighDateTime;
return ull.QuadPart;
}
inline void self_path(char* path)
{
GetModuleFileNameA(NULL, (LPSTR) path, MAX_PATH);
}
#endif

View File

@ -106,7 +106,6 @@ void audio_play(AudioSetting* setting, DirectSoundSetting* api_setting)
}
api_setting->secondary_buffer->Play(0, 0, DSBPLAY_LOOPING);
setting->is_playing = true;
}
inline
@ -116,7 +115,6 @@ void audio_stop(AudioSetting* setting, DirectSoundSetting* api_setting) {
}
api_setting->secondary_buffer->Stop();
setting->is_playing = false;
}
inline
@ -155,7 +153,8 @@ uint32 audio_buffer_fillable(const AudioSetting* setting, const DirectSoundSetti
DWORD target_cursor = (player_cursor + (setting->latency * setting->sample_size)) % setting->buffer_size;
if (bytes_to_lock == player_cursor) {
bytes_to_write = setting->is_playing ? 0 : setting->buffer_size;
// @bug What if just started?
bytes_to_write = 0;
} else if (bytes_to_lock > target_cursor) {
bytes_to_write = setting->buffer_size - bytes_to_lock;
bytes_to_write += target_cursor;
@ -173,10 +172,6 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting)
return;
}
if (!setting->is_playing) {
audio_play(setting, api_setting);
}
void *region1;
DWORD region1_size;
@ -192,9 +187,6 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting)
0
);
// @performance why are we copying again, we already created our buffer, now we have to copy it again?!
// Ideally we should have already copied it into the correct final one, no?
// We should probably provide a audio_buffer_fill function, that does this -> we could remove one whole memcopy
memcpy(
(void *) region1,
(void *) setting->buffer,
@ -215,43 +207,4 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting)
setting->sample_buffer_size = 0;
}
// Basically the same as audio_play_buffer but by using this we can avoid one copy
// The only reason we have audio_play_buffer is that there might be situations where this is not possible
inline
void audio_fill_play_buffer(AudioSetting* setting, uint32 to_fill, Audio* sound, DirectSoundSetting* api_setting)
{
setting->sample_buffer_size = to_fill;
if (setting->sample_buffer_size == 0) {
return;
}
if (!setting->is_playing) {
audio_play(setting, api_setting);
}
void *region1;
DWORD region1_size;
void *region2;
DWORD region2_size;
DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size;
api_setting->secondary_buffer->Lock(
bytes_to_lock, setting->sample_buffer_size,
&region1, &region1_size,
&region2, &region2_size,
0
);
audio_fill_buffer(setting, to_fill, sound, (int16 *) region1, (int32) region1_size, (int16 *) region2, (int32) region2_size);
api_setting->secondary_buffer->Unlock(region1, region1_size, region2, region2_size);
setting->sample_index += setting->sample_buffer_size / setting->sample_size;
setting->sample_buffer_size = 0;
}
#endif

View File

@ -117,7 +117,6 @@ void audio_play(AudioSetting* setting, XAudio2Setting* api_setting) {
}
api_setting->source_voice->Start(0, XAUDIO2_COMMIT_NOW);
setting->is_playing = true;
if (setting->sample_index > 1) {
setting->sample_index = 0;
@ -131,7 +130,6 @@ void audio_stop(AudioSetting* setting, XAudio2Setting* api_setting) {
}
api_setting->source_voice->Stop(0, XAUDIO2_COMMIT_NOW);
setting->is_playing = false;
}
inline
@ -156,10 +154,6 @@ void audio_free(AudioSetting* setting, XAudio2Setting* api_setting)
if (api_setting->internal_buffer[1].pAudioData) {
free((void *) api_setting->internal_buffer[1].pAudioData);
}
if (setting->buffer) {
free((void *) setting->buffer);
}
}
/**
@ -190,15 +184,8 @@ void audio_play_buffer(AudioSetting* setting, XAudio2Setting* api_setting) {
return;
}
if (!setting->is_playing) {
audio_play(setting, api_setting);
}
uint32 idx = setting->sample_output % 2;
// @performance why are we copying again, we already created our buffer, now we have to copy it again?!
// Ideally we should have already copied it into the correct final one, no?
// We should probably provide a audio_buffer_fill function, that does this -> we could remove one whole memcopy
memcpy(
(void *) api_setting->internal_buffer[idx].pAudioData,
setting->buffer,
@ -211,35 +198,7 @@ void audio_play_buffer(AudioSetting* setting, XAudio2Setting* api_setting) {
return;
}
setting->sample_index += setting->sample_buffer_size / setting->sample_size;
setting->sample_buffer_size = 0;
}
// Basically the same as audio_play_buffer but by using this we can avoid one copy
// The only reason we have audio_play_buffer is that there might be situations where this is not possible
inline
void audio_fill_play_buffer(AudioSetting* setting, uint32 to_fill, Audio* sound, XAudio2Setting* api_setting)
{
setting->sample_buffer_size = to_fill;
if (!api_setting->source_voice || setting->sample_buffer_size == 0) {
return;
}
if (!setting->is_playing) {
audio_play(setting, api_setting);
}
uint32 idx = setting->sample_output % 2;
audio_fill_buffer(setting, to_fill, sound, (int16 *) api_setting->internal_buffer[idx].pAudioData, to_fill);
if (!SUCCEEDED(api_setting->source_voice->SubmitSourceBuffer(&api_setting->internal_buffer[idx]))) {
LOG("Xaudio2: SubmitSourceBuffer failed\n", true, true);
return;
}
++setting->sample_output;
setting->sample_index += setting->sample_buffer_size / setting->sample_size;
setting->sample_buffer_size = 0;
}

View File

@ -18,12 +18,45 @@ void atomic_set(volatile int32* value, int32 new_value)
InterlockedExchange((long *) value, new_value);
}
inline
void atomic_set(volatile byte* value, const byte new_value[16])
{
int64* value64 = (int64*) value;
const int64* new_value64 = (const int64*) new_value;
int64 expected_low, expected_high;
do {
expected_low = value64[0];
expected_high = value64[1];
} while (
!InterlockedCompareExchange128(
(volatile long long*) value,
new_value64[1],
new_value64[0],
&expected_low
)
);
}
inline
int32 atomic_get(volatile int32* value)
{
return (int32) InterlockedCompareExchange((long *) value, 0, 0);
}
inline
int64 atomic_get(volatile int64* value)
{
return (int64) InterlockedCompareExchange((long *) value, 0, 0);
}
inline
void atomic_get(volatile byte* value, byte data[16])
{
InterlockedCompareExchange128((volatile long long *) value, 0, 0, (long long *) data);
}
inline
void atomic_increment(volatile int32* value) {
InterlockedIncrement((long *) value);
@ -44,4 +77,19 @@ int32 atomic_subtract(volatile int32* value, int32 decrement) {
return InterlockedExchangeAdd((long *) value, -decrement);
}
inline
int32 atomic_compare_exchange_weak(volatile int32* value, int32* expected, int32 desired) {
return (int32) InterlockedCompareExchange((long *) value, desired, *expected);
}
inline
int32 atomic_fetch_add(volatile int32* value, int32 operand) {
return (int32) InterlockedExchangeAdd((long *) value, operand);
}
inline
int32 atomic_fetch_sub(volatile int32* value, int32 operand) {
return (int32) InterlockedExchangeSubtract((unsigned long *) value, operand);
}
#endif

View File

@ -16,7 +16,7 @@ typedef HANDLE sem_t;
void sem_init(sem_t* semaphore, int32 value)
{
*semaphore = CreateSemaphore(NULL, value, MAX_INT32, NULL);
*semaphore = CreateSemaphore(NULL, value, MAX_UINT32, NULL);
}
void sem_destroy(sem_t* semaphore)

View File

@ -10,7 +10,7 @@
#define TOS_PLATFORM_WIN32_THREADING_THREAD_H
#include "../../../stdlib/Types.h"
#include "../UtilsWin32.h"
#include "../TimeUtils.h"
#include "ThreadDefines.h"
#include <windows.h>
@ -105,18 +105,6 @@ int32 pthread_cond_destroy(pthread_cond_t*)
return 0;
}
static DWORD timespec_to_ms(const timespec* abstime)
{
if (abstime == NULL) {
return INFINITE;
}
time_t seconds_since_epoch = unix_epoch_s();
DWORD t = (DWORD) (((abstime->tv_sec - seconds_since_epoch) * 1000) + (abstime->tv_nsec / 1000000));
return t < 0 ? 1 : t;
}
int32 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime)
{
if (cond == NULL || mutex == NULL) {

View File

@ -12,6 +12,7 @@
#include <stdio.h>
#include <windows.h>
#define THREAD_RETURN DWORD WINAPI
typedef DWORD (WINAPI *ThreadJobFunc)(void*);
typedef CRITICAL_SECTION pthread_mutex_t;
typedef void pthread_mutexattr_t;
@ -25,6 +26,4 @@ struct pthread_rwlock_t {
bool exclusive;
};
#define THREAD_RETURN DWORD WINAPI
#endif

View File

@ -6,8 +6,8 @@
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_STDLIB_HASHMAP_H
#define TOS_STDLIB_HASHMAP_H
#ifndef TOS_STDLIB_HASH_MAP_H
#define TOS_STDLIB_HASH_MAP_H
#include "Types.h"
#include "../hash/GeneralHash.h"
@ -122,11 +122,6 @@ int64 hashmap_size(const HashMap* hm)
}
void hashmap_insert(HashMap* hm, const char* key, int32 value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -152,11 +147,6 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
}
void hashmap_insert(HashMap* hm, const char* key, int64 value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -182,11 +172,6 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
}
void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -212,11 +197,6 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
}
void hashmap_insert(HashMap* hm, const char* key, void* value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -242,11 +222,6 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
}
void hashmap_insert(HashMap* hm, const char* key, f32 value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -272,11 +247,6 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
}
void hashmap_insert(HashMap* hm, const char* key, const char* value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -304,11 +274,6 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
}
void hashmap_insert(HashMap* hm, const char* key, byte* value) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return;
}
uint64 index = hash_djb2(key) % hm->buf.count;
int64 element = chunk_reserve(&hm->buf, 1);
@ -337,11 +302,6 @@ void hashmap_insert(HashMap* hm, const char* key, byte* value) {
}
HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
// @performance Do we really want to do this check every time?
if (hm->buf.count == 0) {
return NULL;
}
uint64 index = hash_djb2(key) % hm->buf.count;
HashEntry* entry = (HashEntry *) hm->table[index];
@ -359,10 +319,6 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
// This function only saves one step (omission of the hash function)
// The reason for this is in some cases we can use compile time hashing
HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 index) {
if (hm->buf.count == 0) {
return NULL;
}
index %= hm->buf.count;
HashEntry* entry = (HashEntry *) hm->table[index];
@ -378,7 +334,7 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 index) {
}
void hashmap_delete_entry(HashMap* hm, const char* key) {
uint64 index = hash_djb2(key);
uint64 index = hash_djb2(key) % hm->buf.count;
HashEntry* entry = (HashEntry *) hm->table[index];
HashEntry* prev = NULL;

134
stdlib/ThreadedHashMap.h Normal file
View File

@ -0,0 +1,134 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_STDLIB_THREADED_HASH_MAP_H
#define TOS_STDLIB_THREADED_HASH_MAP_H
#include "../stdlib/Types.h"
#include "HashMap.h"
#if _WIN32
#include "../platform/win32/threading/Thread.h"
#include "../platform/win32/threading/Semaphore.h"
#include "../platform/win32/threading/Atomic.h"
#elif __linux__
#include "../platform/linux/threading/Thread.h"
#include "../platform/linux/threading/Semaphore.h"
#include "../platform/linux/threading/Atomic.h"
#endif
struct ThreadedHashMap {
void** table;
ChunkMemory buf;
pthread_mutex_t mutex;
};
// WARNING: element_size = element size + remaining HashEntry data size
inline
void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, RingMemory* ring)
{
hashmap_create((HashMap *) hm, count, element_size, ring);
pthread_mutex_init(&hm->mutex, NULL);
}
// WARNING: element_size = element size + remaining HashEntry data size
inline
void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, BufferMemory* buf)
{
hashmap_create((HashMap *) hm, count, element_size, buf);
pthread_mutex_init(&hm->mutex, NULL);
}
// WARNING: element_size = element size + remaining HashEntry data size
inline
void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, byte* buf)
{
hashmap_create((HashMap *) hm, count, element_size, buf);
pthread_mutex_init(&hm->mutex, NULL);
}
inline
void threaded_hashmap_free(ThreadedHashMap* hm)
{
pthread_mutex_destroy(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, int32 value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, int64 value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, uintptr_t value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, void* value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, f32 value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, const char* value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, byte* value) {
pthread_mutex_lock(&hm->mutex);
hashmap_insert((HashMap *) hm, key, value);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_get_entry(ThreadedHashMap* hm, HashEntry* entry, const char* key) {
pthread_mutex_lock(&hm->mutex);
HashEntry* temp = hashmap_get_entry((HashMap *) hm, key);
memcpy(entry, temp, hm->buf.chunk_size);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_get_entry(ThreadedHashMap* hm, HashEntry* entry, const char* key, uint64 index) {
pthread_mutex_lock(&hm->mutex);
HashEntry* temp = hashmap_get_entry((HashMap *) hm, key, index);
memcpy(entry, temp, hm->buf.chunk_size);
pthread_mutex_unlock(&hm->mutex);
}
inline
void threaded_hashmap_delete_entry(ThreadedHashMap* hm, const char* key) {
pthread_mutex_lock(&hm->mutex);
hashmap_delete_entry((HashMap *) hm, key);
pthread_mutex_unlock(&hm->mutex);
}
#endif

View File

@ -47,8 +47,21 @@ typedef intptr_t smm;
#define GIGABYTE 1073741824
#define MAX_BYTE 0xFF
#define MAX_INT16 0xFFFF
#define MAX_INT32 0xFFFFFFFF
#define MAX_UINT16 0xFFFF
#define MAX_UINT32 0xFFFFFFFF
#define MAX_UINT64 0xFFFFFFFFFFFFFFFF
#define MAX_CHAR 0x7F
#define MAX_INT16 0x7FFF
#define MAX_INT32 0x7FFFFFFF
#define MAX_INT64 0x7FFFFFFFFFFFFFFF
#define MIN_CHAR 0x80
#define MIN_INT16 0x8000
#define MIN_INT32 0x80000000
#define MIN_INT64 0x8000000000000000
#define MILLI_MICRO 1000
#define internal static // only allows local "file" access
#define local_persist static

View File

@ -12,9 +12,9 @@
#include "UIElementType.h"
#if _WIN32
#include "../platform/win32/UtilsWin32.h"
#include "../platform/win32/FileUtils.cpp"
#else
#include "../platform/linux/UtilsLinux.h"
#include "../platform/linux/FileUtils.cpp"
#endif
#define UI_THEME_VERSION 1

View File

@ -15,7 +15,7 @@
#define OMS_PI 3.14159265358979323846f
#define OMS_PI_OVER_TWO (OMS_PI / 2.0f)
#define OMS_PI_OVER_FOUR (OMS_PI / 4.0f)
#define OMS_TWO_PI (2 * OMS_PI)
#define OMS_TWO_PI (2.0f * OMS_PI)
#define OMS_MAX(a, b) ((a) > (b) ? (a) : (b))
#define OMS_MIN(a, b) ((a) > (b) ? (b) : (a))

View File

@ -14,7 +14,7 @@
#include "../stdlib/Types.h"
struct FileBody {
uint64 size = 0; // doesn't include null termination (same as strlen)
uint64 size; // doesn't include null termination (same as strlen)
byte* content;
};
@ -84,4 +84,28 @@ int random_weighted_index(const int32* arr, int32 array_count)
return item_rarity;
}
bool is_equal_aligned(const byte* region1, const byte* region2, uint64 size)
{
while (size > 4) {
if (*(const int32_t*) region1 != *(const int32_t*) region2) {
return false;
}
region1 += 4;
region2 += 4;
size -= 4;
}
for (; size > 0; --size) {
if (region1 != region2) {
return false;
}
++region1;
++region2;
}
return true;
}
#endif