diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b9673f..48b686f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,14 @@ add_executable(${PROJECT_NAME} src/gui/GuiElement.h src/gui/GuiFrame.cpp src/gui/GuiFrame.h - src/gui/GuiImage.cpp - src/gui/GuiImage.h src/gui/sigslot.h src/system/SDLSystem.cpp src/system/SDLSystem.h src/gui/GuiElement.cpp src/gui/GuiText.cpp src/gui/GuiText.h + src/gui/GuiImage.cpp + src/gui/GuiImage.h src/gui/GuiSound.cpp src/gui/GuiSound.h src/gui/GuiTrigger.cpp @@ -23,12 +23,16 @@ add_executable(${PROJECT_NAME} src/gui/GuiController.h src/gui/GuiButton.cpp src/gui/GuiButton.h - - + src/resources/Resources.cpp + src/resources/Resources.h + src/fs/CFile.cpp + src/fs/CFile.hpp + src/fs/FSUtils.cpp + src/fs/FSUtils.h src/input/SDLController.h src/menu/MainWindow.cpp src/menu/MainWindow.h src/input/SDLControllerJoystick.h src/input/SDLControllerMouse.h src/input/SDLControllerWiiUGamepad.h src/input/SDLControllerWiiUProContoller.h - src/gui/GuiTexture.cpp src/gui/GuiTexture.h + src/gui/GuiTextureData.cpp src/gui/GuiTextureData.h src/system/video/SDL_FontCache.h src/system/video/SDL_FontCache.cpp diff --git a/Makefile.wiiu b/Makefile.wiiu index a5e2d3c..d0c2419 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -22,12 +22,17 @@ TARGET := SDL2_Playground BUILD := build SOURCES := src \ src/gui \ + src/fs \ src/input \ src/menu \ + src/resources \ src/system \ src/system/video \ src/utils -DATA := data +DATA := data \ + data/images \ + data/sounds \ + data/fonts INCLUDES := source #------------------------------------------------------------------------------- @@ -45,14 +50,12 @@ LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) LIBS := `$(PREFIX)pkg-config --libs SDL2_mixer SDL2_ttf SDL2_image` - #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level # containing include and lib #------------------------------------------------------------------------------- LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) - #------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional # rules for different file extensions @@ -68,6 +71,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) +FILELIST := $(shell bash ./filelist.sh) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) @@ -138,6 +142,26 @@ $(OFILES_SRC) : $(HFILES_BIN) @echo $(notdir $<) @$(bin2o) +%.png.o %_png.h : %.png + @echo $(notdir $<) + @$(bin2o) + +%.jpg.o %_jpg.h : %.jpg + @echo $(notdir $<) + @$(bin2o) + +%.ogg.o %_ogg.h : %.ogg + @echo $(notdir $<) + @$(bin2o) + +%.mp3.o %_mp3.h : %.mp3 + @echo $(notdir $<) + @$(bin2o) + +%.ttf.o %_ttf.h : %.ttf + @echo $(notdir $<) + @$(bin2o) + #--------------------------------------------------------------------------------- %.o: %.s @echo $(notdir $<) diff --git a/data/fonts/FreeSans.ttf b/data/fonts/FreeSans.ttf new file mode 100644 index 0000000..9db9585 Binary files /dev/null and b/data/fonts/FreeSans.ttf differ diff --git a/data/images/button.png b/data/images/button.png new file mode 100644 index 0000000..974f7f2 Binary files /dev/null and b/data/images/button.png differ diff --git a/data/sounds/bgMusic.ogg b/data/sounds/bgMusic.ogg new file mode 100644 index 0000000..c268fd8 Binary files /dev/null and b/data/sounds/bgMusic.ogg differ diff --git a/data/sounds/button_click.mp3 b/data/sounds/button_click.mp3 new file mode 100644 index 0000000..ec4aad7 Binary files /dev/null and b/data/sounds/button_click.mp3 differ diff --git a/filelist.sh b/filelist.sh new file mode 100644 index 0000000..4112284 --- /dev/null +++ b/filelist.sh @@ -0,0 +1,91 @@ +#! /bin/bash +# +# Automatic resource file list generation +# Created by Dimok + +outFile="./src/resources/filelist.h" +count_old=$(cat $outFile 2>/dev/null | tr -d '\n\n' | sed 's/[^0-9]*\([0-9]*\).*/\1/') + +count=0 +if [[ $OSTYPE == darwin* ]]; +then + +for i in $(gfind ./data/images/ ./data/sounds/ ./data/fonts/ -maxdepth 1 -type f \( ! -printf "%f\n" \) | sort -f) +do + files[count]=$i + count=$((count+1)) +done + +else + +for i in $(find ./data/images/ ./data/sounds/ ./data/fonts/ -maxdepth 1 -type f \( ! -printf "%f\n" \) | sort -f) +do + files[count]=$i + count=$((count+1)) +done + +fi + +if [ "$count_old" != "$count" ] || [ ! -f $outFile ] +then + +echo "Generating filelist.h for $count files." >&2 +cat < $outFile +/**************************************************************************** + * Resource files. + * This file is generated automatically. + * Includes $count files. + * + * NOTE: + * Any manual modification of this file will be overwriten by the generation. + ****************************************************************************/ +#ifndef _FILELIST_H_ +#define _FILELIST_H_ + + +#include "Resources.h" +#ifdef __WIIU__ +EOF + +for i in ${files[@]} +do + filename=${i%.*} + extension=${i##*.} + echo '#include "'$filename'_'$extension'.h"' >> $outFile +done + +echo '' >> $outFile + +echo 'static RecourceFile RecourceList[] =' >> $outFile +echo '{' >> $outFile + +for i in ${files[@]} +do + filename=${i%.*} + extension=${i##*.} + echo -e '\t{"'$i'", '$filename'_'$extension', '$filename'_'$extension'_size, NULL, 0},' >> $outFile +done + +echo -e '\t{NULL, NULL, 0, NULL, 0}' >> $outFile +echo '};' >> $outFile +echo '' >> $outFile +echo '#else' >> $outFile + +echo 'static RecourceFile RecourceList[] =' >> $outFile +echo '{' >> $outFile + +for i in ${files[@]} +do + filename=${i%.*} + extension=${i##*.} + echo -e '\t{"'$i'", NULL, NULL, NULL, 0},' >> $outFile +done + +echo -e '\t{NULL, NULL, 0, NULL, 0}' >> $outFile +echo '};' >> $outFile +echo '' >> $outFile +echo '#endif' >> $outFile + +echo '#endif' >> $outFile + +fi diff --git a/src/fs/CFile.cpp b/src/fs/CFile.cpp new file mode 100644 index 0000000..5d9547e --- /dev/null +++ b/src/fs/CFile.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include "CFile.hpp" + +CFile::CFile() { + iFd = -1; + mem_file = NULL; + filesize = 0; + pos = 0; +} + +CFile::CFile(const std::string &filepath, eOpenTypes mode) { + iFd = -1; + this->open(filepath, mode); +} + +CFile::CFile(const uint8_t *mem, int32_t size) { + iFd = -1; + this->open(mem, size); +} + +CFile::~CFile() { + this->close(); +} + +int32_t CFile::open(const std::string &filepath, eOpenTypes mode) { + this->close(); + int32_t openMode = 0; + + // This depend on the devoptab implementation. + // see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference + + switch (mode) { + default: + case ReadOnly: // file must exist + openMode = O_RDONLY; + break; + case WriteOnly: // file will be created / zerod + openMode = O_TRUNC | O_CREAT | O_WRONLY; + break; + case ReadWrite: // file must exist + openMode = O_RDWR; + break; + case Append: // append to file, file will be created if missing. write only + openMode = O_CREAT | O_APPEND | O_WRONLY; + break; + } + + //! Using fopen works only on the first launch as expected + //! on the second launch it causes issues because we don't overwrite + //! the .data sections which is needed for a normal application to re-init + //! this will be added with launching as RPX + iFd = ::open(filepath.c_str(), openMode); + if (iFd < 0) + return iFd; + + + filesize = ::lseek(iFd, 0, SEEK_END); + ::lseek(iFd, 0, SEEK_SET); + + return 0; +} + +int32_t CFile::open(const uint8_t *mem, int32_t size) { + this->close(); + + mem_file = mem; + filesize = size; + + return 0; +} + +void CFile::close() { + if (iFd >= 0) + ::close(iFd); + + iFd = -1; + mem_file = NULL; + filesize = 0; + pos = 0; +} + +int32_t CFile::read(uint8_t *ptr, size_t size) { + if (iFd >= 0) { + int32_t ret = ::read(iFd, ptr, size); + if (ret > 0) + pos += ret; + return ret; + } + + int32_t readsize = size; + + if (readsize > (int64_t) (filesize - pos)) + readsize = filesize - pos; + + if (readsize <= 0) + return readsize; + + if (mem_file != NULL) { + memcpy(ptr, mem_file + pos, readsize); + pos += readsize; + return readsize; + } + + return -1; +} + +int32_t CFile::write(const uint8_t *ptr, size_t size) { + if (iFd >= 0) { + size_t done = 0; + while (done < size) { + int32_t ret = ::write(iFd, ptr, size - done); + if (ret <= 0) + return ret; + + ptr += ret; + done += ret; + pos += ret; + } + return done; + } + + return -1; +} + +int32_t CFile::seek(long int offset, int32_t origin) { + int32_t ret = 0; + int64_t newPos = pos; + + if (origin == SEEK_SET) { + newPos = offset; + } else if (origin == SEEK_CUR) { + newPos += offset; + } else if (origin == SEEK_END) { + newPos = filesize + offset; + } + + if (newPos < 0) { + pos = 0; + } else { + pos = newPos; + } + + if (iFd >= 0) + ret = ::lseek(iFd, pos, SEEK_SET); + + if (mem_file != NULL) { + if (pos > filesize) { + pos = filesize; + } + } + + return ret; +} + +int32_t CFile::fwrite(const char *format, ...) { + char tmp[512]; + tmp[0] = 0; + int32_t result = -1; + + va_list va; + va_start(va, format); + if ((vsprintf(tmp, format, va) >= 0)) { + result = this->write((uint8_t *) tmp, strlen(tmp)); + } + va_end(va); + + + return result; +} + + diff --git a/src/fs/CFile.hpp b/src/fs/CFile.hpp new file mode 100644 index 0000000..81fa9dd --- /dev/null +++ b/src/fs/CFile.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +class CFile { +public: + enum eOpenTypes { + ReadOnly, + WriteOnly, + ReadWrite, + Append + }; + + CFile(); + + CFile(const std::string &filepath, eOpenTypes mode); + + CFile(const uint8_t *memory, int32_t memsize); + + virtual ~CFile(); + + int32_t open(const std::string &filepath, eOpenTypes mode); + + int32_t open(const uint8_t *memory, int32_t memsize); + + int32_t isOpen() const { + if (iFd >= 0) + return true; + + if (mem_file) + return true; + + return false; + } + + void close(); + + int32_t read(uint8_t *ptr, size_t size); + + int32_t write(const uint8_t *ptr, size_t size); + + int32_t fwrite(const char *format, ...); + + int32_t seek(long int offset, int32_t origin); + + uint64_t tell() { + return pos; + }; + + uint64_t size() { + return filesize; + }; + + void rewind() { + this->seek(0, SEEK_SET); + }; + +protected: + int32_t iFd; + const uint8_t *mem_file; + uint64_t filesize; + uint64_t pos; +}; \ No newline at end of file diff --git a/src/fs/FSUtils.cpp b/src/fs/FSUtils.cpp new file mode 100644 index 0000000..abca9d8 --- /dev/null +++ b/src/fs/FSUtils.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include "FSUtils.h" +#include "CFile.hpp" + +#ifdef WIN32 +#include "../utils/dirent.h" +#endif + + +int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) { + //! always initialze input + *inbuffer = nullptr; + if (size) + *size = 0; + + +#ifdef _WIN32 + int32_t iFd = open(filepath, O_RDONLY|O_BINARY); +#else + int32_t iFd = open(filepath, O_RDONLY); +#endif + if (iFd < 0){ + return -1; + } + + uint32_t filesize = lseek(iFd, 0, SEEK_END); + lseek(iFd, 0, SEEK_SET); + + uint8_t *buffer = (uint8_t *) malloc(filesize); + if (buffer == nullptr) { + close(iFd); + return -2; + } + + uint32_t blocksize = 0x4000; + uint32_t done = 0; + int32_t readBytes = 0; + + while (done < filesize) { + if (done + blocksize > filesize) { + blocksize = filesize - done; + } + readBytes = read(iFd, buffer + done, blocksize); + if (readBytes <= 0) + break; + done += readBytes; + } + + close(iFd); + + if (done != filesize) { + free(buffer); + buffer = nullptr; + return -3; + } + + *inbuffer = buffer; + + //! sign is optional input + if (size) { + *size = filesize; + } + + return filesize; +} + +int32_t FSUtils::CheckFile(const char *filepath) { + if (!filepath) + return 0; + + struct stat filestat; + + char dirnoslash[strlen(filepath) + 2]; + snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath); + + while (dirnoslash[strlen(dirnoslash) - 1] == '/') + dirnoslash[strlen(dirnoslash) - 1] = '\0'; + + char *notRoot = strrchr(dirnoslash, '/'); + if (!notRoot) { + strcat(dirnoslash, "/"); + } + + if (stat(dirnoslash, &filestat) == 0) + return 1; + + return 0; +} + +int32_t FSUtils::CreateSubfolder(const char *fullpath) { + if (!fullpath) + return 0; + + int32_t result = 0; + + char dirnoslash[strlen(fullpath) + 1]; + strcpy(dirnoslash, fullpath); + + int32_t pos = strlen(dirnoslash) - 1; + while (dirnoslash[pos] == '/') { + dirnoslash[pos] = '\0'; + pos--; + } + + if (CheckFile(dirnoslash)) { + return 1; + } else { + char parentpath[strlen(dirnoslash) + 2]; + strcpy(parentpath, dirnoslash); + char *ptr = strrchr(parentpath, '/'); + + if (!ptr) { + //!Device root directory (must be with '/') + strcat(parentpath, "/"); + struct stat filestat; + if (stat(parentpath, &filestat) == 0) + return 1; + + return 0; + } + + ptr++; + ptr[0] = '\0'; + + result = CreateSubfolder(parentpath); + } + + if (!result) + return 0; + +#ifdef _WIN32 + if (mkdir(dirnoslash) == -1) { + return 0; + } +#else + if (mkdir(dirnoslash, 0777) == -1) { + return 0; + } +#endif + + return 1; +} + + +bool FSUtils::copyFile(const std::string &in, const std::string &out) { + // Using C++ buffers is **really** slow. Copying in 1023 byte chunks. + // Let's do it the old way. + size_t size; + + int source = open(in.c_str(), O_RDONLY, 0); + int dest = open(out.c_str(), 0x602, 0644); + if (source < 0) { + return false; + } + if (dest < 0) { + close(source); + return false; + } + + auto bufferSize = 1024 * 1024; + char *buf = (char *) malloc(bufferSize); + if (buf == NULL) { + return false; + } + + while ((size = read(source, buf, bufferSize)) > 0) { + write(dest, buf, size); + } + + free(buf); + + close(source); + close(dest); + return true; +} + +int32_t FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) { + CFile file(path, CFile::WriteOnly); + if (!file.isOpen()) { + return -1; + } + int32_t written = file.write((const uint8_t *) buffer, size); + file.close(); + return written; +} + diff --git a/src/fs/FSUtils.h b/src/fs/FSUtils.h new file mode 100644 index 0000000..f862c97 --- /dev/null +++ b/src/fs/FSUtils.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +class FSUtils { +public: + static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size); + + static int32_t CreateSubfolder(const char *fullpath); + + static int32_t CheckFile(const char *filepath); + + static bool copyFile(const std::string &in, const std::string &out); + + static int32_t saveBufferToFile(const char *path, void *buffer, uint32_t size); +}; \ No newline at end of file diff --git a/src/gui/GuiButton.cpp b/src/gui/GuiButton.cpp index dd79357..143e3cc 100644 --- a/src/gui/GuiButton.cpp +++ b/src/gui/GuiButton.cpp @@ -25,28 +25,28 @@ GuiButton::GuiButton(float w, float h) { width = w; height = h; - image = NULL; - imageOver = NULL; - imageHold = NULL; - imageClick = NULL; - icon = NULL; - iconOver = NULL; + image = nullptr; + imageOver = nullptr; + imageHold = nullptr; + imageClick = nullptr; + icon = nullptr; + iconOver = nullptr; for (int32_t i = 0; i < 4; i++) { - label[i] = NULL; - labelOver[i] = NULL; - labelHold[i] = NULL; - labelClick[i] = NULL; + label[i] = nullptr; + labelOver[i] = nullptr; + labelHold[i] = nullptr; + labelClick[i] = nullptr; } - for (int32_t i = 0; i < iMaxGuiTriggers; i++) { - trigger[i] = NULL; + for (auto &i : trigger) { + i = nullptr; } - soundOver = NULL; - soundHold = NULL; - soundClick = NULL; - clickedTrigger = NULL; - heldTrigger = NULL; + soundOver = nullptr; + soundHold = nullptr; + soundClick = nullptr; + clickedTrigger = nullptr; + heldTrigger = nullptr; selectable = true; holdable = false; clickable = true; @@ -55,8 +55,7 @@ GuiButton::GuiButton(float w, float h) { /** * Destructor for the GuiButton class. */ -GuiButton::~GuiButton() { -} +GuiButton::~GuiButton() = default; void GuiButton::setImage(GuiImage *img) { image = img; @@ -124,9 +123,9 @@ void GuiButton::setTrigger(GuiTrigger *t, int32_t idx) { if (idx >= 0 && idx < iMaxGuiTriggers) { trigger[idx] = t; } else { - for (int32_t i = 0; i < iMaxGuiTriggers; i++) { - if (!trigger[i]) { - trigger[i] = t; + for (auto &i : trigger) { + if (!i) { + i = t; break; } } @@ -134,8 +133,8 @@ void GuiButton::setTrigger(GuiTrigger *t, int32_t idx) { } void GuiButton::resetState() { - clickedTrigger = NULL; - heldTrigger = NULL; + clickedTrigger = nullptr; + heldTrigger = nullptr; GuiElement::resetState(); } @@ -213,21 +212,21 @@ void GuiButton::update(GuiController *c) { } } - for (int32_t i = 0; i < iMaxGuiTriggers; i++) { - if (!trigger[i]) { + for (auto & i : trigger) { + if (!i) { continue; } // button triggers if (clickable) { - int32_t isClicked = trigger[i]->clicked(c); + int32_t isClicked = i->clicked(c); if (!clickedTrigger && (isClicked != GuiTrigger::CLICKED_NONE) - && (trigger[i]->isClickEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { + && (i->isClickEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && i->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { if (soundClick) { soundClick->Play(); } - clickedTrigger = trigger[i]; + clickedTrigger = i; if (!isStateSet(STATE_CLICKED, c->chanIdx)) { if (isClicked == GuiTrigger::CLICKED_TOUCH) { @@ -237,38 +236,38 @@ void GuiButton::update(GuiController *c) { } } - clicked(this, c, trigger[i]); - } else if ((isStateSet(STATE_CLICKED, c->chanIdx) || isStateSet(STATE_CLICKED_TOUCH, c->chanIdx)) && (clickedTrigger == trigger[i]) && !isStateSet(STATE_HELD, c->chanIdx) && !trigger[i]->held(c) && - ((isClicked == GuiTrigger::CLICKED_NONE) || trigger[i]->released(c))) { + clicked(this, c, i); + } else if ((isStateSet(STATE_CLICKED, c->chanIdx) || isStateSet(STATE_CLICKED_TOUCH, c->chanIdx)) && (clickedTrigger == i) && !isStateSet(STATE_HELD, c->chanIdx) && !i->held(c) && + ((isClicked == GuiTrigger::CLICKED_NONE) || i->released(c))) { if ((isStateSet(STATE_CLICKED_TOUCH, c->chanIdx) && this->isInside(c->data.x, c->data.y)) || (isStateSet(STATE_CLICKED, c->chanIdx))) { clickedTrigger = nullptr; clearState(STATE_CLICKED, c->chanIdx); - released(this, c, trigger[i]); + released(this, c, i); } } } if (holdable) { - bool isHeld = trigger[i]->held(c); + bool isHeld = i->held(c); - if ((!heldTrigger || heldTrigger == trigger[i]) && isHeld - && (trigger[i]->isHoldEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { - heldTrigger = trigger[i]; + if ((!heldTrigger || heldTrigger == i) && isHeld + && (i->isHoldEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chanIdx) && i->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) { + heldTrigger = i; if (!isStateSet(STATE_HELD, c->chanIdx)) { setState(STATE_HELD, c->chanIdx); } - held(this, c, trigger[i]); - } else if (isStateSet(STATE_HELD, c->chanIdx) && (heldTrigger == trigger[i]) && (!isHeld || trigger[i]->released(c))) { + held(this, c, i); + } else if (isStateSet(STATE_HELD, c->chanIdx) && (heldTrigger == i) && (!isHeld || i->released(c))) { //! click is removed at this point and converted to held - if (clickedTrigger == trigger[i]) { + if (clickedTrigger == i) { clickedTrigger = nullptr; clearState(STATE_CLICKED, c->chanIdx); } heldTrigger = nullptr; clearState(STATE_HELD, c->chanIdx); - released(this, c, trigger[i]); + released(this, c, i); } } } diff --git a/src/gui/GuiController.h b/src/gui/GuiController.h index 8866593..f506eb2 100644 --- a/src/gui/GuiController.h +++ b/src/gui/GuiController.h @@ -61,7 +61,7 @@ public: int32_t y; } PadData; - int32_t chan; + uint32_t chan; int32_t chanIdx; PadData data; PadData lastData; diff --git a/src/gui/GuiElement.cpp b/src/gui/GuiElement.cpp index 1a5d516..cee2a97 100644 --- a/src/gui/GuiElement.cpp +++ b/src/gui/GuiElement.cpp @@ -33,11 +33,11 @@ GuiElement::GuiElement() { scaleX = 1.0f; scaleY = 1.0f; scaleZ = 1.0f; - for (int32_t i = 0; i < 5; i++) { - state[i] = STATE_DEFAULT; + for (unsigned int & i : state) { + i = STATE_DEFAULT; } stateChan = -1; - parentElement = NULL; + parentElement = nullptr; rumble = true; selectable = false; clickable = false; @@ -120,7 +120,7 @@ float GuiElement::getTop() { return y + yoffset; } -void GuiElement::setEffect(int32_t eff, int32_t amount, int32_t target) { +void GuiElement::setEffect(uint32_t eff, int32_t amount, int32_t target) { if (eff & EFFECT_SLIDE_IN) { // these calculations overcompensate a little if (eff & EFFECT_SLIDE_TOP) { @@ -163,7 +163,7 @@ void GuiElement::setEffect(int32_t eff, int32_t amount, int32_t target) { //!\param e Effect to enable //!\param a Amount of the effect (usage varies on effect) //!\param t Target amount of the effect (usage varies on effect) -void GuiElement::setEffectOnOver(int32_t e, int32_t a, int32_t t) { +void GuiElement::setEffectOnOver(uint32_t e, int32_t a, int32_t t) { effectsOver |= e; effectAmountOver = a; effectTargetOver = t; diff --git a/src/gui/GuiElement.h b/src/gui/GuiElement.h index e258679..e2d3df5 100644 --- a/src/gui/GuiElement.h +++ b/src/gui/GuiElement.h @@ -69,7 +69,7 @@ public: GuiElement(); //!Destructor - virtual ~GuiElement() {} + virtual ~GuiElement() = default; //!Set the element's parent //!\param e Pointer to parent element @@ -105,7 +105,7 @@ public: return zParent + zoffset; } - virtual float getCenterX(void) { + virtual float getCenterX() { float pCenterX = 0.0f; if (parentElement) { @@ -138,7 +138,7 @@ public: return pCenterX; } - virtual float getCenterY(void) { + virtual float getCenterY() { float pCenterY = 0.0f; if (parentElement) { @@ -261,7 +261,7 @@ public: //!Sets the element's state //!\param s State (STATE_DEFAULT, STATE_SELECTED, STATE_CLICKED, STATE_DISABLED) //!\param c Controller channel (0-3, -1 = none) - virtual void setState(int32_t s, int32_t c = -1) { + virtual void setState(uint32_t s, int32_t c) { if (c >= 0 && c < 5) { state[c] |= s; } else { @@ -273,24 +273,24 @@ public: stateChanged(this, s, c); } - virtual void clearState(int32_t s, int32_t c = -1) { + virtual void clearState(uint32_t s, int32_t c) { if (c >= 0 && c < 5) { state[c] &= ~s; } else { - for (int32_t i = 0; i < 5; i++) { - state[i] &= ~s; + for (unsigned int & i : state) { + i &= ~s; } } stateChan = c; stateChanged(this, s, c); } - virtual bool isStateSet(int32_t s, int32_t c = -1) const { + virtual bool isStateSet(uint32_t s, int32_t c = -1) const { if (c >= 0 && c < 5) { return (state[c] & s) != 0; } else { - for (int32_t i = 0; i < 5; i++) { - if ((state[i] & s) != 0) { + for (unsigned int i : state) { + if ((i & s) != 0) { return true; } } @@ -301,7 +301,7 @@ public: //!Gets the element's current state //!\return state - virtual int32_t getState(int32_t c = 0) { + virtual int32_t getState(int32_t c) { return state[c]; }; @@ -313,8 +313,8 @@ public: //!Resets the element's state to STATE_DEFAULT virtual void resetState() { - for (int32_t i = 0; i < 5; i++) { - state[i] = STATE_DEFAULT; + for (unsigned int & i : state) { + i = STATE_DEFAULT; } stateChan = -1; } @@ -434,13 +434,13 @@ public: //!\param e Effect to enable //!\param a Amount of the effect (usage varies on effect) //!\param t Target amount of the effect (usage varies on effect) - virtual void setEffect(int32_t e, int32_t a, int32_t t = 0); + virtual void setEffect(uint32_t e, int32_t a, int32_t t); //!Sets an effect to be enabled on wiimote cursor over //!\param e Effect to enable //!\param a Amount of the effect (usage varies on effect) //!\param t Target amount of the effect (usage varies on effect) - virtual void setEffectOnOver(int32_t e, int32_t a, int32_t t = 0); + virtual void setEffectOnOver(uint32_t e, int32_t a, int32_t t); //!Shortcut to SetEffectOnOver(EFFECT_SCALE, 4, 110) virtual void setEffectGrow() { @@ -466,10 +466,30 @@ public: //!\param y Y coordinate //!\return true if contained within, false otherwise virtual bool isInside(float x, float y) { - return (x > (this->getCenterX() - getScaleX() * getWidth() * 0.5f) - && x < (this->getCenterX() + getScaleX() * getWidth() * 0.5f) - && y > (this->getCenterY() - getScaleY() * getHeight() * 0.5f) - && y < (this->getCenterY() + getScaleY() * getHeight() * 0.5f)); + float rotatedX = x; + float rotatedY = y; + + if (getAngle() != 0.f) { + // translate input point + float tempX = x - getCenterX(); + float tempY = y - getCenterY(); + + // Conver to Rad + auto angleInRad = (float) ((getAngle() * -1.0f) * M_PI / 180.0f); + + // now apply rotation + rotatedX = tempX * cos((angleInRad)) - tempY * sin(angleInRad); + rotatedY = tempX * sin(angleInRad) + tempY * cos(angleInRad); + + // translate back + rotatedX = rotatedX + getCenterX(); + rotatedY = rotatedY + getCenterY(); + } + + return (rotatedX > (this->getCenterX() - getScaleX() * getWidth() * 0.5f) + && rotatedX < (this->getCenterX() + getScaleX() * getWidth() * 0.5f) + && rotatedY > (this->getCenterY() - getScaleY() * getHeight() * 0.5f) + && rotatedY < (this->getCenterY() + getScaleY() * getHeight() * 0.5f)); } //!Sets the element's position @@ -586,8 +606,8 @@ protected: float scaleX; //!< Element scale (1 = 100%) float scaleY; //!< Element scale (1 = 100%) float scaleZ; //!< Element scale (1 = 100%) - int32_t alignment; //!< Horizontal element alignment, respective to parent element - int32_t state[5]; //!< Element state (DEFAULT, SELECTED, CLICKED, DISABLED) + uint32_t alignment; //!< Horizontal element alignment, respective to parent element + uint32_t state[5]{}; //!< Element state (DEFAULT, SELECTED, CLICKED, DISABLED) int32_t stateChan; //!< Which controller channel is responsible for the last change in state GuiElement *parentElement; //!< Parent element @@ -596,10 +616,10 @@ protected: int32_t yoffsetDyn; //!< Element Y offset, dynamic (added to yoffset value for animation effects) float alphaDyn; //!< Element alpha, dynamic (multiplied by alpha value for blending/fading effects) float scaleDyn; //!< Element scale, dynamic (multiplied by alpha value for blending/fading effects) - int32_t effects; //!< Currently enabled effect(s). 0 when no effects are enabled + uint32_t effects; //!< Currently enabled effect(s). 0 when no effects are enabled int32_t effectAmount; //!< Effect amount. Used by different effects for different purposes int32_t effectTarget; //!< Effect target amount. Used by different effects for different purposes - int32_t effectsOver; //!< Effects to enable when wiimote cursor is over this element. Copied to effects variable on over event + uint32_t effectsOver; //!< Effects to enable when wiimote cursor is over this element. Copied to effects variable on over event int32_t effectAmountOver; //!< EffectAmount to set when wiimote cursor is over this element int32_t effectTargetOver; //!< EffectTarget to set when wiimote cursor is over this element }; diff --git a/src/gui/GuiFrame.cpp b/src/gui/GuiFrame.cpp index e699946..a87b6d2 100644 --- a/src/gui/GuiFrame.cpp +++ b/src/gui/GuiFrame.cpp @@ -16,6 +16,7 @@ ****************************************************************************/ #include "GuiFrame.h" #include "../system/video/Renderer.h" +#include "../utils/logger.h" GuiFrame::GuiFrame(GuiFrame *p) { parent = p; @@ -48,9 +49,10 @@ GuiFrame::~GuiFrame() { } void GuiFrame::append(GuiElement *e) { - if (e == NULL) { + if (e == nullptr) { return; } + DEBUG_FUNCTION_LINE("append %08X", e); remove(e); mutex.lock(); @@ -60,7 +62,7 @@ void GuiFrame::append(GuiElement *e) { } void GuiFrame::insert(GuiElement *e, uint32_t index) { - if (e == NULL || (index >= elements.size())) { + if (e == nullptr || (index >= elements.size())) { return; } @@ -94,7 +96,7 @@ void GuiFrame::removeAll() { GuiElement *GuiFrame::getGuiElementAt(uint32_t index) const { if (index >= elements.size()) { - return NULL; + return nullptr; } return elements[index]; @@ -108,13 +110,13 @@ void GuiFrame::resetState() { GuiElement::resetState(); mutex.lock(); - for (uint32_t i = 0; i < elements.size(); ++i) { - elements[i]->resetState(); + for (auto & element : elements) { + element->resetState(); } mutex.unlock(); } -void GuiFrame::setState(int32_t s, int32_t c) { +void GuiFrame::setState(uint32_t s, int32_t c) { GuiElement::setState(s, c); mutex.lock(); for (uint32_t i = 0; i < elements.size(); ++i) { @@ -123,7 +125,7 @@ void GuiFrame::setState(int32_t s, int32_t c) { mutex.unlock(); } -void GuiFrame::clearState(int32_t s, int32_t c) { +void GuiFrame::clearState(uint32_t s, int32_t c) { GuiElement::clearState(s, c); mutex.lock(); @@ -137,8 +139,8 @@ void GuiFrame::setVisible(bool v) { visible = v; mutex.lock(); - for (uint32_t i = 0; i < elements.size(); ++i) { - elements[i]->setVisible(v); + for (auto & element : elements) { + element->setVisible(v); } mutex.unlock(); } diff --git a/src/gui/GuiFrame.h b/src/gui/GuiFrame.h index 6297660..0488239 100644 --- a/src/gui/GuiFrame.h +++ b/src/gui/GuiFrame.h @@ -76,9 +76,9 @@ public: //!Sets the window's state //!\param s State - void setState(int32_t s, int32_t c = -1) override; + void setState(uint32_t s, int32_t c = -1) override; - void clearState(int32_t s, int32_t c = -1) override; + void clearState(uint32_t s, int32_t c = -1) override; //!Gets the index of the GuiElement inside the window that is currently selected //!\return index of selected GuiElement diff --git a/src/gui/GuiImage.cpp b/src/gui/GuiImage.cpp index 36ff4ff..cd5ee0e 100644 --- a/src/gui/GuiImage.cpp +++ b/src/gui/GuiImage.cpp @@ -15,11 +15,40 @@ * along with this program. If not, see . ****************************************************************************/ #include -#include #include "GuiImage.h" -#include "../system/SDLSystem.h" +#include "../utils/logger.h" -GuiImage::GuiImage(const std::string& path) : GuiTexture(path){ +GuiImage::GuiImage(GuiTextureData *texture) { + setTexture(texture); } -GuiImage::~GuiImage() = default; +GuiImage::~GuiImage() { + if(this->texture && freeTextureData){ + delete this->texture; + } +} + +void GuiImage::draw(Renderer *renderer) { + if (!this->isVisible()) { + return; + } + + if (texture) { + SDL_Rect rect; + rect.x = (int) getLeft(); + rect.y = (int) getTop(); + rect.w = (int) (getScaleX() * getWidth()); + rect.h = (int) (getScaleY() * getHeight()); + texture->draw(renderer, rect, getAngle()); + } +} + +void GuiImage::setTexture(GuiTextureData *tex) { + if (tex) { + if(this->texture && freeTextureData){ + delete this->texture; + } + this->texture = tex; + this->setSize(tex->getWidth(), tex->getHeight()); + } +} diff --git a/src/gui/GuiImage.h b/src/gui/GuiImage.h index 36bad71..05b5e39 100644 --- a/src/gui/GuiImage.h +++ b/src/gui/GuiImage.h @@ -18,15 +18,24 @@ #include #include "GuiElement.h" -#include "GuiTexture.h" +#include "GuiTextureData.h" //!Display, manage, and manipulate images in the GUI -class GuiImage : public GuiTexture { +class GuiImage : public GuiElement { public: //!\overload //!\param img Pointer to GuiImageData element - explicit GuiImage(const std::string &path); + GuiImage() = default; + + explicit GuiImage(GuiTextureData *texture); //!Destructor ~GuiImage() override; + + void draw(Renderer *r) override; + + void setTexture(GuiTextureData *tex); +private: + GuiTextureData *texture = nullptr; + bool freeTextureData = false; }; diff --git a/src/gui/GuiSound.cpp b/src/gui/GuiSound.cpp index 3419333..1a633c7 100644 --- a/src/gui/GuiSound.cpp +++ b/src/gui/GuiSound.cpp @@ -15,64 +15,65 @@ * along with this program. If not, see . ****************************************************************************/ #include "GuiSound.h" -#include "../utils/logger.h" + +GuiSound::GuiSound(void *buffer, uint32_t filesize, bool freeSrc) { + SDL_RWops *rw = SDL_RWFromMem(buffer, filesize); + music = Mix_LoadWAV_RW(rw, freeSrc); +} GuiSound::GuiSound(const char *filepath) { Load(filepath); } GuiSound::~GuiSound() { - if(music){ + if (music) { Mix_FreeChunk(music); + music = nullptr; } } - bool GuiSound::Load(const char *filepath) { music = Mix_LoadWAV(filepath); - DEBUG_FUNCTION_LINE("load %s %d", filepath, music); return music != nullptr; } - - void GuiSound::Play() { - playedOn = Mix_PlayChannel(-1,music, loops); + if (music) { + playedOn = Mix_PlayChannel(-1, music, loops); + } } -void GuiSound::Stop() { +void GuiSound::Stop() const { Pause(); } -void GuiSound::Pause() { - Mix_HaltChannel(playedOn); +void GuiSound::Pause() const { + if (playedOn != -1) { + Mix_HaltChannel(playedOn); + } } void GuiSound::Resume() { Play(); } -bool GuiSound::IsPlaying() { - if(playedOn == -1){ +bool GuiSound::IsPlaying() const { + if (playedOn == -1) { return false; } return Mix_Playing(playedOn); } -void GuiSound::SetVolume(uint32_t vol) { - if(music != nullptr){ - Mix_VolumeChunk(music, vol); - } +void GuiSound::SetVolume(uint32_t vol) const { + if (music) { Mix_VolumeChunk(music, vol); } } void GuiSound::SetLoop(bool l) { - if(l){ - loops = -1; - }else{ - loops = 1; - } + // < 0 == infinitive loop + loops = l ? -1 : 1; } -void GuiSound::Rewind() { +void GuiSound::Rewind() const { + // TODO: how to rewind? Stop(); } diff --git a/src/gui/GuiSound.h b/src/gui/GuiSound.h index 2664cfc..cada45d 100644 --- a/src/gui/GuiSound.h +++ b/src/gui/GuiSound.h @@ -22,10 +22,12 @@ //!Sound conversion and playback. A wrapper for other sound libraries - ASND, libmad, ltremor, etc class GuiSound : public GuiElement { public: + explicit GuiSound(const char *filepath); + //!Constructor //!\param sound Pointer to the sound data //!\param filesize Length of sound data - explicit GuiSound(const char *filepath); + GuiSound(void *buffer, uint32_t filesize, bool freeSrc = false); //!Destructor ~GuiSound() override; @@ -37,29 +39,29 @@ public: void Play(); //!Stop sound playback - void Stop(); + void Stop() const; //!Pause sound playback - void Pause(); + void Pause() const; //!Resume sound playback void Resume(); //!Checks if the sound is currently playing //!\return true if sound is playing, false otherwise - bool IsPlaying(); + [[nodiscard]] bool IsPlaying() const; //!Rewind the music - void Rewind(); + void Rewind() const; //!Set sound volume //!\param v Sound volume (0-100) - void SetVolume(uint32_t v); + void SetVolume(uint32_t v) const; //!\param l Loop (true to loop) void SetLoop(bool l); - Mix_Chunk *music; + Mix_Chunk *music = nullptr; int32_t loops = 0; int32_t playedOn = -1; }; \ No newline at end of file diff --git a/src/gui/GuiText.cpp b/src/gui/GuiText.cpp index b712189..0873a43 100644 --- a/src/gui/GuiText.cpp +++ b/src/gui/GuiText.cpp @@ -26,16 +26,12 @@ GuiText::GuiText(const std::string& text, SDL_Color c, FC_Font* gFont) { this->text = text; this->color = c; this->fc_font = gFont; - updateSize(); this->doUpdateTexture = true; + this->texture.setParent(this); } -GuiText::~GuiText(){ - if(fc_font){ - FC_FreeFont(fc_font); - fc_font = nullptr; - } - delete texture; +GuiText::~GuiText() { + delete textureData; } void GuiText::draw(Renderer *renderer) { @@ -45,9 +41,7 @@ void GuiText::draw(Renderer *renderer) { updateTexture(renderer); - if(texture){ - texture->draw(renderer); - } + texture.draw(renderer); } void GuiText::process() { @@ -57,7 +51,7 @@ void GuiText::process() { void GuiText::setMaxWidth(float width) { this->maxWidth = width; - // Rebuild the texture cache. + // Rebuild the texture cache on next draw doUpdateTexture = true; } @@ -69,20 +63,23 @@ void GuiText::updateSize() { } void GuiText::updateTexture(Renderer *renderer) { - if(!texture || doUpdateTexture) { + if(doUpdateTexture) { updateSize(); + int tex_width = tex_width = width == 0 ? 1 : (int) width; + int tex_height = tex_height = height == 0 ? 1 : (int)height; - SDL_Texture *temp = SDL_CreateTexture(renderer->getRenderer(), renderer->getPixelFormat(), SDL_TEXTUREACCESS_TARGET, (int) width, (int) height); + SDL_Texture *temp = SDL_CreateTexture(renderer->getRenderer(), renderer->getPixelFormat(), SDL_TEXTUREACCESS_TARGET, tex_width, tex_height); if (temp) { - delete texture; - texture = new GuiTexture(temp); - texture->setParent(this); - texture->setBlendMode(SDL_BLENDMODE_BLEND); + texture.setTexture(nullptr); + delete textureData; + textureData = new GuiTextureData(temp); + textureData->setBlendMode(SDL_BLENDMODE_BLEND); + texture.setTexture(textureData); FC_DrawColumnToTexture(fc_font, temp, 0, 0, maxWidth, text.c_str()); - doUpdateTexture = false; } else { DEBUG_FUNCTION_LINE("Failed to create texture"); } + doUpdateTexture = false; } } diff --git a/src/gui/GuiText.h b/src/gui/GuiText.h index 83a292b..9fe139f 100644 --- a/src/gui/GuiText.h +++ b/src/gui/GuiText.h @@ -17,8 +17,9 @@ #pragma once #include "GuiElement.h" -#include "GuiTexture.h" +#include "GuiTextureData.h" #include "../system/video/SDL_FontCache.h" +#include "GuiImage.h" #include #include @@ -39,7 +40,9 @@ public: void setMaxWidth(float width); protected: - GuiTexture* texture = nullptr; + GuiImage texture; + GuiTextureData* textureData = nullptr; + std::string text; SDL_Color color; FC_Font *fc_font = nullptr; diff --git a/src/gui/GuiTexture.cpp b/src/gui/GuiTexture.cpp deleted file mode 100644 index db2061e..0000000 --- a/src/gui/GuiTexture.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include "GuiTexture.h" -#include "../system/SDLSystem.h" -#include "../utils/logger.h" - -GuiTexture::GuiTexture(const std::string& path) { - imgSurface = IMG_Load( path.c_str() ); - - if(!imgSurface){ - return; - } - this->width = imgSurface->w; - this->height = imgSurface->h; -} - -GuiTexture::GuiTexture(SDL_Texture *texture) { - if (this->texture) { - SDL_DestroyTexture(this->texture); - this->texture = NULL; - } - this->texture = texture; - int w, h; - SDL_QueryTexture(texture, nullptr, nullptr, &w, &h); - this->setSize(w, h); - -} - -GuiTexture::GuiTexture(SDL_Surface *pSurface) { - if(!pSurface){ - return; - } - imgSurface = pSurface; - - this->width = imgSurface->w; - this->height = imgSurface->h; -} - -/** - * Destructor for the GuiImage class. - */ -GuiTexture::~GuiTexture() { - if (this->imgSurface) { - SDL_FreeSurface(this->imgSurface); - this->imgSurface = nullptr; - } - if (this->texture) { - SDL_DestroyTexture(this->texture); - this->texture = nullptr; - } -} - -void GuiTexture::draw(Renderer *renderer) { - if (!this->isVisible()) { - DEBUG_FUNCTION_LINE("not visible!"); - return; - } - - if (texture == NULL && imgSurface) { - SDL_Surface *optimizedSurface = SDL_ConvertSurfaceFormat(imgSurface, renderer->getPixelFormat(), 0); - if (optimizedSurface != NULL) { - SDL_FreeSurface(imgSurface); - imgSurface = optimizedSurface; - DEBUG_FUNCTION_LINE("Optimized surface"); - } - texture = SDL_CreateTextureFromSurface(renderer->getRenderer(), imgSurface); - } - if (!texture) { - DEBUG_FUNCTION_LINE("no texture!"); - return; - } - - float currScaleX = getScaleX(); - float currScaleY = getScaleY(); - - SDL_Rect rect; - rect.x = getLeft(); - rect.y = getTop(); - rect.w = currScaleX * getWidth(); - rect.h = currScaleY * getHeight(); - - if (getAngle() == 0) { - SDL_RenderCopy(renderer->getRenderer(), texture, nullptr, &rect); - } else { - SDL_RenderCopyEx(renderer->getRenderer(), texture, nullptr, &rect, getAngle(), nullptr, SDL_FLIP_NONE); - } -} - -int GuiTexture::setBlendMode(SDL_BlendMode) { - if(texture){ - return SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); - } - return SDL_BLENDMODE_INVALID; -} diff --git a/src/gui/GuiTexture.h b/src/gui/GuiTexture.h deleted file mode 100644 index 36b1d9c..0000000 --- a/src/gui/GuiTexture.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include -#include "GuiElement.h" - -class GuiTexture : public GuiElement { -public: - explicit GuiTexture(const std::string &path); - explicit GuiTexture(SDL_Texture * texture); - explicit GuiTexture(SDL_Surface *pSurface); - -//!Destructor - ~GuiTexture() override; - - //!Constantly called to draw the image - void draw(Renderer *pVideo) override; - - int setBlendMode(SDL_BlendMode blendMode); - -protected: - SDL_Surface *imgSurface = nullptr; - SDL_Texture *texture = nullptr; -}; diff --git a/src/gui/GuiTextureData.cpp b/src/gui/GuiTextureData.cpp new file mode 100644 index 0000000..d93f14f --- /dev/null +++ b/src/gui/GuiTextureData.cpp @@ -0,0 +1,87 @@ +#include +#include "GuiTextureData.h" +#include "../system/SDLSystem.h" +#include "../utils/logger.h" + +GuiTextureData::GuiTextureData(const std::string& path) { + SDL_Surface *surface = IMG_Load_RW(SDL_RWFromFile(path.c_str(), "rb"), 1); + loadSurface(surface); +} + +GuiTextureData::GuiTextureData(void *buffer, const uint32_t filesize, bool freesrc) { + SDL_RWops *rw = SDL_RWFromMem(buffer, filesize); + SDL_Surface *surface = IMG_Load_RW(rw, freesrc); + loadSurface(surface); +} + +GuiTextureData::GuiTextureData(SDL_Texture *texture) { + if (this->texture) { + SDL_DestroyTexture(this->texture); + this->texture = nullptr; + } + this->texture = texture; + int w, h; + SDL_QueryTexture(this->texture, nullptr, nullptr, &w, &h); + this->width = w; + this->height = h; + +} + + + +void GuiTextureData::loadSurface(SDL_Surface *pSurface) { + if(!pSurface){ + return; + } + + cleanUp(); + + imgSurface = pSurface; + + this->width = imgSurface->w; + this->height = imgSurface->h; +} + +void GuiTextureData::cleanUp() { + if (imgSurface) { + SDL_FreeSurface(imgSurface); + imgSurface = nullptr; + } + if (texture) { + SDL_DestroyTexture(texture); + texture = nullptr; + } +} + +/** + * Destructor for the GuiImage class. + */ +GuiTextureData::~GuiTextureData() { + cleanUp(); +} + +void GuiTextureData::draw(Renderer *renderer, const SDL_Rect& dest, float angle) { + if (texture == nullptr && imgSurface) { + SDL_Surface *optimizedSurface = SDL_ConvertSurfaceFormat(imgSurface, renderer->getPixelFormat(), 0); + if (optimizedSurface != nullptr) { + SDL_FreeSurface(imgSurface); + imgSurface = optimizedSurface; + } + texture = SDL_CreateTextureFromSurface(renderer->getRenderer(), imgSurface); + } + if (!texture) { + DEBUG_FUNCTION_LINE("no texture!"); + return; + } + + if (angle == 0) { + SDL_RenderCopy(renderer->getRenderer(), texture, nullptr, &dest); + } else { + SDL_RenderCopyEx(renderer->getRenderer(), texture, nullptr, &dest, angle, nullptr, SDL_FLIP_NONE); + } +} + +int GuiTextureData::setBlendMode(SDL_BlendMode) { + if(texture){ return SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); } + return SDL_BLENDMODE_INVALID; +} \ No newline at end of file diff --git a/src/gui/GuiTextureData.h b/src/gui/GuiTextureData.h new file mode 100644 index 0000000..3f11b24 --- /dev/null +++ b/src/gui/GuiTextureData.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "GuiElement.h" + +class GuiTextureData { +public: + GuiTextureData(void *buffer, uint32_t filesize, bool freesrc = false); + + explicit GuiTextureData(SDL_Texture *texture); + + explicit GuiTextureData(const std::string &path); + + //!Destructor + ~GuiTextureData(); + + void draw(Renderer *pVideo, const SDL_Rect &rect, float angle); + + int setBlendMode(SDL_BlendMode blendMode); + + [[nodiscard]] int32_t getWidth() const { + return width; + } + + [[nodiscard]] int32_t getHeight() const { + return height; + } + + +protected: + void loadSurface(SDL_Surface *pSurface); + + SDL_Surface *imgSurface = nullptr; + + void cleanUp(); + + SDL_Texture *texture = nullptr; + int32_t width = 0; + int32_t height = 0; +}; diff --git a/src/gui/GuiTrigger.cpp b/src/gui/GuiTrigger.cpp index 3b6a9a8..23f3ecd 100644 --- a/src/gui/GuiTrigger.cpp +++ b/src/gui/GuiTrigger.cpp @@ -14,10 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ -#include -#include "GuiElement.h" #include "GuiController.h" -#include "GuiTrigger.h" /** * Constructor for the GuiTrigger class. @@ -33,8 +30,7 @@ GuiTrigger::GuiTrigger(uint32_t ch, uint32_t btn, bool clickEverywhere, bool hol /** * Destructor for the GuiTrigger class. */ -GuiTrigger::~GuiTrigger() { -} +GuiTrigger::~GuiTrigger() = default; /** * Sets a simple trigger. Requires: @@ -93,6 +89,7 @@ int32_t GuiTrigger::clicked(const GuiController *controller) const { int32_t bResult = CLICKED_NONE; + if (controller->data.touched && controller->data.validPointer && (btns & TOUCHED) && !controller->lastData.touched) { bResult = CLICKED_TOUCH; } diff --git a/src/gui/GuiTrigger.h b/src/gui/GuiTrigger.h index 98507bc..6134d1d 100644 --- a/src/gui/GuiTrigger.h +++ b/src/gui/GuiTrigger.h @@ -20,7 +20,7 @@ class GuiController; -//!Menu input trigger management. Determine if action is neccessary based on input data by comparing controller input data to a specific trigger element. +//!Menu input trigger management. Determine if action is necessary based on input data by comparing controller input data to a specific trigger element. class GuiTrigger { public: enum eClicked { @@ -96,15 +96,15 @@ public: bSelectionClickEverywhere = b; } - bool isClickEverywhere() const { + [[nodiscard]] bool isClickEverywhere() const { return bClickEverywhere; } - bool isHoldEverywhere() const { + [[nodiscard]] bool isHoldEverywhere() const { return bHoldEverywhere; } - bool isSelectionClickEverywhere() const { + [[nodiscard]] bool isSelectionClickEverywhere() const { return bSelectionClickEverywhere; } diff --git a/src/input/SDLControllerMouse.h b/src/input/SDLControllerMouse.h index e3d1a43..80cea96 100644 --- a/src/input/SDLControllerMouse.h +++ b/src/input/SDLControllerMouse.h @@ -7,15 +7,19 @@ public: } + void before() override{ + SDLController::before(); + data.validPointer = true; + } + virtual bool update(SDL_Event *e, int32_t screenWidth, int32_t screenHeight) override { if (e->type == SDL_MOUSEMOTION) { data.y = e->motion.y; data.x = e->motion.x; - data.validPointer = true; } else if (e->type == SDL_MOUSEBUTTONDOWN && e->button.button == SDL_BUTTON_LEFT) { - data.buttons_h |= GuiTrigger::TOUCHED; + data.touched = true; } else if (e->type == SDL_MOUSEBUTTONUP && e->button.button == SDL_BUTTON_LEFT) { - data.buttons_h &= ~GuiTrigger::TOUCHED; + data.touched = false; }else{ DEBUG_FUNCTION_LINE("Unknown event"); return false; diff --git a/src/input/SDLControllerWiiUGamepad.h b/src/input/SDLControllerWiiUGamepad.h index d2972e3..352bf44 100644 --- a/src/input/SDLControllerWiiUGamepad.h +++ b/src/input/SDLControllerWiiUGamepad.h @@ -26,16 +26,14 @@ public: } bool update(SDL_Event *e, int32_t screenWidth, int32_t screenHeight) override { - if (e->type == SDL_FINGERMOTION) { + if (e->type == SDL_FINGERMOTION || e->type == SDL_FINGERUP || e->type == SDL_FINGERDOWN) { data.y = e->tfinger.y * screenHeight; - data.x = e->tfinger.x * screenWidth;; - data.validPointer = true; - } else if (e->type == SDL_FINGERUP) { - data.validPointer = false; - data.buttons_h &= ~GuiTrigger::TOUCHED; - } else if (e->type == SDL_FINGERDOWN) { - data.validPointer = true; - data.buttons_h |= GuiTrigger::TOUCHED; + data.x = e->tfinger.x * screenWidth; + if (e->type == SDL_FINGERUP) { + data.touched = false; + } else if (e->type == SDL_FINGERDOWN) { + data.touched = true; + } } else if (e->type == SDL_JOYBUTTONDOWN) { data.buttons_h |= vpad_button_map[e->jbutton.button]; } else if (e->type == SDL_JOYBUTTONUP) { @@ -46,5 +44,10 @@ public: } return true; } + + void after() override { + data.validPointer = data.touched; + SDLController::after(); + } }; diff --git a/src/main.cpp b/src/main.cpp index d5ae0a2..c575c45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,23 +1,16 @@ #include #include "system/SDLSystem.h" #include "gui/GuiFrame.h" -#include "gui/GuiImage.h" #include "gui/GuiButton.h" #include "gui/GuiController.h" #include "menu/MainWindow.h" -#include "utils/logger.h" #include "input/SDLController.h" #include "input/SDLControllerMouse.h" -#include "input/SDLControllerWiiUGamepad.h" -#include "input/SDLControllerXboxOne.h" -#include "input/SDLControllerWiiUProContoller.h" -#include "input/SDLControllerJoystick.h" #include "input/ControllerManager.h" #include #include -#include #if defined _WIN32 #include diff --git a/src/menu/MainWindow.cpp b/src/menu/MainWindow.cpp index 65ffe61..1e0798d 100644 --- a/src/menu/MainWindow.cpp +++ b/src/menu/MainWindow.cpp @@ -1,57 +1,91 @@ #include "MainWindow.h" +#include "../resources/Resources.h" MainWindow::~MainWindow() { - delete label;; - delete touchTrigger;; - delete sound;; - delete image;; - delete image2;; - delete image3;; - delete image4;; - delete image5;; - delete image;; - delete label;; - delete button;; - delete bgMusic;; + delete label; + delete touchTrigger; + delete buttonTrigger; + delete sound; + delete image; + delete image2; + delete image3; + delete image4; + delete image5; + delete image; + delete label; + delete button; + delete bgMusic; } MainWindow::MainWindow(int32_t w, int32_t h, Renderer* renderer) : GuiFrame(w, h) { #if defined _WIN32 - auto picture_path = "test.png"; + Resources::LoadFiles("."); +#endif + auto picture_path = "button.png"; auto font_path = "FreeSans.ttf"; auto bgMusic_path = "bgMusic.ogg"; auto music_click = "button_click.mp3"; -#else - auto picture_path = "fs:/vol/external01/test.png"; - auto font_path = "fs:/vol/external01/FreeSans.ttf"; - auto bgMusic_path = "fs:/vol/external01/bgMusic.ogg"; - auto music_click = "fs:/vol/external01/button_click.mp3"; -#endif TTF_Init(); TTF_Font *font; - font = TTF_OpenFont(font_path, 28); + SDL_RWops *rw = SDL_RWFromMem((void *) Resources::GetFile(font_path), Resources::GetFileSize(font_path)); + + DEBUG_FUNCTION_LINE("load font %08X %d", Resources::GetFile(font_path), Resources::GetFileSize(font_path)); + + font = TTF_OpenFontRW(rw, 0, 28); + if(!font){ + DEBUG_FUNCTION_LINE("Failed to load the font"); + return; + } FC_Font* fc_font = FC_CreateFont(); if(!fc_font){ DEBUG_FUNCTION_LINE("Failed to create font"); } - FC_LoadFontFromTTF(fc_font, renderer->getRenderer(), font, {255, 255, 255, 255}); + auto res = FC_LoadFontFromTTF(fc_font, renderer->getRenderer(), font, {255, 255, 255, 255}); + DEBUG_FUNCTION_LINE("FontCache init %d", res); - label = new GuiText("This is a test.This is a test. This is a test.This is a test.This is a test.This is a test.", {255, 255, 0, 255}, fc_font); + label = new GuiText("This is a test.", {255, 255, 0, 255}, fc_font); - bgMusic = new GuiSound(bgMusic_path); + + bgMusic = Resources::GetSound(bgMusic_path); + if(!bgMusic){ + DEBUG_FUNCTION_LINE("Failed to load %s", bgMusic_path); + return; + } bgMusic->SetLoop(true); bgMusic->Play(); - image = new GuiImage(picture_path); - image2 = new GuiImage(picture_path); - image3 = new GuiImage(picture_path); - image4 = new GuiImage(picture_path); - image5 = new GuiImage(picture_path); + image = new GuiImage(Resources::GetTexture(picture_path)); + image2 = new GuiImage(Resources::GetTexture(picture_path)); + image3 = new GuiImage(Resources::GetTexture(picture_path)); + image4 = new GuiImage(Resources::GetTexture(picture_path)); + image5 = new GuiImage(Resources::GetTexture(picture_path)); + if(!image){ + DEBUG_FUNCTION_LINE("Failed to add image"); + return; + } + if(!image2){ + DEBUG_FUNCTION_LINE("Failed to add image"); + return; + } + if(!image3){ + DEBUG_FUNCTION_LINE("Failed to add image"); + return; + } + if(!image4){ + DEBUG_FUNCTION_LINE("Failed to add image"); + return; + } + if(!image5){ + DEBUG_FUNCTION_LINE("Failed to add image"); + return; + } + + DEBUG_FUNCTION_LINE("%d", image5->getWidth()); button = new GuiButton(image5->getWidth(), image5->getHeight()); this->setAlignment(ALIGN_TOP_LEFT); @@ -69,11 +103,12 @@ MainWindow::MainWindow(int32_t w, int32_t h, Renderer* renderer) : GuiFrame(w, h button->setAlignment(ALIGN_CENTERED); button->setImage(image5); - sound = new GuiSound(music_click); + sound = Resources::GetSound(music_click); touchTrigger = new GuiTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::TOUCHED); - touchTrigger = new GuiTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::TOUCHED); + buttonTrigger = new GuiTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::BUTTON_A, true); button->setTrigger(touchTrigger); + button->setTrigger(buttonTrigger); button->setEffectGrow(); label->setAlignment(ALIGN_CENTERED); button->setLabel(label); @@ -86,8 +121,11 @@ MainWindow::MainWindow(int32_t w, int32_t h, Renderer* renderer) : GuiFrame(w, h void MainWindow::process() { GuiFrame::process(); + if(!button){ + return; + } // Rotate the button for fun. - auto res = button->getAngle() + 1; + auto res = button->getAngle() + 0.1f; if(res > 360){ res = 0; } diff --git a/src/menu/MainWindow.h b/src/menu/MainWindow.h index fcc9df7..925b249 100644 --- a/src/menu/MainWindow.h +++ b/src/menu/MainWindow.h @@ -4,6 +4,7 @@ #include "../gui/GuiFrame.h" #include "../gui/GuiButton.h" #include "../utils/logger.h" +#include "../gui/GuiImage.h" class MainWindow : public GuiFrame, public sigslot::has_slots<> { public: @@ -16,6 +17,7 @@ public: private: GuiText *label = nullptr; GuiTrigger *touchTrigger = nullptr; + GuiTrigger *buttonTrigger = nullptr; GuiSound *sound = nullptr; GuiImage *image = nullptr; GuiImage *image2 = nullptr; diff --git a/src/resources/Resources.cpp b/src/resources/Resources.cpp new file mode 100644 index 0000000..b682bbd --- /dev/null +++ b/src/resources/Resources.cpp @@ -0,0 +1,177 @@ +#include +#include +#include +#include "Resources.h" +#include "../fs/FSUtils.h" +#include "../gui/GuiSound.h" +#include "../gui/GuiTextureData.h" + +#include + +#include "filelist.h" +#include "../utils/logger.h" + +Resources *Resources::instance = nullptr; + +void Resources::Clear() { + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + if (RecourceList[i].CustomFile) { + free(RecourceList[i].CustomFile); + RecourceList[i].CustomFile = nullptr; + } + + if (RecourceList[i].CustomFileSize != 0) { + RecourceList[i].CustomFileSize = 0; + } + } + + delete instance; + + instance = nullptr; +} + +bool Resources::LoadFiles(const char *path) { + if (!path) { + return false; + } + + bool result = false; + Clear(); + + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + std::string fullpath(path); + fullpath += "/"; + fullpath += RecourceList[i].filename; + DEBUG_FUNCTION_LINE("%s", fullpath.c_str()); + + uint8_t *buffer = nullptr; + uint32_t filesize = 0; + + auto res = FSUtils::LoadFileToMem(fullpath.c_str(), &buffer, &filesize); + if (filesize > 0) { + RecourceList[i].CustomFile = buffer; + RecourceList[i].CustomFileSize = (uint32_t) filesize; + + } + result |= (buffer != 0); + } + + return result; +} + +const uint8_t *Resources::GetFile(const char *filename) { + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + if (strcasecmp(filename, RecourceList[i].filename) == 0) { + return (RecourceList[i].CustomFile ? RecourceList[i].CustomFile : RecourceList[i].DefaultFile); + } + } + + return nullptr; +} + +uint32_t Resources::GetFileSize(const char *filename) { + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + if (strcasecmp(filename, RecourceList[i].filename) == 0) { + return (RecourceList[i].CustomFile ? RecourceList[i].CustomFileSize : RecourceList[i].DefaultFileSize); + } + } + return 0; +} + +GuiTextureData *Resources::GetTexture(const char *filename) { + if (!instance) { + instance = new Resources; + } + + auto itr = instance->textureDataMap.find(std::string(filename)); + if (itr != instance->textureDataMap.end()) { + itr->second.first++; + return itr->second.second; + } + + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + if (strcasecmp(filename, RecourceList[i].filename) == 0) { + const uint8_t *buff = RecourceList[i].CustomFile ? RecourceList[i].CustomFile : RecourceList[i].DefaultFile; + const uint32_t size = RecourceList[i].CustomFile ? RecourceList[i].CustomFileSize : RecourceList[i].DefaultFileSize; + + if (buff == nullptr) { + return nullptr; + } + + auto *image = new GuiTextureData((void *) buff, size, false); + instance->textureDataMap[std::string(filename)].first = 1; + instance->textureDataMap[std::string(filename)].second = image; + + return image; + } + } + + return nullptr; +} + +bool Resources::RemoveTexture(GuiTextureData *image) { + std::map >::iterator itr; + + for (itr = instance->textureDataMap.begin(); itr != instance->textureDataMap.end(); itr++) { + if (itr->second.second == image) { + itr->second.first--; + + if (itr->second.first == 0) { + delete itr->second.second; + // AsyncExecutor::pushForDelete(itr->second.second); + + instance->textureDataMap.erase(itr); + } + return true; + } + } + return false; +} + +GuiSound *Resources::GetSound(const char *filename) { + if (!instance) { + instance = new Resources; + } + + auto itr = instance->soundDataMap.find(std::string(filename)); + if (itr != instance->soundDataMap.end()) { + itr->second.first++; + return itr->second.second; + } + + for (int32_t i = 0; RecourceList[i].filename != nullptr; ++i) { + if (strcasecmp(filename, RecourceList[i].filename) == 0) { + const uint8_t *buff = RecourceList[i].CustomFile ? RecourceList[i].CustomFile : RecourceList[i].DefaultFile; + const uint32_t size = RecourceList[i].CustomFile ? RecourceList[i].CustomFileSize : RecourceList[i].DefaultFileSize; + + if (buff == nullptr) { + return nullptr; + } + + auto *sound = new GuiSound((void *) buff, size); + instance->soundDataMap[std::string(filename)].first = 1; + instance->soundDataMap[std::string(filename)].second = sound; + + return sound; + } + } + + return nullptr; +} + +void Resources::RemoveSound(GuiSound *sound) { + std::map >::iterator itr; + + for (itr = instance->soundDataMap.begin(); itr != instance->soundDataMap.end(); itr++) { + if (itr->second.second == sound) { + itr->second.first--; + + if (itr->second.first == 0) { + // AsyncExecutor::pushForDelete(itr->second.second); + delete itr->second.second; + instance->soundDataMap.erase(itr); + } + break; + } + } +} diff --git a/src/resources/Resources.h b/src/resources/Resources.h new file mode 100644 index 0000000..d8e82ef --- /dev/null +++ b/src/resources/Resources.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +//! forward declaration +class GuiTextureData; + +class GuiSound; + +typedef struct RecourceFile +{ + const char *filename; + const unsigned char *DefaultFile; + const unsigned int &DefaultFileSize; + unsigned char *CustomFile; + unsigned int CustomFileSize; +} RecourceFile; + +class Resources { +public: + static void Clear(); + + static bool LoadFiles(const char *path); + + static const uint8_t *GetFile(const char *filename); + + static uint32_t GetFileSize(const char *filename); + + static GuiTextureData *GetTexture(const char *filename); + + static bool RemoveTexture(GuiTextureData *image); + + static GuiSound *GetSound(const char *filename); + + static void RemoveSound(GuiSound *sound); + +private: + static Resources *instance; + + Resources() {} + + ~Resources() {} + + std::map > textureDataMap; + std::map > soundDataMap; +}; \ No newline at end of file diff --git a/src/resources/filelist.h b/src/resources/filelist.h new file mode 100644 index 0000000..8228d6a --- /dev/null +++ b/src/resources/filelist.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * Resource files. + * This file is generated automatically. + * Includes 4 files. + * + * NOTE: + * Any manual modification of this file will be overwriten by the generation. + ****************************************************************************/ +#ifndef _FILELIST_H_ +#define _FILELIST_H_ + + +#include "Resources.h" +#ifdef __WIIU__ +#include "bgMusic_ogg.h" +#include "button_png.h" +#include "button_click_mp3.h" +#include "FreeSans_ttf.h" + +static RecourceFile RecourceList[] = +{ + {"bgMusic.ogg", bgMusic_ogg, bgMusic_ogg_size, NULL, 0}, + {"button.png", button_png, button_png_size, NULL, 0}, + {"button_click.mp3", button_click_mp3, button_click_mp3_size, NULL, 0}, + {"FreeSans.ttf", FreeSans_ttf, FreeSans_ttf_size, NULL, 0}, + {NULL, NULL, 0, NULL, 0} +}; + +#else +static RecourceFile RecourceList[] = +{ + {"bgMusic.ogg", NULL, 0, NULL, 0}, + {"button.png", NULL, 0, NULL, 0}, + {"button_click.mp3", nullptr, 0, NULL, 0}, + {"FreeSans.ttf", NULL, 0, NULL, 0}, + {NULL, NULL, 0, NULL, 0} +}; + +#endif +#endif diff --git a/src/system/video/SDL_FontCache.cpp b/src/system/video/SDL_FontCache.cpp index 66d41d2..65efc39 100644 --- a/src/system/video/SDL_FontCache.cpp +++ b/src/system/video/SDL_FontCache.cpp @@ -451,9 +451,9 @@ struct FC_Font FC_Image** glyph_cache; char* loading_string; -}; -std::recursive_mutex mutex; + std::recursive_mutex* mutex = nullptr; +}; // Private static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); @@ -845,12 +845,7 @@ void FC_SetTabWidth(unsigned int width_in_spaces) fc_tab_width = width_in_spaces; } - - - - // Constructors - static void FC_Init(FC_Font* font) { if(font == NULL) @@ -895,6 +890,7 @@ static void FC_Init(FC_Font* font) font->glyph_cache_size = 3; font->glyph_cache_count = 0; + font->mutex = new std::recursive_mutex(); font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); @@ -1181,7 +1177,7 @@ Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, S #endif FC_ClearFont(font); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); // Might as well check render target support here #ifdef FC_USE_SDL_GPU @@ -1435,6 +1431,9 @@ void FC_ClearFont(FC_Font* font) } free(font->glyph_cache); font->glyph_cache = NULL; + delete font->mutex; + + font->mutex = nullptr; // Reset font FC_Init(font); @@ -1770,7 +1769,7 @@ FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* fo if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); set_color_for_all_caches(font, font->default_color); @@ -2062,7 +2061,7 @@ FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* form if(formatted_text == NULL || font == NULL) return FC_MakeRect(box.x, box.y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2096,7 +2095,7 @@ FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnu if(formatted_text == NULL || font == NULL) return FC_MakeRect(box.x, box.y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2129,7 +2128,7 @@ FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale sc if(formatted_text == NULL || font == NULL) return FC_MakeRect(box.x, box.y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2162,7 +2161,7 @@ FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color c if(formatted_text == NULL || font == NULL) return FC_MakeRect(box.x, box.y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2195,7 +2194,7 @@ FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect if(formatted_text == NULL || font == NULL) return FC_MakeRect(box.x, box.y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2230,7 +2229,7 @@ FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 w if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2245,7 +2244,7 @@ FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 w FC_Rect FC_DrawColumnToTexture(FC_Font* font, SDL_Texture* target, float x, float y, Uint16 width, const char* text) { - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); // Draw the text onto it SDL_SetRenderTarget(font->renderer, target); @@ -2265,7 +2264,7 @@ FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uin if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2298,7 +2297,7 @@ FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uin if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2321,7 +2320,7 @@ FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uin if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2344,7 +2343,7 @@ FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Ui if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2442,7 +2441,7 @@ FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2460,7 +2459,7 @@ FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignE if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2493,7 +2492,7 @@ FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2512,7 +2511,7 @@ FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effec if(formatted_text == NULL || font == NULL) return FC_MakeRect(x, y, 0, 0); - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2567,7 +2566,7 @@ Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) if(formatted_text == NULL || font == NULL) return 0; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2593,7 +2592,7 @@ Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) if(formatted_text == NULL || font == NULL) return 0; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2633,7 +2632,7 @@ FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_w if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) return result; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2692,7 +2691,7 @@ Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_tex if(formatted_text == NULL || width == 0) return font->height; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2744,7 +2743,7 @@ int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) if(formatted_text == NULL) return font->ascent; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2780,7 +2779,7 @@ int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) if(formatted_text == NULL) return font->descent; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2899,7 +2898,7 @@ Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_widt if(formatted_text == NULL || column_width == 0 || font == NULL) return 0; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); @@ -2947,7 +2946,7 @@ int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 w if(formatted_text == NULL || width == 0) return 0; - std::scoped_lock lock(mutex); + std::scoped_lock lock(*font->mutex); FC_EXTRACT_VARARGS(fc_buffer, formatted_text); diff --git a/src/utils/dirent.h b/src/utils/dirent.h new file mode 100644 index 0000000..1b14e16 --- /dev/null +++ b/src/utils/dirent.h @@ -0,0 +1,1166 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n; +#else + /* WinRT */ + size_t n; +#endif + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/src/utils/logger.h b/src/utils/logger.h index 153c928..3b1539f 100644 --- a/src/utils/logger.h +++ b/src/utils/logger.h @@ -9,6 +9,7 @@ #endif #include +#include #ifdef __cplusplus extern "C" {