mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-22 16:25:39 +00:00
192 lines
6 KiB
C
192 lines
6 KiB
C
#include "rc_util.h"
|
|
|
|
#include "rc_compat.h"
|
|
#include "rc_error.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#undef DEBUG_BUFFERS
|
|
|
|
/* --- rc_buffer --- */
|
|
|
|
void rc_buffer_init(rc_buffer_t* buffer)
|
|
{
|
|
buffer->chunk.write = buffer->chunk.start = &buffer->data[0];
|
|
buffer->chunk.end = &buffer->data[sizeof(buffer->data)];
|
|
buffer->chunk.next = NULL;
|
|
/* leave buffer->data uninitialized */
|
|
}
|
|
|
|
void rc_buffer_destroy(rc_buffer_t* buffer)
|
|
{
|
|
rc_buffer_chunk_t* chunk;
|
|
#ifdef DEBUG_BUFFERS
|
|
int count = 0;
|
|
int wasted = 0;
|
|
int total = 0;
|
|
#endif
|
|
|
|
/* first chunk is not allocated. skip it. */
|
|
chunk = buffer->chunk.next;
|
|
|
|
/* deallocate any additional buffers */
|
|
while (chunk)
|
|
{
|
|
rc_buffer_chunk_t* next = chunk->next;
|
|
#ifdef DEBUG_BUFFERS
|
|
total += (int)(chunk->end - chunk->start);
|
|
wasted += (int)(chunk->end - chunk->write);
|
|
++count;
|
|
#endif
|
|
free(chunk);
|
|
chunk = next;
|
|
}
|
|
|
|
#ifdef DEBUG_BUFFERS
|
|
printf("-- %d allocated buffers (%d/%d used, %d wasted, %0.2f%% efficiency)\n", count,
|
|
total - wasted, total, wasted, (float)(100.0 - (wasted * 100.0) / total));
|
|
#endif
|
|
}
|
|
|
|
uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount)
|
|
{
|
|
rc_buffer_chunk_t* chunk = &buffer->chunk;
|
|
size_t remaining;
|
|
while (chunk)
|
|
{
|
|
remaining = chunk->end - chunk->write;
|
|
if (remaining >= amount)
|
|
return chunk->write;
|
|
|
|
if (!chunk->next)
|
|
{
|
|
/* allocate a chunk of memory that is a multiple of 256-bytes. the first 32 bytes will be associated
|
|
* to the chunk header, and the remaining will be used for data.
|
|
*/
|
|
const size_t chunk_header_size = sizeof(rc_buffer_chunk_t);
|
|
const size_t alloc_size = (chunk_header_size + amount + 0xFF) & ~0xFF;
|
|
chunk->next = (rc_buffer_chunk_t*)malloc(alloc_size);
|
|
if (!chunk->next)
|
|
break;
|
|
|
|
chunk->next->start = (uint8_t*)chunk->next + chunk_header_size;
|
|
chunk->next->write = chunk->next->start;
|
|
chunk->next->end = (uint8_t*)chunk->next + alloc_size;
|
|
chunk->next->next = NULL;
|
|
}
|
|
|
|
chunk = chunk->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void rc_buffer_consume(rc_buffer_t* buffer, const uint8_t* start, uint8_t* end)
|
|
{
|
|
rc_buffer_chunk_t* chunk = &buffer->chunk;
|
|
do
|
|
{
|
|
if (chunk->write == start)
|
|
{
|
|
size_t offset = (end - chunk->start);
|
|
offset = (offset + 7) & ~7;
|
|
chunk->write = &chunk->start[offset];
|
|
|
|
if (chunk->write > chunk->end)
|
|
chunk->write = chunk->end;
|
|
break;
|
|
}
|
|
|
|
chunk = chunk->next;
|
|
} while (chunk);
|
|
}
|
|
|
|
void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount)
|
|
{
|
|
uint8_t* ptr = rc_buffer_reserve(buffer, amount);
|
|
rc_buffer_consume(buffer, ptr, ptr + amount);
|
|
return (void*)ptr;
|
|
}
|
|
|
|
char* rc_buffer_strncpy(rc_buffer_t* buffer, const char* src, size_t len)
|
|
{
|
|
uint8_t* dst = rc_buffer_reserve(buffer, len + 1);
|
|
memcpy(dst, src, len);
|
|
dst[len] = '\0';
|
|
rc_buffer_consume(buffer, dst, dst + len + 2);
|
|
return (char*)dst;
|
|
}
|
|
|
|
char* rc_buffer_strcpy(rc_buffer_t* buffer, const char* src)
|
|
{
|
|
return rc_buffer_strncpy(buffer, src, strlen(src));
|
|
}
|
|
|
|
/* --- other --- */
|
|
|
|
void rc_format_md5(char checksum[33], const uint8_t digest[16])
|
|
{
|
|
snprintf(checksum, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
|
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
|
);
|
|
}
|
|
|
|
uint32_t rc_djb2(const char* input)
|
|
{
|
|
uint32_t result = 5381;
|
|
char c;
|
|
|
|
while ((c = *input++) != '\0')
|
|
result = ((result << 5) + result) + c; /* result = result * 33 + c */
|
|
|
|
return result;
|
|
}
|
|
|
|
const char* rc_error_str(int ret)
|
|
{
|
|
switch (ret) {
|
|
case RC_OK: return "OK";
|
|
case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
|
|
case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
|
|
case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
|
|
case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
|
|
case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
|
|
case RC_INVALID_OPERATOR: return "Invalid operator";
|
|
case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
|
|
case RC_DUPLICATED_START: return "Duplicated start condition";
|
|
case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
|
|
case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
|
|
case RC_DUPLICATED_VALUE: return "Duplicated value expression";
|
|
case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
|
|
case RC_MISSING_START: return "Missing start condition";
|
|
case RC_MISSING_CANCEL: return "Missing cancel condition";
|
|
case RC_MISSING_SUBMIT: return "Missing submit condition";
|
|
case RC_MISSING_VALUE: return "Missing value expression";
|
|
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
|
|
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
|
|
case RC_OUT_OF_MEMORY: return "Out of memory";
|
|
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
|
|
case RC_MISSING_VALUE_MEASURED: return "Missing measured flag in value expression";
|
|
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
|
|
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
|
|
case RC_INVALID_COMPARISON: return "Invalid comparison";
|
|
case RC_INVALID_STATE: return "Invalid state";
|
|
case RC_INVALID_JSON: return "Invalid JSON";
|
|
case RC_API_FAILURE: return "API call failed";
|
|
case RC_LOGIN_REQUIRED: return "Login required";
|
|
case RC_NO_GAME_LOADED: return "No game loaded";
|
|
case RC_HARDCORE_DISABLED: return "Hardcore disabled";
|
|
case RC_ABORTED: return "Aborted";
|
|
case RC_NO_RESPONSE: return "No response";
|
|
case RC_ACCESS_DENIED: return "Access denied";
|
|
case RC_INVALID_CREDENTIALS: return "Invalid credentials";
|
|
case RC_EXPIRED_TOKEN: return "Expired token";
|
|
case RC_INSUFFICIENT_BUFFER: return "Buffer not large enough";
|
|
case RC_INVALID_VARIABLE_NAME: return "Invalid variable name";
|
|
case RC_UNKNOWN_VARIABLE_NAME: return "Unknown variable name";
|
|
default: return "Unknown error";
|
|
}
|
|
}
|