mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-28 18:38:41 +00:00
update
Some checks failed
Some checks failed
This commit is contained in:
parent
02ee4480ad
commit
e939a3f4a6
101
Compression/LZP.h
Normal file
101
Compression/LZP.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @package Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef COMPRESSION_LZP_H
|
||||
#define COMPRESSION_LZP_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../Stdlib/Types.h"
|
||||
|
||||
uint32 encode_lzp(const byte* in, uint32 length, byte* out)
|
||||
{
|
||||
byte buf[9];
|
||||
byte table[1 << 16] = {0};
|
||||
|
||||
uint16 hash = 0;
|
||||
int32 mask, i, j, c, in_pos = 0, out_pos = 0;
|
||||
|
||||
while(true) {
|
||||
j = 1;
|
||||
mask = 0;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (in_pos == length) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = in[in_pos++];
|
||||
if (c == table[hash]) {
|
||||
mask |= 1 << i;
|
||||
} else {
|
||||
table[hash] = c;
|
||||
buf[j++] = c;
|
||||
}
|
||||
|
||||
hash = (hash << 4) ^ c;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
buf[0] = mask;
|
||||
for (i = 0; i < j; ++i) {
|
||||
out[out_pos++] = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (in_pos == length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out_pos;
|
||||
}
|
||||
|
||||
uint32 decode_lzp(const byte* in, uint32 length, byte* out)
|
||||
{
|
||||
byte buf[8];
|
||||
byte table[1 << 16] = {0};
|
||||
|
||||
uint16 hash = 0;
|
||||
int mask, i, j, c, in_pos = 0, out_pos = 0;
|
||||
|
||||
while (true) {
|
||||
j = 0;
|
||||
if (in_pos == length) {
|
||||
break;
|
||||
}
|
||||
|
||||
mask = in[in_pos++];
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if ((mask & (1 << i)) != 0) {
|
||||
c = table[hash];
|
||||
} else {
|
||||
if (in_pos == length) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = in[in_pos++];
|
||||
table[hash] = c;
|
||||
}
|
||||
|
||||
buf[j++] = c;
|
||||
|
||||
hash = (hash << 4) ^ c;
|
||||
}
|
||||
|
||||
if (j > 0) {
|
||||
for (i = 0; i < j; ++i) {
|
||||
out[out_pos++] = buf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out_pos;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -25,8 +25,14 @@ namespace Sound {
|
|||
uint32 sample_rate;
|
||||
uint32 sample_size;
|
||||
uint32 sample_index;
|
||||
|
||||
int16 volume;
|
||||
|
||||
uint32 buffer_size;
|
||||
|
||||
// Actual samples inside the buffer
|
||||
// The buffer could be larger than the data to output
|
||||
uint32 sample_buffer_size;
|
||||
int16* buffer;
|
||||
|
||||
bool playing = false;
|
||||
|
|
@ -61,6 +67,8 @@ namespace Sound {
|
|||
|
||||
if(!SUCCEEDED(setting->direct_sound->SetCooperativeLevel(w->hwnd, DSSCL_PRIORITY))) {
|
||||
// @todo Log
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WAVEFORMATEX wf = {};
|
||||
|
|
@ -81,13 +89,18 @@ namespace Sound {
|
|||
|
||||
if(!SUCCEEDED(setting->direct_sound->CreateSoundBuffer(&bufferDesc, &setting->primary_buffer, 0))) {
|
||||
// @todo Log
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(setting->primary_buffer->SetFormat(&wf))) {
|
||||
// @todo Log
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setting->buffer_size = setting->sample_rate * setting->sample_size;
|
||||
setting->buffer = (int16 *) calloc(setting->sample_rate, setting->sample_size);
|
||||
|
||||
// Create secondary buffer
|
||||
DSBUFFERDESC bufferDesc2;
|
||||
|
|
@ -100,15 +113,19 @@ namespace Sound {
|
|||
|
||||
if(!SUCCEEDED(setting->direct_sound->CreateSoundBuffer(&bufferDesc2, &setting->secondary_buffer, 0))) {
|
||||
// @todo Log
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void direct_sound_play(DirectSoundSetting* setting)
|
||||
{
|
||||
setting->secondary_buffer->Play(0, 0, DSBPLAY_LOOPING);
|
||||
setting->playing = true;
|
||||
}
|
||||
|
||||
inline
|
||||
void direct_sound_free(DirectSoundSetting* setting)
|
||||
{
|
||||
if (setting->direct_sound) {
|
||||
|
|
@ -124,31 +141,19 @@ namespace Sound {
|
|||
}
|
||||
}
|
||||
|
||||
bool direct_sound_should_update(DirectSoundSetting* setting)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 *direct_sound_return_buffer(DirectSoundSetting* setting)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void direct_sound_play_buffer(DirectSoundSetting* setting)
|
||||
/**
|
||||
* Calculates the samples in bytes to generate for the buffer
|
||||
*/
|
||||
inline
|
||||
uint32 direct_sound_sample_buffer_size(const DirectSoundSetting* setting)
|
||||
{
|
||||
DWORD player_cursor;
|
||||
DWORD write_cursor;
|
||||
if (!SUCCEEDED(setting->secondary_buffer->GetCurrentPosition(&player_cursor, &write_cursor))) {
|
||||
// @todo Log
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *region1;
|
||||
DWORD region1_size;
|
||||
|
||||
void *region2;
|
||||
DWORD region2_size;
|
||||
|
||||
DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size;
|
||||
DWORD bytes_to_write = 0;
|
||||
|
||||
|
|
@ -161,6 +166,24 @@ namespace Sound {
|
|||
bytes_to_write = player_cursor - bytes_to_lock;
|
||||
}
|
||||
|
||||
return bytes_to_write;
|
||||
}
|
||||
|
||||
inline
|
||||
void direct_sound_play_buffer(DirectSoundSetting* setting, uint32 bytes_to_write)
|
||||
{
|
||||
if (bytes_to_write == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *region1;
|
||||
DWORD region1_size;
|
||||
|
||||
void *region2;
|
||||
DWORD region2_size;
|
||||
|
||||
DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size;
|
||||
|
||||
setting->secondary_buffer->Lock(
|
||||
bytes_to_lock, bytes_to_write,
|
||||
®ion1, ®ion1_size,
|
||||
|
|
@ -168,31 +191,28 @@ namespace Sound {
|
|||
0
|
||||
);
|
||||
|
||||
int16* sample_out = (int16 *) region1;
|
||||
DWORD region1_sample_count = region1_size / setting->sample_size;
|
||||
// @question Do we even need to use memcpy? Can't we use the buffer directly?
|
||||
// Probably depends on what lock actually does to region1/region2
|
||||
// Of course we would than need some mechanism to check when we can write into the buffer
|
||||
// See XAudio2 for this, we would probably need a second buffer as well
|
||||
memcpy(
|
||||
(void *) region1,
|
||||
(void *) setting->buffer,
|
||||
region1_size
|
||||
);
|
||||
|
||||
for (DWORD idx = 0; idx < region1_sample_count; ++idx) {
|
||||
f32 t = ((f32) 1.0f / (f32) (48000 / 256)) * 2.0f * OMS_PI;
|
||||
f32 sine_value = sinf(t);
|
||||
int16 sample_value = (int16) (sine_value * setting->volume);
|
||||
|
||||
*sample_out++ = sample_value;
|
||||
*sample_out++ = sample_value;
|
||||
}
|
||||
|
||||
sample_out = (int16 *) region2;
|
||||
DWORD region2_sample_count = region2_size / setting->sample_size;
|
||||
|
||||
for (DWORD idx = 0; idx < region2_sample_count; ++idx) {
|
||||
f32 t = ((f32) 1.0f / (f32) (48000 / 256)) * 2.0f * OMS_PI;
|
||||
f32 sine_value = sinf(t);
|
||||
int16 sample_value = (int16) (sine_value * setting->volume);
|
||||
|
||||
*sample_out++ = sample_value;
|
||||
*sample_out++ = sample_value;
|
||||
if (region2_size > 0) {
|
||||
memcpy(
|
||||
(void *) region2,
|
||||
(void *) (setting->buffer + region1_size),
|
||||
region2_size
|
||||
);
|
||||
}
|
||||
|
||||
setting->secondary_buffer->Unlock(region1, region1_size, region2, region2_size);
|
||||
|
||||
setting->sample_index += bytes_to_write / setting->sample_size;
|
||||
setting->sample_buffer_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
119
Sound/XAudio2.h
119
Sound/XAudio2.h
|
|
@ -25,9 +25,16 @@ namespace Sound {
|
|||
uint32 sample_rate;
|
||||
uint32 sample_size;
|
||||
uint32 sample_index;
|
||||
|
||||
int16 volume;
|
||||
|
||||
uint32 buffer_size;
|
||||
|
||||
// Actual samples inside the buffer
|
||||
// The buffer could be larger than the data to output
|
||||
uint32 sample_buffer_size;
|
||||
int16* buffer;
|
||||
|
||||
bool playing = false;
|
||||
byte type = SOUND_API_XAUDIO2;
|
||||
|
||||
|
|
@ -36,7 +43,7 @@ namespace Sound {
|
|||
IXAudio2SourceVoice* source_voice;
|
||||
IXAudio2MasteringVoice* mastering_voice;
|
||||
|
||||
XAUDIO2_BUFFER buffer[2];
|
||||
XAUDIO2_BUFFER internal_buffer[2];
|
||||
};
|
||||
|
||||
// BEGIN: Dynamically load XAudio2
|
||||
|
|
@ -64,7 +71,13 @@ namespace Sound {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(setting->xaudio2->CreateMasteringVoice(&setting->mastering_voice))) {
|
||||
if (!SUCCEEDED(setting->xaudio2->CreateMasteringVoice(
|
||||
&setting->mastering_voice,
|
||||
XAUDIO2_DEFAULT_CHANNELS,
|
||||
setting->sample_rate,
|
||||
0,
|
||||
NULL
|
||||
))) {
|
||||
// @todo Log
|
||||
return;
|
||||
}
|
||||
|
|
@ -72,10 +85,10 @@ namespace Sound {
|
|||
WAVEFORMATEX wf = {};
|
||||
wf.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wf.nChannels = 2;
|
||||
wf.wBitsPerSample = 16;
|
||||
wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8;
|
||||
wf.wBitsPerSample = (uint16) ((setting->sample_size * 8) / wf.nChannels); // = sample_size per channel
|
||||
wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; // = sample_szie
|
||||
wf.nSamplesPerSec = setting->sample_rate;
|
||||
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
|
||||
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; // = buffer_size
|
||||
wf.cbSize = 0;
|
||||
|
||||
if (!SUCCEEDED(setting->xaudio2->CreateSourceVoice(&setting->source_voice, &wf))) {
|
||||
|
|
@ -83,32 +96,34 @@ namespace Sound {
|
|||
return;
|
||||
}
|
||||
|
||||
setting->buffer_size = setting->sample_rate * wf.nBlockAlign;
|
||||
setting->buffer_size = setting->sample_rate * setting->sample_size;
|
||||
setting->buffer = (int16 *) calloc(setting->sample_rate, setting->sample_size);
|
||||
|
||||
// @question Consider to move to the heap?
|
||||
setting->buffer[0].Flags = 0;
|
||||
setting->buffer[0].AudioBytes = setting->buffer_size;
|
||||
setting->buffer[0].pAudioData = (BYTE *) malloc(setting->buffer_size * sizeof(BYTE));
|
||||
setting->buffer[0].PlayBegin = 0;
|
||||
setting->buffer[0].PlayLength = 0;
|
||||
setting->buffer[0].LoopBegin = 0;
|
||||
setting->buffer[0].LoopLength = 0;
|
||||
setting->buffer[0].LoopCount = 0;
|
||||
setting->buffer[0].pContext = NULL;
|
||||
setting->internal_buffer[0].Flags = 0;
|
||||
setting->internal_buffer[0].AudioBytes = setting->buffer_size;
|
||||
setting->internal_buffer[0].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte));
|
||||
setting->internal_buffer[0].PlayBegin = 0;
|
||||
setting->internal_buffer[0].PlayLength = 0;
|
||||
setting->internal_buffer[0].LoopBegin = 0;
|
||||
setting->internal_buffer[0].LoopLength = 0;
|
||||
setting->internal_buffer[0].LoopCount = 0;
|
||||
setting->internal_buffer[0].pContext = NULL;
|
||||
|
||||
setting->buffer[1].Flags = 0;
|
||||
setting->buffer[1].AudioBytes = setting->buffer_size;
|
||||
setting->buffer[1].pAudioData = (BYTE *) malloc(setting->buffer_size * sizeof(BYTE));
|
||||
setting->buffer[1].PlayBegin = 0;
|
||||
setting->buffer[1].PlayLength = 0;
|
||||
setting->buffer[1].LoopBegin = 0;
|
||||
setting->buffer[1].LoopLength = 0;
|
||||
setting->buffer[1].LoopCount = 0;
|
||||
setting->buffer[1].pContext = NULL;
|
||||
setting->internal_buffer[1].Flags = 0;
|
||||
setting->internal_buffer[1].AudioBytes = setting->buffer_size;
|
||||
setting->internal_buffer[1].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte));
|
||||
setting->internal_buffer[1].PlayBegin = 0;
|
||||
setting->internal_buffer[1].PlayLength = 0;
|
||||
setting->internal_buffer[1].LoopBegin = 0;
|
||||
setting->internal_buffer[1].LoopLength = 0;
|
||||
setting->internal_buffer[1].LoopCount = 0;
|
||||
setting->internal_buffer[1].pContext = NULL;
|
||||
|
||||
setting->sample_index = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
void xaudio2_play(XAudio2Setting* setting) {
|
||||
if (!setting->source_voice) {
|
||||
// @todo Log
|
||||
|
|
@ -120,14 +135,19 @@ namespace Sound {
|
|||
setting->playing = true;
|
||||
}
|
||||
|
||||
inline
|
||||
void xaudio2_free(XAudio2Setting* setting)
|
||||
{
|
||||
if (setting->buffer[0].pAudioData) {
|
||||
free((void *) setting->buffer[0].pAudioData);
|
||||
if (setting->internal_buffer[0].pAudioData) {
|
||||
free((void *) setting->internal_buffer[0].pAudioData);
|
||||
}
|
||||
|
||||
if (setting->buffer[1].pAudioData) {
|
||||
free((void *) setting->buffer[1].pAudioData);
|
||||
if (setting->internal_buffer[1].pAudioData) {
|
||||
free((void *) setting->internal_buffer[1].pAudioData);
|
||||
}
|
||||
|
||||
if (setting->buffer) {
|
||||
free((void *) setting->buffer);
|
||||
}
|
||||
|
||||
if (setting->source_voice) {
|
||||
|
|
@ -143,46 +163,55 @@ namespace Sound {
|
|||
}
|
||||
}
|
||||
|
||||
bool xaudio2_should_update(XAudio2Setting* setting)
|
||||
/**
|
||||
* Calculates the samples to generate for the buffer
|
||||
*
|
||||
* For XAudio2 we currently always fill the entire buffer size.
|
||||
* For other audio APIs we maybe have to do something else
|
||||
*/
|
||||
inline
|
||||
uint32 xaudio2_sample_buffer_size(XAudio2Setting* setting)
|
||||
{
|
||||
if (!setting->source_voice) {
|
||||
// @todo Log
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
setting->source_voice->GetState(&state);
|
||||
if (state.BuffersQueued > 1) {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
return setting->buffer_size;
|
||||
}
|
||||
|
||||
int16* xaudio2_return_buffer(XAudio2Setting* setting)
|
||||
{
|
||||
return (int16 *) setting->buffer[setting->sample_index].pAudioData;
|
||||
}
|
||||
|
||||
int16* xaudio2_fill_buffer(XAudio2Setting* setting, int16* buffer)
|
||||
{
|
||||
int16* sample_out = (int16 *) setting->buffer[setting->sample_index].pAudioData;
|
||||
}
|
||||
|
||||
void xaudio2_play_buffer(XAudio2Setting* setting) {
|
||||
inline
|
||||
void xaudio2_play_buffer(XAudio2Setting* setting, uint32 bytes_to_write) {
|
||||
if (!setting->source_voice) {
|
||||
// @todo Log
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setting->sample_index = (setting->sample_index + 1) % 2;
|
||||
if (bytes_to_write == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(setting->source_voice->SubmitSourceBuffer(&setting->buffer[setting->sample_index]))) {
|
||||
memcpy(
|
||||
(void *) setting->internal_buffer[setting->sample_index].pAudioData,
|
||||
setting->buffer,
|
||||
bytes_to_write
|
||||
);
|
||||
|
||||
if (!SUCCEEDED(setting->source_voice->SubmitSourceBuffer(&setting->internal_buffer[setting->sample_index]))) {
|
||||
// @todo Log
|
||||
return;
|
||||
}
|
||||
|
||||
setting->sample_index = (setting->sample_index + 1) % 2;
|
||||
setting->sample_buffer_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ typedef uint16_t uint16;
|
|||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
typedef uint16_t f16;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
|
|
@ -32,4 +33,71 @@ typedef unsigned char byte;
|
|||
#define local_persist static
|
||||
#define global_persist static
|
||||
|
||||
#define HALF_FLOAT_SIGN_MASK 0x8000
|
||||
#define HALF_FLOAT_EXP_MASK 0x7C00
|
||||
#define HALF_FLOAT_FRAC_MASK 0x03FF
|
||||
|
||||
#define HALF_FLOAT_EXP_SHIFT 10
|
||||
#define HALF_FLOAT_EXP_BIAS 15
|
||||
|
||||
#define FLOAT32_SIGN_MASK 0x80000000
|
||||
#define FLOAT32_EXP_MASK 0x7F800000
|
||||
#define FLOAT32_FRAC_MASK 0x007FFFFF
|
||||
|
||||
#define FLOAT32_EXP_SHIFT 23
|
||||
#define FLOAT32_EXP_BIAS 127
|
||||
|
||||
uint16 float_to_f16(float f) {
|
||||
uint32_t f_bits = *((uint32_t*)&f);
|
||||
uint16_t f16_bits = 0;
|
||||
|
||||
// Extract sign, exponent, and fraction from float
|
||||
uint16_t sign = (f_bits & FLOAT32_SIGN_MASK) >> 16;
|
||||
int32_t exponent = ((f_bits & FLOAT32_EXP_MASK) >> FLOAT32_EXP_SHIFT) - FLOAT32_EXP_BIAS + HALF_FLOAT_EXP_BIAS;
|
||||
uint32_t fraction = (f_bits & FLOAT32_FRAC_MASK) >> (FLOAT32_EXP_SHIFT - HALF_FLOAT_EXP_SHIFT);
|
||||
|
||||
if (exponent <= 0) {
|
||||
if (exponent < -10) {
|
||||
fraction = 0;
|
||||
} else {
|
||||
fraction = (fraction | 0x0400) >> (1 - exponent);
|
||||
}
|
||||
exponent = 0;
|
||||
} else if (exponent >= 0x1F) {
|
||||
exponent = 0x1F;
|
||||
fraction = 0;
|
||||
}
|
||||
|
||||
f16_bits = sign | (exponent << HALF_FLOAT_EXP_SHIFT) | (fraction & HALF_FLOAT_FRAC_MASK);
|
||||
|
||||
return f16_bits;
|
||||
}
|
||||
|
||||
float f16_to_float(f16 f) {
|
||||
uint32_t f_bits = 0;
|
||||
|
||||
uint32_t sign = (f & HALF_FLOAT_SIGN_MASK) << 16;
|
||||
int32_t exponent = (f & HALF_FLOAT_EXP_MASK) >> HALF_FLOAT_EXP_SHIFT;
|
||||
uint32_t fraction = (f & HALF_FLOAT_FRAC_MASK) << (FLOAT32_EXP_SHIFT - HALF_FLOAT_EXP_SHIFT);
|
||||
|
||||
if (exponent == 0) {
|
||||
if (fraction != 0) {
|
||||
exponent = 1;
|
||||
while ((fraction & (1 << FLOAT32_EXP_SHIFT)) == 0) {
|
||||
fraction <<= 1;
|
||||
exponent--;
|
||||
}
|
||||
fraction &= ~FLOAT32_EXP_MASK;
|
||||
}
|
||||
} else if (exponent == 0x1F) {
|
||||
exponent = 0xFF;
|
||||
} else {
|
||||
exponent += FLOAT32_EXP_BIAS - HALF_FLOAT_EXP_BIAS;
|
||||
}
|
||||
|
||||
f_bits = sign | (exponent << FLOAT32_EXP_SHIFT) | fraction;
|
||||
|
||||
return *((float*)&f_bits);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ namespace Test {
|
|||
double dt = 0.0;
|
||||
};
|
||||
|
||||
struct LogMessages {
|
||||
char *messages;
|
||||
};
|
||||
|
||||
void update_timing_stat(TimingStat *stat)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
|
@ -46,6 +50,12 @@ namespace Test {
|
|||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#define UPDATE_TIMING_STAT(stat) Test::update_timing_stat(stat);
|
||||
#else
|
||||
#define UPDATE_TIMING_STAT(stat)
|
||||
#endif
|
||||
|
||||
#define ASSERT_EQUALS(a, b, t1, t2) \
|
||||
({ \
|
||||
if ((a) == (b)) { \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user