mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
started to implement audio mixer, game working but no audio yet
This commit is contained in:
parent
2943d418e2
commit
faf5f98914
198
asset/AssetArchive.h
Normal file
198
asset/AssetArchive.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#define TOS_ASSET_TYPE_H
|
||||
|
||||
enum AssetType {
|
||||
ASSET_TYPE_GENERAL,
|
||||
ASSET_TYPE_OBJ,
|
||||
ASSET_TYPE_TEXTURE,
|
||||
ASSET_TYPE_AUDIO,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
347
audio/AudioMixer.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
259
platform/linux/FileUtils.cpp
Normal file
259
platform/linux/FileUtils.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
801
platform/win32/FileUtils.cpp
Normal file
801
platform/win32/FileUtils.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
91
platform/win32/TimeUtils.h
Normal file
91
platform/win32/TimeUtils.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
®ion1, ®ion1_size,
|
||||
®ion2, ®ion2_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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
134
stdlib/ThreadedHashMap.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue
Block a user