Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2023-02-15 01:38:05 -08:00
commit 9991729de5
42 changed files with 4166 additions and 17031 deletions

View File

@ -112,6 +112,7 @@ Other fixes:
- Qt: Fix preloading for ROM replacing
- Qt: Fix screen not displaying on Wayland (fixes mgba.io/i/2190)
- Qt: Fix crash when selecting 256-color sprite in sprite view
- Qt: Fix coloration of swatches on styles with distinct frame backgrounds
- VFS: Failed file mapping should return NULL on POSIX
Misc:
- Core: Suspend runloop when a core crashes

View File

@ -239,6 +239,7 @@ elseif(UNIX)
endif()
if(APPLE)
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
add_definitions(-D_DARWIN_C_SOURCE)
list(APPEND OS_LIB "-framework Foundation")
if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6
@ -417,7 +418,7 @@ endif()
if(BUILD_GL)
find_package(OpenGL QUIET)
if(NOT OPENGL_FOUND)
if(NOT OPENGL_FOUND OR (APPLE AND MACOSX_SDK VERSION_GREATER 10.14))
set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE)
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
set(OPENGL_LIBRARY OpenGL::GL)
@ -434,6 +435,11 @@ if(NOT BUILD_GL AND NOT LIBMGBA_ONLY)
endif()
if(BUILD_GLES2 AND NOT BUILD_GL)
if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14)
find_package(OpenGL QUIET)
set(OPENGLES2_INCLUDE_DIR ${OPENGL_INCLUDE_DIR})
set(OPENGLES2_LIBRARY ${OPENGL_LIBRARY})
endif()
find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h)
find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM)
if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY)
@ -447,6 +453,11 @@ if(BUILD_GLES2)
endif()
if(BUILD_GLES3 AND NOT BUILD_GL)
if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14)
find_package(OpenGL QUIET)
set(OPENGLES3_INCLUDE_DIR ${OPENGL_INCLUDE_DIR})
set(OPENGLES3_LIBRARY ${OPENGL_LIBRARY})
endif()
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY)

View File

@ -33,14 +33,20 @@ struct mTiming {
void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextEvent);
void mTimingDeinit(struct mTiming* timing);
void mTimingClear(struct mTiming* timing);
void mTimingInterrupt(struct mTiming* timing);
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
void mTimingScheduleAbsolute(struct mTiming* timing, struct mTimingEvent*, int32_t when);
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*);
bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*);
int32_t mTimingTick(struct mTiming* timing, int32_t cycles);
int32_t mTimingCurrentTime(const struct mTiming* timing);
uint64_t mTimingGlobalTime(const struct mTiming* timing);
int32_t mTimingNextEvent(struct mTiming* timing);
int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent*);

View File

@ -463,7 +463,8 @@ struct GBSerializedState {
union {
uint8_t huc3Registers[0x80];
struct {
uint8_t registers[8];
uint8_t registers[4];
uint8_t reserved[4];
uint8_t rtcTimerPage[8];
uint8_t rtcAlarmPage[8];
uint8_t rtcFreePage0[8];

View File

@ -20,14 +20,12 @@ CXX_GUARD_START
#ifdef USE_EPOXY
#include <epoxy/gl.h>
#elif defined(BUILD_GL)
#ifdef __APPLE__
#elif defined(__APPLE__)
#include <OpenGL/gl3.h>
#else
#elif defined(BUILD_GL)
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#else
#include <GLES3/gl3.h>
#endif

View File

@ -25,6 +25,14 @@ void mTimingClear(struct mTiming* timing) {
timing->masterCycles = 0;
}
void mTimingInterrupt(struct mTiming* timing) {
if (!timing->root) {
return;
}
timing->reroot = timing->root;
timing->root = NULL;
}
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t when) {
int32_t nextEvent = when + *timing->relativeCycles;
event->when = nextEvent + timing->masterCycles;

View File

@ -94,7 +94,7 @@ static void mGUIShowCheatSet(struct mGUIRunner* runner, struct mCheatDevice* dev
switch (action) {
case CHEAT_ADD_LINE:
strlcpy(keyboard.title, "Add line", sizeof(keyboard.title));
keyboard.maxLen = 12;
keyboard.maxLen = 17;
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
mCheatAddLine(set, keyboard.result, 0);
_rebuildCheatView(&view.items, set);

View File

@ -29,6 +29,8 @@
#define W_OK 02
#endif
FILE* logfile;
bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
char path[PATH_MAX] = {0};
struct VDirEntry* vde;
@ -53,7 +55,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
}
switch (vde->type(vde)) {
case VFS_DIRECTORY:
printf("mkdir %s\n", fname);
fprintf(logfile, "mkdir %s\n", fname);
if (mkdir(path, 0755) < 0 && errno != EEXIST) {
return false;
}
@ -70,7 +72,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
}
break;
case VFS_FILE:
printf("extract %s\n", fname);
fprintf(logfile, "extract %s\n", fname);
vfIn = archive->openFile(archive, vde->name(vde), O_RDONLY);
errno = 0;
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
@ -111,13 +113,17 @@ int main(int argc, char* argv[]) {
const char* root;
int ok = 1;
mCoreConfigDirectory(bin, sizeof(bin));
strncat(bin, "/updater.log", sizeof(bin));
logfile = fopen(bin, "w");
mCoreConfigInit(&config, "updater");
if (!mCoreConfigLoad(&config)) {
puts("Failed to load config");
fputs("Failed to load config", logfile);
} else if (!mUpdateGetArchivePath(&config, updateArchive, sizeof(updateArchive)) || !(root = mUpdateGetRoot(&config))) {
puts("No pending update found");
fputs("No pending update found", logfile);
} else if (access(root, W_OK)) {
puts("Cannot write to update path");
fputs("Cannot write to update path", logfile);
} else {
#ifdef __APPLE__
char subdir[PATH_MAX];
@ -160,7 +166,7 @@ int main(int argc, char* argv[]) {
}
off_t diff = devend - devinfo - 1;
memcpy(devpath, &devinfo[1], diff);
puts(devpath);
fputs(devpath, logfile);
break;
}
int retstat;
@ -177,11 +183,11 @@ int main(int argc, char* argv[]) {
archive = VDirOpenArchive(updateArchive);
}
if (archive) {
puts("Extracting update");
fputs("Extracting update", logfile);
if (extractArchive(archive, root, prefix)) {
ok = 0;
} else {
puts("An error occurred");
fputs("An error occurred", logfile);
}
archive->close(archive);
unlink(updateArchive);
@ -218,10 +224,10 @@ int main(int argc, char* argv[]) {
close(infd);
}
if (ok == 2) {
puts("Cannot move update over old file");
fputs("Cannot move update over old file", logfile);
}
} else {
puts("Cannot move update over old file");
fputs("Cannot move update over old file", logfile);
}
} else {
ok = 0;
@ -232,10 +238,10 @@ int main(int argc, char* argv[]) {
}
#endif
else {
puts("Cannot open update archive");
fputs("Cannot open update archive", logfile);
}
if (ok == 0) {
puts("Complete");
fputs("Complete", logfile);
const char* command = mUpdateGetCommand(&config);
strlcpy(bin, command, sizeof(bin));
mUpdateDeregister(&config);
@ -260,6 +266,7 @@ int main(int argc, char* argv[]) {
}
}
mCoreConfigDeinit(&config);
fclose(logfile);
if (ok == 0) {
#ifdef _WIN32
char qbin[PATH_MAX + 2] = {0};

View File

@ -672,6 +672,8 @@ static void _GBCoreReset(struct mCore* core) {
if (core->opts.skipBios) {
GBSkipBIOS(core->board);
}
mTimingInterrupt(&gb->timing);
}
static void _GBCoreRunFrame(struct mCore* core) {

View File

@ -768,9 +768,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
case GB_TAMA5:
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.tama5.lastLatch);
state->memory.tama5.reg = memory->mbcState.tama5.reg;
for (i = 0; i < 8; ++i) {
for (i = 0; i < 4; ++i) {
state->tama5Registers.registers[i] = memory->mbcState.tama5.registers[i * 2] & 0xF;
state->tama5Registers.registers[i] |= memory->mbcState.tama5.registers[i * 2 + 1] << 4;
}
for (i = 0; i < 8; ++i) {
state->tama5Registers.rtcTimerPage[i] = memory->mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
state->tama5Registers.rtcTimerPage[i] |= memory->mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
state->tama5Registers.rtcAlarmPage[i] = memory->mbcState.tama5.rtcAlarmPage[i * 2] & 0xF;
@ -893,9 +895,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
case GB_TAMA5:
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.tama5.lastLatch);
memory->mbcState.tama5.reg = state->memory.tama5.reg;
for (i = 0; i < 8; ++i) {
for (i = 0; i < 4; ++i) {
memory->mbcState.tama5.registers[i * 2] = state->tama5Registers.registers[i] & 0xF;
memory->mbcState.tama5.registers[i * 2 + 1] = state->tama5Registers.registers[i] >> 4;
}
for (i = 0; i < 8; ++i) {
memory->mbcState.tama5.rtcTimerPage[i * 2] = state->tama5Registers.rtcTimerPage[i] & 0xF;
memory->mbcState.tama5.rtcTimerPage[i * 2 + 1] = state->tama5Registers.rtcTimerPage[i] >> 4;
memory->mbcState.tama5.rtcAlarmPage[i * 2] = state->tama5Registers.rtcAlarmPage[i] & 0xF;

View File

@ -217,8 +217,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
gb->timing.reroot = gb->timing.root;
gb->timing.root = NULL;
mTimingInterrupt(&gb->timing);
return true;
}

View File

@ -723,6 +723,8 @@ static void _GBACoreReset(struct mCore* core) {
if (forceSkip || (core->opts.skipBios && (gba->romVf || gba->memory.rom))) {
GBASkipBIOS(core->board);
}
mTimingInterrupt(&gba->timing);
}
static void _GBACoreRunFrame(struct mCore* core) {

View File

@ -74,9 +74,9 @@ const uint8_t hleBios[SIZE_BIOS] = {
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00,
0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1,
0xc2, 0x03, 0xa0, 0xe3, 0x03, 0x10, 0x50, 0xe4, 0x00, 0x00, 0x51, 0xe3,
0x00, 0x10, 0xa0, 0x13, 0x10, 0xff, 0x2f, 0x11, 0x1c, 0x00, 0x9f, 0xe5,
0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x10, 0xff, 0x2f, 0x11, 0xc0, 0x00, 0x40, 0xe2, 0x10, 0xff, 0x2f, 0xe1,
0xc2, 0xe3, 0xa0, 0xe3, 0x03, 0x10, 0x5e, 0xe4, 0x00, 0x00, 0x51, 0xe3,
0x00, 0x10, 0xa0, 0x13, 0x1e, 0xff, 0x2f, 0x11, 0x1c, 0xe0, 0x9f, 0xe5,
0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x1e, 0xff, 0x2f, 0x11, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02
};

View File

@ -314,17 +314,17 @@ bhi StallCall
bx lr
resetBase:
mov r0, #0x8000003
ldrb r1, [r0], #-3
mov lr, #0x8000003
ldrb r1, [lr], #-3
cmp r1, #0
movne r1, #0
bxne r0
ldr r0, =0x20000C0
ldr r1, [r0]
bxne lr
ldr lr, =0x20000C0
ldr r1, [lr]
cmp r1, #0
mov r1, #0
bxne r0
sub r0, #0xC0
bx r0
bxne lr
sub lr, #0xC0
bx lr
.word 0
.word 0xE129F000

View File

@ -585,7 +585,7 @@ void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
}
void GBASavedataRTCWrite(struct GBASavedata* savedata) {
if (!(savedata->gpio->devices & HW_RTC)) {
if (!(savedata->gpio->devices & HW_RTC) || !savedata->vf || savedata->mapMode == MAP_READ) {
return;
}
@ -595,9 +595,18 @@ void GBASavedataRTCWrite(struct GBASavedata* savedata) {
buffer.control = savedata->gpio->rtc.control;
STORE_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
size_t size = GBASavedataSize(savedata) & ~0xFF;
savedata->vf->seek(savedata->vf, size, SEEK_SET);
size_t size = GBASavedataSize(savedata);
savedata->vf->seek(savedata->vf, size & ~0xFF, SEEK_SET);
if ((savedata->vf->size(savedata->vf) & 0xFF) != sizeof(buffer)) {
// Writing past the end of the file can invalidate the file mapping
savedata->vf->unmap(savedata->vf, savedata->data, size);
savedata->data = NULL;
}
savedata->vf->write(savedata->vf, &buffer, sizeof(buffer));
if (!savedata->data) {
savedata->data = savedata->vf->map(savedata->vf, size, MAP_WRITE);
}
}
static uint8_t _unBCD(uint8_t byte) {
@ -605,6 +614,10 @@ static uint8_t _unBCD(uint8_t byte) {
}
void GBASavedataRTCRead(struct GBASavedata* savedata) {
if (!savedata->vf) {
return;
}
struct GBASavedataRTCBuffer buffer;
size_t size = GBASavedataSize(savedata) & ~0xFF;

View File

@ -211,8 +211,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBAMatrixDeserialize(gba, state);
}
gba->timing.reroot = gba->timing.root;
gba->timing.root = NULL;
mTimingInterrupt(&gba->timing);
return true;
}

View File

@ -12,14 +12,12 @@ CXX_GUARD_START
#ifdef USE_EPOXY
#include <epoxy/gl.h>
#elif defined(BUILD_GL)
#ifdef __APPLE__
#elif defined(__APPLE__)
#include <OpenGL/gl3.h>
#else
#elif defined(BUILD_GL)
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#elif defined(BUILD_GLES3)
#include <GLES3/gl3.h>
#else

View File

@ -132,9 +132,17 @@ static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
utf16Buffer = params.inputTextBuffer;
utf8Buffer = keyboard->result;
i = keyboard->maxLen;
while (i > 0 && *utf16Buffer) {
uint32_t unichar = utf16Char((const uint16_t**) &utf16Buffer, &i);
utf8Buffer += toUtf8(unichar, utf8Buffer);
size_t bufferSize = sizeof(SceWChar16) * keyboard->maxLen;
while (bufferSize && *utf16Buffer) {
char buffer[4];
uint32_t unichar = utf16Char((const uint16_t**) &utf16Buffer, &bufferSize);
size_t bytes = toUtf8(unichar, buffer);
if (i < bytes) {
break;
}
memcpy(utf8Buffer, buffer, bytes);
utf8Buffer += bytes;
i -= bytes;
}
utf8Buffer[0] = 0;

View File

@ -40,7 +40,6 @@ if(NOT ${QT}Widgets_FOUND)
endif()
if(APPLE)
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
if(MACOSX_SDK VERSION_GREATER 10.14)
list(APPEND QT_DEFINES USE_SHARE_WIDGET)
endif()

View File

@ -15,11 +15,8 @@ ColorPicker::ColorPicker() {
ColorPicker::ColorPicker(QWidget* parent, const QColor& defaultColor)
: m_parent(parent)
, m_defaultColor(defaultColor)
{
QPalette palette = parent->palette();
palette.setColor(parent->backgroundRole(), defaultColor);
parent->setPalette(palette);
setColor(defaultColor);
parent->installEventFilter(this);
}
@ -36,10 +33,7 @@ ColorPicker& ColorPicker::operator=(const ColorPicker& other) {
void ColorPicker::setColor(const QColor& color) {
m_defaultColor = color;
QPalette palette = m_parent->palette();
palette.setColor(m_parent->backgroundRole(), color);
m_parent->setPalette(palette);
m_parent->setStyleSheet(QString("background-color: %1;").arg(color.name()));
}
bool ColorPicker::eventFilter(QObject* obj, QEvent* event) {
@ -57,10 +51,7 @@ bool ColorPicker::eventFilter(QObject* obj, QEvent* event) {
colorPicker->setCurrentColor(m_defaultColor);
colorPicker->open();
connect(colorPicker, &QColorDialog::colorSelected, [this, swatch](const QColor& color) {
m_defaultColor = color;
QPalette palette = swatch->palette();
palette.setColor(swatch->backgroundRole(), color);
swatch->setPalette(palette);
setColor(color);
emit colorChanged(color);
});
return true;

View File

@ -83,6 +83,7 @@ void mGLWidget::initializeGL() {
m_positionLocation = m_program->attributeLocation("position");
m_vaoDone = false;
m_tex = 0;
connect(&m_refresh, &QTimer::timeout, this, static_cast<void (QWidget::*)()>(&QWidget::update));
}
@ -115,6 +116,10 @@ void mGLWidget::paintGL() {
if (!m_vaoDone && !finalizeVAO()) {
return;
}
if (!m_tex) {
m_refresh.start(10);
return;
}
QOpenGLFunctions_Baseline* fn = context()->versionFunctions<QOpenGLFunctions_Baseline>();
m_program->bind();
m_vao->bind();
@ -523,7 +528,6 @@ void PainterGL::create() {
m_finalTexIdx = 0;
gl2Backend->finalShader.tex = m_finalTex[m_finalTexIdx];
m_widget->setTex(m_finalTex[m_finalTexIdx]);
}
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
}

View File

@ -1187,6 +1187,11 @@ void Window::changeRenderer() {
m_config->updateOption("videoScale");
}
} else {
std::shared_ptr<VideoProxy> proxy = m_display->videoProxy();
if (proxy) {
proxy->detach(m_controller.get());
m_display->setVideoProxy({});
}
m_controller->setFramebufferHandle(-1);
}
}
@ -1388,7 +1393,6 @@ void Window::setupMenu(QMenuBar* menubar) {
m_actions.addAction(tr("Boot BIOS"), "bootBIOS", this, &Window::bootBIOS, "file");
#endif
addGameAction(tr("Replace ROM..."), "replaceROM", this, &Window::replaceROM, "file");
#ifdef M_CORE_GBA
Action* scanCard = addGameAction(tr("Scan e-Reader dotcodes..."), "scanCard", this, &Window::scanCard, "file");
m_platformActions.insert(mPLATFORM_GBA, scanCard);
@ -1499,10 +1503,12 @@ void Window::setupMenu(QMenuBar* menubar) {
addGameAction(tr("&Reset"), "reset", &CoreController::reset, "emu", QKeySequence("Ctrl+R"));
addGameAction(tr("Sh&utdown"), "shutdown", &CoreController::stop, "emu");
m_actions.addSeparator("emu");
addGameAction(tr("Replace ROM..."), "replaceROM", this, &Window::replaceROM, "emu");
Action* yank = addGameAction(tr("Yank game pak"), "yank", &CoreController::yankPak, "emu");
m_platformActions.insert(mPLATFORM_GBA, yank);
m_platformActions.insert(mPLATFORM_GB, yank);
m_actions.addSeparator("emu");
Action* pause = m_actions.addBooleanAction(tr("&Pause"), "pause", [this](bool paused) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -144,7 +144,7 @@ uint32_t utf8Char(const char** unicode, size_t* length) {
if (numBytes == 0) {
return 0xFFFD;
}
if (*length < numBytes) {
if (*length + 1 < numBytes) {
*length = 0;
return 0xFFFD;
}

View File

@ -8,125 +8,140 @@
#include <mgba-util/string.h>
M_TEST_DEFINE(strlenASCII) {
assert_int_equal(utf8strlen(""), 0);
assert_int_equal(utf8strlen("a"), 1);
assert_int_equal(utf8strlen("aa"), 2);
assert_int_equal(utf8strlen("aaa"), 3);
assert_int_equal(utf8strlen(""), 0);
assert_int_equal(utf8strlen("a"), 1);
assert_int_equal(utf8strlen("aa"), 2);
assert_int_equal(utf8strlen("aaa"), 3);
}
M_TEST_DEFINE(strlenMultibyte) {
assert_int_equal(utf8strlen("\300\200"), 1);
assert_int_equal(utf8strlen("a\300\200"), 2);
assert_int_equal(utf8strlen("\300\200a"), 2);
assert_int_equal(utf8strlen("a\300\200a"), 3);
assert_int_equal(utf8strlen("\300\200"), 1);
assert_int_equal(utf8strlen("a\300\200"), 2);
assert_int_equal(utf8strlen("\300\200a"), 2);
assert_int_equal(utf8strlen("a\300\200a"), 3);
assert_int_equal(utf8strlen("\300\200\300\200"), 2);
assert_int_equal(utf8strlen("a\300\200\300\200"), 3);
assert_int_equal(utf8strlen("\300\200a\300\200"), 3);
assert_int_equal(utf8strlen("\300\200\300\200a"), 3);
assert_int_equal(utf8strlen("\300\200\300\200"), 2);
assert_int_equal(utf8strlen("a\300\200\300\200"), 3);
assert_int_equal(utf8strlen("\300\200a\300\200"), 3);
assert_int_equal(utf8strlen("\300\200\300\200a"), 3);
assert_int_equal(utf8strlen("\340\200\200"), 1);
assert_int_equal(utf8strlen("a\340\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200a"), 2);
assert_int_equal(utf8strlen("a\340\200\200a"), 3);
assert_int_equal(utf8strlen("\340\200\200"), 1);
assert_int_equal(utf8strlen("a\340\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200a"), 2);
assert_int_equal(utf8strlen("a\340\200\200a"), 3);
assert_int_equal(utf8strlen("\340\200\200\340\200\200"), 2);
assert_int_equal(utf8strlen("a\340\200\200\340\200\200"), 3);
assert_int_equal(utf8strlen("\340\200\200a\340\200\200"), 3);
assert_int_equal(utf8strlen("\340\200\200\340\200\200a"), 3);
assert_int_equal(utf8strlen("\340\200\200\340\200\200"), 2);
assert_int_equal(utf8strlen("a\340\200\200\340\200\200"), 3);
assert_int_equal(utf8strlen("\340\200\200a\340\200\200"), 3);
assert_int_equal(utf8strlen("\340\200\200\340\200\200a"), 3);
assert_int_equal(utf8strlen("\340\200\200\300\200"), 2);
assert_int_equal(utf8strlen("\300\200\340\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200\300\200"), 2);
assert_int_equal(utf8strlen("\300\200\340\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200"), 1);
assert_int_equal(utf8strlen("a\360\200\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200a"), 2);
assert_int_equal(utf8strlen("a\360\200\200\200a"), 3);
assert_int_equal(utf8strlen("\360\200\200\200"), 1);
assert_int_equal(utf8strlen("a\360\200\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200a"), 2);
assert_int_equal(utf8strlen("a\360\200\200\200a"), 3);
assert_int_equal(utf8strlen("\360\200\200\200\360\200\200\200"), 2);
assert_int_equal(utf8strlen("a\360\200\200\200\360\200\200\200"), 3);
assert_int_equal(utf8strlen("\360\200\200\200a\360\200\200\200"), 3);
assert_int_equal(utf8strlen("\360\200\200\200\360\200\200\200a"), 3);
assert_int_equal(utf8strlen("\360\200\200\200\360\200\200\200"), 2);
assert_int_equal(utf8strlen("a\360\200\200\200\360\200\200\200"), 3);
assert_int_equal(utf8strlen("\360\200\200\200a\360\200\200\200"), 3);
assert_int_equal(utf8strlen("\360\200\200\200\360\200\200\200a"), 3);
assert_int_equal(utf8strlen("\360\200\200\200\300\200"), 2);
assert_int_equal(utf8strlen("\300\200\360\200\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200\300\200"), 2);
assert_int_equal(utf8strlen("\300\200\360\200\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200\340\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200\360\200\200\200"), 2);
assert_int_equal(utf8strlen("\360\200\200\200\340\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200\360\200\200\200"), 2);
}
M_TEST_DEFINE(strlenDegenerate) {
assert_int_equal(utf8strlen("\200"), 1);
assert_int_equal(utf8strlen("\200a"), 2);
assert_int_equal(utf8strlen("\200"), 1);
assert_int_equal(utf8strlen("\200a"), 2);
assert_int_equal(utf8strlen("\300"), 1);
assert_int_equal(utf8strlen("\300a"), 2);
assert_int_equal(utf8strlen("\300"), 1);
assert_int_equal(utf8strlen("\300a"), 2);
assert_int_equal(utf8strlen("\300\300"), 2);
assert_int_equal(utf8strlen("\300\300a"), 3);
assert_int_equal(utf8strlen("\300\300\200"), 2);
assert_int_equal(utf8strlen("\300\300"), 2);
assert_int_equal(utf8strlen("\300\300a"), 3);
assert_int_equal(utf8strlen("\300\300\200"), 2);
assert_int_equal(utf8strlen("\300\200\200"), 2);
assert_int_equal(utf8strlen("\300\200\200a"), 3);
assert_int_equal(utf8strlen("\300\200\200"), 2);
assert_int_equal(utf8strlen("\300\200\200a"), 3);
assert_int_equal(utf8strlen("\340"), 1);
assert_int_equal(utf8strlen("\340a"), 2);
assert_int_equal(utf8strlen("\340\300"), 2);
assert_int_equal(utf8strlen("\340\300a"), 3);
assert_int_equal(utf8strlen("\340\300\200"), 2);
assert_int_equal(utf8strlen("\340\200"), 1);
assert_int_equal(utf8strlen("\340\200a"), 2);
assert_int_equal(utf8strlen("\340\200\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200\200a"), 3);
assert_int_equal(utf8strlen("\340"), 1);
assert_int_equal(utf8strlen("\340a"), 2);
assert_int_equal(utf8strlen("\340\300"), 2);
assert_int_equal(utf8strlen("\340\300a"), 3);
assert_int_equal(utf8strlen("\340\300\200"), 2);
assert_int_equal(utf8strlen("\340\200"), 1);
assert_int_equal(utf8strlen("\340\200a"), 2);
assert_int_equal(utf8strlen("\340\200\200\200"), 2);
assert_int_equal(utf8strlen("\340\200\200\200a"), 3);
assert_int_equal(utf8strlen("\360"), 1);
assert_int_equal(utf8strlen("\360a"), 2);
assert_int_equal(utf8strlen("\360\300"), 2);
assert_int_equal(utf8strlen("\360\300a"), 3);
assert_int_equal(utf8strlen("\360\300\200"), 2);
assert_int_equal(utf8strlen("\360\200"), 1);
assert_int_equal(utf8strlen("\360\200a"), 2);
assert_int_equal(utf8strlen("\360\200\300"), 2);
assert_int_equal(utf8strlen("\360\200\300a"), 3);
assert_int_equal(utf8strlen("\360\200\300\200"), 2);
assert_int_equal(utf8strlen("\360\200\200"), 1);
assert_int_equal(utf8strlen("\360\200\200a"), 2);
assert_int_equal(utf8strlen("\360"), 1);
assert_int_equal(utf8strlen("\360a"), 2);
assert_int_equal(utf8strlen("\360\300"), 2);
assert_int_equal(utf8strlen("\360\300a"), 3);
assert_int_equal(utf8strlen("\360\300\200"), 2);
assert_int_equal(utf8strlen("\360\200"), 1);
assert_int_equal(utf8strlen("\360\200a"), 2);
assert_int_equal(utf8strlen("\360\200\300"), 2);
assert_int_equal(utf8strlen("\360\200\300a"), 3);
assert_int_equal(utf8strlen("\360\200\300\200"), 2);
assert_int_equal(utf8strlen("\360\200\200"), 1);
assert_int_equal(utf8strlen("\360\200\200a"), 2);
assert_int_equal(utf8strlen("\370"), 1);
assert_int_equal(utf8strlen("\370a"), 2);
assert_int_equal(utf8strlen("\370\200"), 2);
assert_int_equal(utf8strlen("\370\200a"), 3);
assert_int_equal(utf8strlen("\374"), 1);
assert_int_equal(utf8strlen("\374a"), 2);
assert_int_equal(utf8strlen("\374\200"), 2);
assert_int_equal(utf8strlen("\374\200a"), 3);
assert_int_equal(utf8strlen("\376"), 1);
assert_int_equal(utf8strlen("\376a"), 2);
assert_int_equal(utf8strlen("\376\200"), 2);
assert_int_equal(utf8strlen("\376\200a"), 3);
assert_int_equal(utf8strlen("\377"), 1);
assert_int_equal(utf8strlen("\377a"), 2);
assert_int_equal(utf8strlen("\377\200"), 2);
assert_int_equal(utf8strlen("\377\200a"), 3);
assert_int_equal(utf8strlen("\370"), 1);
assert_int_equal(utf8strlen("\370a"), 2);
assert_int_equal(utf8strlen("\370\200"), 2);
assert_int_equal(utf8strlen("\370\200a"), 3);
assert_int_equal(utf8strlen("\374"), 1);
assert_int_equal(utf8strlen("\374a"), 2);
assert_int_equal(utf8strlen("\374\200"), 2);
assert_int_equal(utf8strlen("\374\200a"), 3);
assert_int_equal(utf8strlen("\376"), 1);
assert_int_equal(utf8strlen("\376a"), 2);
assert_int_equal(utf8strlen("\376\200"), 2);
assert_int_equal(utf8strlen("\376\200a"), 3);
assert_int_equal(utf8strlen("\377"), 1);
assert_int_equal(utf8strlen("\377a"), 2);
assert_int_equal(utf8strlen("\377\200"), 2);
assert_int_equal(utf8strlen("\377\200a"), 3);
}
M_TEST_DEFINE(roundtrip) {
uint32_t unichar;
char buf[8] = {0};
for (unichar = 0; unichar < 0x110000; ++unichar) {
memset(buf, 0, sizeof(buf));
size_t len = toUtf8(unichar, buf) + 1;
const char* ptr = buf;
assert_true(len);
assert_false(buf[len]);
assert_int_equal(utf8Char(&ptr, &len), unichar);
assert_int_equal(len, 1);
}
uint32_t unichar;
char buf[8] = {0};
for (unichar = 0; unichar < 0x110000; ++unichar) {
memset(buf, 0, sizeof(buf));
size_t len = toUtf8(unichar, buf) + 1;
const char* ptr = buf;
assert_true(len);
assert_false(buf[len]);
assert_int_equal(utf8Char(&ptr, &len), unichar);
assert_int_equal(len, 1);
}
}
M_TEST_DEFINE(roundtripUnpadded) {
uint32_t unichar;
char buf[8] = {0};
for (unichar = 0; unichar < 0x110000; ++unichar) {
memset(buf, 0, sizeof(buf));
size_t len = toUtf8(unichar, buf);
const char* ptr = buf;
assert_true(len);
assert_false(buf[len]);
assert_int_equal(utf8Char(&ptr, &len), unichar);
assert_int_equal(len, 0);
}
}
M_TEST_SUITE_DEFINE(StringUTF8,
cmocka_unit_test(strlenASCII),
cmocka_unit_test(strlenMultibyte),
cmocka_unit_test(strlenDegenerate),
cmocka_unit_test(roundtrip),
cmocka_unit_test(strlenASCII),
cmocka_unit_test(strlenMultibyte),
cmocka_unit_test(strlenDegenerate),
cmocka_unit_test(roundtrip),
cmocka_unit_test(roundtripUnpadded),
)