push source code for Switch version

This commit is contained in:
BernardoGiordano 2018-05-09 11:25:01 +02:00
parent 6b154ca5ae
commit 20c4bbffbb
44 changed files with 4678 additions and 13 deletions

4
.gitignore vendored
View file

@ -1,2 +1,4 @@
3ds/build/
3ds/out/
3ds/out/
switch/build/
switch/out/

View file

@ -1,47 +1,68 @@
# Checkpoint
A fast and simple save manager for cfw/rosalina-based Homebrew Launchers written in C++.
![](https://i.imgur.com/Nttk8hX.png)
A fast and simple homebrew save manager for 3DS and Switch written in C++.
## Why use Checkpoint?
Checkpoint is created with the ideas of simplicity and efficiency. The UI has been designed to condense as many options as possible, while keeping it simple to work with.
Checkpoint is created following ideas of simplicity and efficiency. The UI has been designed to condense as many options as possible, while keeping it simple to work with.
Moreover, Checkpoint is extremely lightweight and is built using very few (and up-to-date) libraries. It contains minimal assets, while being packaged with a nice graphic user interface.
Moreover, Checkpoint is extremely lightweight - while being packaged with a nice graphic user interface - and is built using the most recent libraries available.
Checkpoint supports DS cartridges, normal titles, and demos. It also automatically checks and filters homebrew titles which may not have a save archive to backup or restore, which is done without an external title list and filters. For this reason, Checkpoint doesn't need constant user maintenance to retain full functionality.
Checkpoint for 3DS natively supports 3DS and DS cartridges, digital standard titles and demo titles. It also automatically checks and filters homebrew titles which may not have a save archive to backup or restore, which is done without an external title list and filters. For this reason, Checkpoint doesn't need constant user maintenance to retain full functionality.
Checkpoint for Switch natively supports NAND saves for the titles you have played. Title informations are loaded automatically.
## Working path
Checkpoint uses the following folders to store the files it generates. Note that all the working directories are automatically generated on first launch (or when Checkpoint finds a new title that doesn't have a working directory yet).
Checkpoint relies on the following folders to store the files it generates. Note that all the working directories are automatically generated on first launch (or when Checkpoint finds a new title that doesn't have a working directory yet).
### 3DS
* **`sdmc:/3ds/Checkpoint`**: root path
* **`sdmc:/3ds/Checkpoint/saves/<unique id> <game title>`**: root path for all the save backups for a generic game
* **`sdmc:/3ds/Checkpoint/extdata/<unique id> <game title>`**: root path for all the extdata backups for a generic game
### Switch
* **`sdmc:/switch/Checkpoint`**: root path
* **`sdmc:/switch/Checkpoint/saves/<title id> <game title>`**: root path for all the save backups for a generic game
## Usage
You can use Checkpoint with both cfw and Rosalina-based Homebrew Launcher. *hax-based Homebrew Launchers are not supported by Checkpoint.
You can use Checkpoint for 3DS with both cfw and Rosalina-based Homebrew Launchers. *hax-based Homebrew Launchers are not supported by Checkpoint. Checkpoint for Switch only runs on homebrew launcher and firmware 3.0.0 is required.
The first launch will take considerably longer than usual (usually 1-2 minutes depending on how many titles you have installed), due to the working directories being created - Checkpoint will be significatively faster upon launch from then on.
You can scroll between the title list with the DPAD/LR and target a title with A when the selector is on it. Now, you can use the DPAD or the touchscreen to select a target backup to restore/overwrite.
## Issues
## Troubleshooting
Checkpoint displays error codes when something weird happens or operations fail. If you have any issues, please ensure they haven't already been addressed, and report the error code and a summary of your operations to reproduce it.
Additionally, you can receive real-time support by joining PKSM's discord server.
[![Discord](https://discordapp.com/api/guilds/278222834633801728/widget.png?style=banner3&time-)](https://discord.gg/bGKEyfY)
## Building
Checkpoint uses [latest libctru](https://github.com/smealum/ctrulib), [latest citro3d](https://github.com/fincs/citro3d) and [latest pp2d](https://github.com/BernardoGiordano/Checkpoint/tree/master/source/pp2d).
Checkpoint for 3DS relies on [latest libctru](https://github.com/smealum/ctrulib) and [latest citro3d](https://github.com/fincs/citro3d).
Checkpoint for Switch relies on [latest libnx](https://github.com/switchbrew/libnx).
## License
This project is licensed under the GNU GPLv3. See [LICENSE.md](https://github.com/BernardoGiordano/Checkpoint/blob/master/LICENSE) for details.
This project is licensed under the GNU GPLv3. Additional Terms 7.b and 7.c of GPLv3 apply to this. See [LICENSE.md](https://github.com/BernardoGiordano/Checkpoint/blob/master/LICENSE) for details.
## Credits
Even though this is the result of independent research and work, this couldn't be possible without J-D-K's [JKSM](https://github.com/J-D-K/JKSM), which is an incredible piece of software that you should all be using. Best regards JK, hope you're fine.
Even though this is the result of independent research and work, Checkpoint for 3DS couldn't be possible without J-D-K's [JKSM](https://gbatemp.net/threads/release-jks-savemanager-homebrew-cia-save-manager.413143/), which is an incredible piece of software that you should all be using. Best regards JK, hope you're fine.
TuxSH for [TWLSaveTool](https://github.com/TuxSH/TWLSaveTool), from which SPI code has been taken.
All the maintainers for [nx-hbmenu](https://github.com/switchbrew/nx-hbmenu), for all the Switch rendering functions.
Yellows8 and all the mantainers for [switch-examples](https://github.com/switchbrew/switch-examples).
Hikari-chin and all the other testers for their help with testing.
If you like my work, **support me on [Patreon](https://www.patreon.com/bernardogiordano)**!

211
switch/Makefile Normal file
View file

@ -0,0 +1,211 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#---------------------------------------------------------------------------------
VERSION_MAJOR := 3
VERSION_MINOR := 1
VERSION_MICRO := 0
APP_TITLE := Checkpoint
APP_AUTHOR := Bernardo Giordano
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
OUTDIR := out
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O3 -ffunction-sections \
$(ARCH) $(DEFINES) \
-DVERSION_MAJOR=${VERSION_MAJOR} \
-DVERSION_MINOR=${VERSION_MINOR} \
-DVERSION_MICRO=${VERSION_MICRO}
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-no-as-needed,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(OUTDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@ $(BUILD) $(OUTDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTDIR)
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.nxfnt.o : %.nxfnt
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

BIN
switch/data/flag.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
switch/data/tahoma24.nxfnt Normal file

Binary file not shown.

BIN
switch/icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,43 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef ACCOUNT_HPP
#define ACCOUNT_HPP
#include <string>
#include <string.h>
#include <switch.h>
#include <unordered_map>
namespace Account
{
Result init(void);
void exit(void);
std::string username(u128 id);
}
#endif

View file

@ -0,0 +1,61 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef CLICKABLE_HPP
#define CLICKABLE_HPP
#include <string>
#include <switch.h>
#include "draw.hpp"
class Clickable
{
public:
Clickable(u32 x, u32 y, u16 w, u16 h, color_t colorBg, color_t colorText, const std::string& message, bool centered);
~Clickable(void) { };
void draw(void);
void draw(const ffnt_header_t* font, u32 messageHeight);
bool held(void);
void invertColors(void);
bool released(void);
void setColors(color_t bg, color_t text);
std::string text(void);
void text(const std::string& v);
private:
u32 mx;
u32 my;
u16 mw;
u16 mh;
color_t mColorBg;
color_t mColorText;
std::string mText;
bool mCentered;
bool mOldPressed;
};
#endif

View file

@ -0,0 +1,60 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef DIRECTORY_HPP
#define DIRECTORY_HPP
#include <dirent.h>
#include <errno.h>
#include <string>
#include <switch.h>
#include <vector>
struct DirectoryEntry {
std::string name;
bool directory;
};
class Directory
{
public:
Directory(const std::string& root);
~Directory(void) { };
Result error(void);
std::string entry(size_t index);
bool folder(size_t index);
bool good(void);
size_t size(void);
private:
std::vector
<struct DirectoryEntry> mList;
Result mError;
bool mGood;
};
#endif

80
switch/include/draw.hpp Normal file
View file

@ -0,0 +1,80 @@
/*
* Copyright 2017-2018 nx-hbmenu Authors
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef DRAW_HPP
#define DRAW_HPP
#include <switch.h>
#include "types.hpp"
extern const ffnt_header_t tahoma24_nxfnt;
extern const ffnt_header_t interuiregular20_nxfnt;
extern const ffnt_header_t interuiregular14_nxfnt;
#define font24 &tahoma24_nxfnt
#define font20 &interuiregular20_nxfnt
#define font14 &interuiregular14_nxfnt
extern u8* framebuf;
extern u32 framebuf_width;
// the following code is from nx-hbmenu
// https://github.com/switchbrew/nx-hbmenu/blob/master/common/common.h#L63
static inline u8 BlendColor(u32 src, u32 dst, u8 alpha)
{
u8 one_minus_alpha = (u8)255 - alpha;
return (dst*alpha + src*one_minus_alpha)/(u8)255;
}
static inline color_t MakeColor(u8 r, u8 g, u8 b, u8 a)
{
color_t clr;
clr.r = r;
clr.g = g;
clr.b = b;
clr.a = a;
return clr;
}
static inline void DrawPixel(u32 x, u32 y, color_t clr)
{
if (x >= 1280 || y >= 720)
return;
u32 off = (y * framebuf_width + x)*4;
framebuf[off] = BlendColor(framebuf[off], clr.r, clr.a); off++;
framebuf[off] = BlendColor(framebuf[off], clr.g, clr.a); off++;
framebuf[off] = BlendColor(framebuf[off], clr.b, clr.a); off++;
framebuf[off] = 0xff;
}
static inline void Draw4PixelsRaw(u32 x, u32 y, color_t clr)
{
if (x >= 1280 || y >= 720 || x > 1280-4)
return;
u32 color = clr.r | (clr.g<<8) | (clr.b<<16) | (0xff<<24);
u128 val = color | ((u128)color<<32) | ((u128)color<<64) | ((u128)color<<96);
u32 off = (y * framebuf_width + x)*4;
*((u128*)&framebuf[off]) = val;
}
void rectangle(u32 x, u32 y, u32 w, u32 h, color_t color);
void DrawPixel(u32 x, u32 y, color_t clr);
void DrawText(const ffnt_header_t* font, u32 x, u32 y, color_t clr, const char* text);
void DrawTextTruncate(const ffnt_header_t* font, u32 x, u32 y, color_t clr, const char* text, u32 max_width, const char* end_text);
void GetTextDimensions(const ffnt_header_t* font, const char* text, u32* width_out, u32* height_out);
void DrawImage(int x, int y, int width, int height, const u8 *image, ImageMode mode);
#endif

View file

@ -0,0 +1,40 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef FILESYSTEM_HPP
#define FILESYSTEM_HPP
#include <switch.h>
#include "gui.hpp"
namespace FileSystem
{
Result mount(FsFileSystem* fileSystem, u64 titleID, u128 userID);
int mount(FsFileSystem fs);
void unmount(void);
}
#endif

89
switch/include/gui.hpp Normal file
View file

@ -0,0 +1,89 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef GUI_HPP
#define GUI_HPP
#include <math.h>
#include <switch.h>
#include <string.h>
#include "account.hpp"
#include "clickable.hpp"
#include "draw.hpp"
#include "info.hpp"
#include "hid.hpp"
#include "messagebox.hpp"
#include "scrollable.hpp"
#include "title.hpp"
#include "types.hpp"
#include "util.hpp"
#include "checkbox_grey_bin.h"
#include "checkbox_white_bin.h"
#include "flag_bin.h"
#define COLOR_WHITE MakeColor(255, 255, 255, 255)
#define COLOR_BLACK MakeColor(0, 0, 0, 255)
#define COLOR_BLUE MakeColor(0, 0, 255, 255)
#define COLOR_GREEN MakeColor(0, 255, 201, 255)
#define COLOR_GREY_DARKER MakeColor(70, 70, 70, 255)
#define COLOR_GREY_DARK MakeColor(79, 79, 79, 255)
#define COLOR_GREY_MEDIUM MakeColor(94, 94, 94, 255)
#define COLOR_GREY_LIGHT MakeColor(138, 138, 138, 255)
namespace Gui
{
void init(void);
void exit(void);
void draw(void);
void createInfo(const std::string& title, const std::string& message);
void createError(Result res, const std::string& message);
bool backupScroll(void);
void backupScroll(bool enable);
size_t count(entryType_t type);
size_t index(entryType_t type);
void index(entryType_t type, size_t i);
bool isBackupReleased(void);
bool isRestoreReleased(void);
std::string nameFromCell(entryType_t type, size_t index);
void resetIndex(entryType_t type);
void updateButtonsColor(void);
void updateSelector(void);
bool askForConfirmation(const std::string& text);
void drawCopy(const std::string& src, u64 offset, u64 size);
void addSelectedEntry(size_t idx);
void clearSelectedEntries(void);
bool multipleSelectionEnabled(void);
std::vector
<size_t> selectedEntries(void);
}
#endif

51
switch/include/hbkbd.hpp Normal file
View file

@ -0,0 +1,51 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef HBKBD_HPP
#define HBKBD_HPP
#include <locale>
#include <string>
#include <switch.h>
#include <vector>
#include "clickable.hpp"
#include "draw.hpp"
#include "util.hpp"
#define CUSTOM_PATH_LEN 49
extern u8* framebuf;
extern u32 framebuf_width;
namespace hbkbd
{
void init(void);
void exit(void);
std::string keyboard(const std::string& suggestion);
}
#endif

40
switch/include/hid.hpp Normal file
View file

@ -0,0 +1,40 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef HID_HPP
#define HID_HPP
#include "gui.hpp"
#include "types.hpp"
namespace hid
{
entryType_t entryType(void);
void entryType(entryType_t type_);
void index(size_t &currentEntry, int &page, size_t maxpages, size_t maxentries, const size_t entries, const size_t columns);
}
#endif

62
switch/include/info.hpp Normal file
View file

@ -0,0 +1,62 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef INFO_HPP
#define INFO_HPP
#include <switch.h>
#include <string>
#include "draw.hpp"
#include "util.hpp"
typedef enum {
TYPE_INFO,
TYPE_ERROR
} Info_t;
class Info
{
public:
Info(void);
~Info(void) { };
void draw(void);
void init(std::string title, std::string message, int ttl, Info_t type);
void init(Result res, std::string message, int ttl, Info_t type);
private:
size_t mw;
size_t mh;
u32 mx;
u32 my;
int mTTL;
Result mRes;
Info_t mType;
std::string mTitle;
std::string mMessage;
};
#endif

54
switch/include/io.hpp Normal file
View file

@ -0,0 +1,54 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef IO_HPP
#define IO_HPP
#include <cstdio>
#include <dirent.h>
#include <switch.h>
#include <sys/stat.h>
#include <unistd.h>
#include "directory.hpp"
#include "gui.hpp"
#include "hbkbd.hpp"
#define BUFFER_SIZE 0x50000
namespace io
{
void backup(size_t index);
void restore(size_t index);
Result copyDirectory(const std::string& srcPath, const std::string& dstPath);
void copyFile(const std::string& srcPath, const std::string& dstPath);
Result createDirectory(const std::string& path);
int deleteFolderRecursively(const char* path);
bool directoryExists(const std::string& path);
bool fileExists(const std::string& path);
}
#endif

View file

@ -0,0 +1,53 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef MESSAGEBOX_HPP
#define MESSAGEBOX_HPP
#include <string>
#include <switch.h>
#include <vector>
#include "draw.hpp"
#include "gui.hpp"
class MessageBox
{
public:
MessageBox(color_t colorbg, color_t colormessage);
~MessageBox() { };
void push_message(const std::string& str);
void clear(void);
void draw(void);
private:
std::vector
<std::string> mList;
color_t mColorBg;
color_t mColorText;
};
#endif

66
switch/include/nanojpeg.h Normal file
View file

@ -0,0 +1,66 @@
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and paste this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H

View file

@ -0,0 +1,67 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef SCROLLABLE_HPP
#define SCROLLABLE_HPP
#include <switch.h>
#include <vector>
#include "clickable.hpp"
#include "draw.hpp"
#include "hid.hpp"
class Scrollable
{
public:
Scrollable(u32 x, u32 y, u32 w, u32 h, size_t visibleEntries);
~Scrollable(void);
std::string cellName(size_t i);
void draw(void);
void flush(void);
size_t index(void);
void index(size_t i);
void invertCellColors(size_t index);
size_t maxVisibleEntries(void);
int page(void);
void push_back(color_t color, color_t colorMessage, const std::string& message);
void resetIndex(void);
size_t size(void);
void updateSelection(void);
private:
u32 mx;
u32 my;
u32 mw;
u32 mh;
size_t mVisibleEntries;
size_t mIndex;
int mPage;
std::vector
<Clickable*> mCells;
};
#endif

40
switch/include/thread.hpp Normal file
View file

@ -0,0 +1,40 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef THREAD_HPP
#define THREAD_HPP
#include <switch.h>
#include <vector>
#include "title.hpp"
namespace Threads
{
Result create(ThreadFunc entrypoint);
void destroy(void);
}
#endif

73
switch/include/title.hpp Normal file
View file

@ -0,0 +1,73 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef TITLE_HPP
#define TITLE_HPP
#include <algorithm>
#include <stdlib.h>
#include <string>
#include <switch.h>
#include <utility>
#include <vector>
#include "filesystem.hpp"
#include "io.hpp"
extern "C" {
#include "nanojpeg.h"
}
class Title
{
public:
void init(u64 titleid, u128 userID, const std::string& name);
u8* icon(void);
void icon(u8* data, size_t iconsize);
u64 id(void);
std::string name(void);
std::string path(void);
void refreshDirectories(void);
std::vector
<std::string> saves(void);
u128 userId(void);
private:
u64 mId;
u128 mUserId;
std::string mName;
std::string mPath;
u8* mIcon;
std::vector
<std::string> mSaves;
};
void getTitle(Title &dst, size_t i);
size_t getTitleCount(void);
void loadTitles(void);
void refreshDirectories(u64 id);
#endif

54
switch/include/types.hpp Normal file
View file

@ -0,0 +1,54 @@
#ifndef TYPES_HPP
#define TYPES_HPP
typedef enum {
TITLES,
CELLS
} entryType_t;
typedef union {
u32 abgr;
struct {
u8 r,g,b,a;
};
} color_t;
typedef enum
{
IMAGE_MODE_RGB24,
IMAGE_MODE_RGBA32
} ImageMode;
typedef struct {
u8 magic[4]; // 'fFNT'
int version; // 1
u16 npages;
u8 height;
u8 baseline;
} ffnt_header_t;
typedef struct {
u32 size, offset;
} ffnt_pageentry_t;
typedef struct {
u32 pos[0x100];
u8 widths[0x100];
u8 heights[0x100];
int8_t advances[0x100];
int8_t posX[0x100];
int8_t posY[0x100];
} ffnt_pagehdr_t;
typedef struct {
ffnt_pagehdr_t hdr;
u8 data[];
} ffnt_page_t;
typedef struct {
u8 width, height;
int8_t posX, posY, advance;
const u8* data;
} glyph_t;
#endif

61
switch/include/util.hpp Normal file
View file

@ -0,0 +1,61 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef UTIL_HPP
#define UTIL_HPP
#include <switch.h>
#include <cctype>
#include <memory>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string>
#include <time.h>
#include "account.hpp"
#include "gui.hpp"
#include "hbkbd.hpp"
#include "io.hpp"
void servicesExit(void);
Result servicesInit(void);
namespace StringUtils
{
bool containsInvalidChar(const std::string& str);
std::string format(const std::string fmt_str, ...);
size_t lines(const std::string& str);
std::string removeForbiddenCharacters(std::string src);
std::string sizeString(double size);
}
namespace DateTime
{
std::string dateTimeStr(void);
std::string timeStr(void);
}
#endif

72
switch/source/account.cpp Normal file
View file

@ -0,0 +1,72 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "account.hpp"
static std::unordered_map<u128, std::string> mUsernames;
Result Account::init(void)
{
return accountInitialize();
}
void Account::exit(void)
{
accountExit();
}
std::string Account::username(u128 id)
{
std::unordered_map<u128, std::string>::const_iterator got = mUsernames.find(id);
if (got == mUsernames.end())
{
// look for a user and add it to the map
AccountProfile profile;
AccountProfileBase profilebase;
char username[0x21] = {0};
memset(&profilebase, 0, sizeof(profilebase));
Result res = accountGetProfile(&profile, id);
if (R_FAILED(res))
{
return "";
}
res = accountProfileGet(&profile, NULL, &profilebase);
if (R_FAILED(res))
{
return "";
}
strncpy(username, profilebase.username, sizeof(profilebase.username));
std::string user = std::string(username);
mUsernames.insert({id, user});
accountProfileClose(&profile);
return user;
}
return got->second;
}

117
switch/source/clickable.cpp Normal file
View file

@ -0,0 +1,117 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "clickable.hpp"
Clickable::Clickable(u32 x, u32 y, u16 w, u16 h, color_t colorBg, color_t colorText, const std::string& text, bool centered)
{
mx = x;
my = y;
mw = w;
mh = h;
mColorBg = colorBg;
mColorText = colorText;
mText = text;
mCentered = centered;
mOldPressed = false;
}
std::string Clickable::text(void)
{
return mText;
}
void Clickable::text(const std::string& v)
{
mText = v;
}
bool Clickable::held()
{
touchPosition touch;
hidTouchRead(&touch, 0);
return ((hidKeysHeld(CONTROLLER_P1_AUTO) & KEY_TOUCH) && touch.px > mx && touch.px < mx+mw && touch.py > my && touch.py < my+mh);
}
bool Clickable::released(void)
{
touchPosition touch;
hidTouchRead(&touch, 0);
const bool on = touch.px > mx && touch.px < mx+mw && touch.py > my && touch.py < my+mh;
if (on)
{
mOldPressed = true;
}
else
{
if (mOldPressed && !(touch.px > 0 || touch.py > 0))
{
mOldPressed = false;
return true;
}
mOldPressed = false;
}
return false;
}
void Clickable::invertColors(void)
{
color_t tmp = mColorBg;
mColorBg = mColorText;
mColorText = tmp;
}
void Clickable::setColors(color_t bg, color_t text)
{
mColorBg = bg;
mColorText = text;
}
void Clickable::draw(void)
{
static const u32 messageHeight = 44;
u32 textw;
GetTextDimensions(font24, mText.c_str(), &textw, NULL);
const u32 messageWidth = mCentered ? textw : mw - 8;
bool hld = held();
const color_t bgColor = hld ? mColorText : mColorBg;
const color_t msgColor = hld ? mColorBg : mColorText;
rectangle(mx, my, mw, mh, bgColor);
DrawText(font24, mx + (mw - messageWidth)/2, my + (mh - messageHeight)/2, msgColor, mText.c_str());
}
void Clickable::draw(const ffnt_header_t* font, u32 messageHeight)
{
u32 textw;
GetTextDimensions(font, mText.c_str(), &textw, NULL);
const u32 messageWidth = mCentered ? textw : mw - 8;
rectangle(mx, my, mw, mh, mColorBg);
DrawTextTruncate(font, mx + (mw - messageWidth)/2, my + (mh - messageHeight)/2, mColorText, mText.c_str(), mw - 4*2 - 40, NULL);
}

View file

@ -0,0 +1,80 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "directory.hpp"
Directory::Directory(const std::string& root)
{
mGood = false;
mError = 0;
mList.clear();
DIR* dir = opendir(root.c_str());
struct dirent* ent;
if (dir == NULL)
{
mError = (Result)errno;
}
else
{
while ((ent = readdir(dir)))
{
std::string name = std::string(ent->d_name);
bool directory = ent->d_type == DT_DIR;
struct DirectoryEntry de = { name, directory };
mList.push_back(de);
}
}
closedir(dir);
mGood = true;
}
Result Directory::error(void)
{
return mError;
}
bool Directory::good(void)
{
return mGood;
}
std::string Directory::entry(size_t index)
{
return index < mList.size() ? mList.at(index).name : "";
}
bool Directory::folder(size_t index)
{
return index < mList.size() ? mList.at(index).directory : false;
}
size_t Directory::size(void)
{
return mList.size();
}

249
switch/source/draw.cpp Normal file
View file

@ -0,0 +1,249 @@
/*
* Copyright 2017-2018 nx-hbmenu Authors
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "draw.hpp"
static inline const ffnt_page_t* FontGetPage(const ffnt_header_t* font, u32 page_id)
{
if (page_id >= font->npages)
return NULL;
ffnt_pageentry_t* ent = &((ffnt_pageentry_t*)(font+1))[page_id];
if (ent->size == 0)
return NULL;
return (const ffnt_page_t*)((const u8*)font + ent->offset);
}
static inline bool FontLoadGlyph(glyph_t* glyph, const ffnt_header_t* font, u32 codepoint)
{
const ffnt_page_t* page = FontGetPage(font, codepoint >> 8);
if (!page)
return false;
codepoint &= 0xFF;
u32 off = page->hdr.pos[codepoint];
if (off == ~(u32)0)
return false;
glyph->width = page->hdr.widths[codepoint];
glyph->height = page->hdr.heights[codepoint];
glyph->advance = page->hdr.advances[codepoint];
glyph->posX = page->hdr.posX[codepoint];
glyph->posY = page->hdr.posY[codepoint];
glyph->data = &page->data[off];
return true;
}
static void DrawGlyph(u32 x, u32 y, color_t clr, const glyph_t* glyph)
{
u32 i, j;
const u8* data = glyph->data;
x += glyph->posX;
y += glyph->posY;
for (j = 0; j < glyph->height; j ++)
{
for (i = 0; i < glyph->width; i ++)
{
clr.a = *data++;
if (!clr.a) continue;
DrawPixel(x+i, y+j, clr);
}
}
}
static inline u8 DecodeByte(const char** ptr)
{
u8 c = (u8)**ptr;
*ptr += 1;
return c;
}
// UTF-8 code adapted from http://www.json.org/JSON_checker/utf8_decode.c
static inline int8_t DecodeUTF8Cont(const char** ptr)
{
int c = DecodeByte(ptr);
return ((c & 0xC0) == 0x80) ? (c & 0x3F) : -1;
}
static inline u32 DecodeUTF8(const char** ptr)
{
u32 r;
u8 c;
int8_t c1, c2, c3;
c = DecodeByte(ptr);
if ((c & 0x80) == 0)
return c;
if ((c & 0xE0) == 0xC0)
{
c1 = DecodeUTF8Cont(ptr);
if (c1 >= 0)
{
r = ((c & 0x1F) << 6) | c1;
if (r >= 0x80)
return r;
}
} else if ((c & 0xF0) == 0xE0)
{
c1 = DecodeUTF8Cont(ptr);
if (c1 >= 0)
{
c2 = DecodeUTF8Cont(ptr);
if (c2 >= 0)
{
r = ((c & 0x0F) << 12) | (c1 << 6) | c2;
if (r >= 0x800 && (r < 0xD800 || r >= 0xE000))
return r;
}
}
} else if ((c & 0xF8) == 0xF0)
{
c1 = DecodeUTF8Cont(ptr);
if (c1 >= 0)
{
c2 = DecodeUTF8Cont(ptr);
if (c2 >= 0)
{
c3 = DecodeUTF8Cont(ptr);
if (c3 >= 0)
{
r = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
if (r >= 0x10000 && r < 0x110000)
return r;
}
}
}
}
return 0xFFFD;
}
static void DrawText_(const ffnt_header_t* font, u32 x, u32 y, color_t clr, const char* text, u32 max_width, const char* end_text)
{
y += font->baseline;
u32 origX = x;
while (*text)
{
if (max_width && x-origX >= max_width) {
text = end_text;
max_width = 0;
}
glyph_t glyph;
u32 codepoint = DecodeUTF8(&text);
if (codepoint == '\n')
{
if (max_width) {
text = end_text;
max_width = 0;
continue;
}
x = origX;
y += font->height;
continue;
}
if (!FontLoadGlyph(&glyph, font, codepoint))
{
if (!FontLoadGlyph(&glyph, font, '?'))
continue;
}
DrawGlyph(x, y, clr, &glyph);
x += glyph.advance;
}
}
void DrawText(const ffnt_header_t* font, u32 x, u32 y, color_t clr, const char* text)
{
DrawText_(font, x, y, clr, text, 0, NULL);
}
void DrawTextTruncate(const ffnt_header_t* font, u32 x, u32 y, color_t clr, const char* text, u32 max_width, const char* end_text)
{
DrawText_(font, x, y, clr, text, max_width, end_text);
}
void GetTextDimensions(const ffnt_header_t* font, const char* text, u32* width_out, u32* height_out)
{
u32 x = 0;
u32 width = 0, height = 0;
while (*text)
{
glyph_t glyph;
u32 codepoint = DecodeUTF8(&text);
if (codepoint == '\n')
{
x = 0;
height += font->height;
continue;
}
if (!FontLoadGlyph(&glyph, font, codepoint))
{
if (!FontLoadGlyph(&glyph, font, '?'))
continue;
}
x += glyph.advance;
if (x > width)
width = x;
}
if (width_out)
*width_out = width;
if (height_out)
*height_out = height;
}
void DrawImage(int x, int y, int width, int height, const u8 *image, ImageMode mode)
{
int tmpx, tmpy;
int pos;
color_t current_color;
for (tmpy = 0; tmpy < height; tmpy++)
{
for (tmpx = 0; tmpx < width; tmpx++)
{
switch (mode)
{
case IMAGE_MODE_RGB24:
pos = ((tmpy*width) + tmpx) * 3;
current_color = MakeColor(image[pos+0], image[pos+1], image[pos+2], 255);
break;
case IMAGE_MODE_RGBA32:
pos = ((tmpy*width) + tmpx) * 4;
current_color = MakeColor(image[pos+0], image[pos+1], image[pos+2], image[pos+3]);
break;
}
DrawPixel(x+tmpx, y+tmpy, current_color);
}
}
}
void rectangle(u32 x, u32 y, u32 w, u32 h, color_t color)
{
for (u32 j = y; j < y+h; j++)
{
for (u32 i = x; i < x + w; i+=4)
{
Draw4PixelsRaw(i, j, color);
}
}
}

View file

@ -0,0 +1,42 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "filesystem.hpp"
Result FileSystem::mount(FsFileSystem* fileSystem, u64 titleID, u128 userID)
{
return fsMount_SaveData(fileSystem, titleID, userID);
}
int FileSystem::mount(FsFileSystem fs)
{
return fsdevMountDevice("save", fs);
}
void FileSystem::unmount(void)
{
fsdevUnmountDevice("save");
}

390
switch/source/gui.cpp Normal file
View file

@ -0,0 +1,390 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "gui.hpp"
u8* framebuf;
u32 framebuf_width;
static Info* info;
static Clickable* buttonBackup;
static Clickable* buttonRestore;
static MessageBox* messageBox;
static MessageBox* copyList;
static Scrollable* titleList;
static Scrollable* backupList;
static const size_t cols = 14;
static bool backupScrollEnabled;
static char ver[8];
void Gui::createInfo(const std::string& title, const std::string& message)
{
info->init(title, message, 300, TYPE_INFO);
}
void Gui::createError(Result res, const std::string& message)
{
info->init(res, message, 300, TYPE_ERROR);
}
size_t Gui::count(entryType_t type)
{
return type == TITLES ? titleList->size() : backupList->size();
}
std::string Gui::nameFromCell(entryType_t type, size_t index)
{
return type == TITLES ? titleList->cellName(index) : backupList->cellName(index);
}
void Gui::resetIndex(entryType_t type)
{
if (type == TITLES)
{
titleList->resetIndex();
}
else
{
backupList->resetIndex();
}
}
size_t Gui::index(entryType_t type)
{
return type == TITLES ? titleList->index() : backupList->index();
}
void Gui::index(entryType_t type, size_t i)
{
if (type == TITLES)
{
titleList->index(i);
}
else
{
backupList->index(i);
}
}
/// Multi selection
static std::vector<size_t> selEnt;
std::vector<size_t> Gui::selectedEntries(void)
{
return selEnt;
}
bool Gui::multipleSelectionEnabled(void)
{
return !selEnt.empty();
}
void Gui::clearSelectedEntries(void)
{
selEnt.clear();
}
void Gui::addSelectedEntry(size_t idx)
{
int existing = -1;
for (size_t i = 0, sz = selEnt.size(); i < sz && existing == -1; i++)
{
if (selEnt.at(i) == idx)
{
existing = (int)i;
}
}
if (existing == -1)
{
selEnt.push_back(idx);
}
else
{
selEnt.erase(selEnt.begin() + existing);
}
}
/// Gui implementation
static void drawOutline(u32 x, u32 y, u16 w, u16 h, u8 size, color_t color)
{
rectangle(x - size, y - size, w + 2*size, size, color); // top
rectangle(x - size, y, size, h, color); // left
rectangle(x + w, y, size, h, color); // right
rectangle(x - size, y + h, w + 2*size, size, color); // bottom
}
static void drawBackground(void)
{
framebuf = gfxGetFramebuffer(&framebuf_width, NULL);
memset(framebuf, 51, gfxGetFramebufferSize());
rectangle(0, 0, 1280, 40, COLOR_GREY_DARK);
rectangle(0, 680, 1280, 40, COLOR_GREY_DARK);
u32 ver_w, checkpoint_w;
GetTextDimensions(font20, ver, &ver_w, NULL);
GetTextDimensions(font24, "checkpoint", &checkpoint_w, NULL);
DrawText(font20, 10, 7, COLOR_GREY_LIGHT, DateTime::timeStr().c_str());
DrawText(font24, 1280 - 10 - ver_w - 40 - 12 - checkpoint_w, 0, COLOR_WHITE, "checkpoint");
DrawText(font20, 1280 - 10 - ver_w, 7, COLOR_GREY_LIGHT, ver);
DrawImage(1280 - 10 - ver_w - 40 - 6, 0, 40, 40, flag_bin, IMAGE_MODE_RGBA32);
}
void Gui::drawCopy(const std::string& src, u64 offset, u64 size)
{
copyList->clear();
copyList->push_message("Copying " + src);
drawBackground();
copyList->draw();
static const int barHeight = 40;
static const int progressBarHeight = 50;
static const int spacingFromSides = 200;
static const int spacingFromBars = 220 + (720 - barHeight * 2 - progressBarHeight) / 2;
static const int width = 1280 - spacingFromSides * 2;
rectangle(spacingFromSides - 2, barHeight + spacingFromBars - 2, width + 4, progressBarHeight + 4, COLOR_GREY_LIGHT);
rectangle(spacingFromSides, barHeight + spacingFromBars, width, progressBarHeight, COLOR_WHITE);
rectangle(spacingFromSides, barHeight + spacingFromBars, (float)offset / (float)size * width, progressBarHeight, MakeColor(116, 222, 126, 255));
std::string sizeString = StringUtils::sizeString(offset) + " of " + StringUtils::sizeString(size);
u32 textw;
GetTextDimensions(font20, sizeString.c_str(), &textw, NULL);
DrawText(font20, ceilf((1280 - textw)/2), spacingFromBars + 48, COLOR_BLACK, sizeString.c_str());
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
bool Gui::askForConfirmation(const std::string& text)
{
bool ret = false;
Clickable* buttonYes = new Clickable(293, 540, 200, 80, COLOR_WHITE, COLOR_BLACK, "Yes", true);
Clickable* buttonNo = new Clickable(786, 540, 200, 80, COLOR_WHITE, COLOR_BLACK, "No", true);
MessageBox* message = new MessageBox(COLOR_GREY_DARK, COLOR_WHITE);
message->push_message(text);
while(appletMainLoop() && !(buttonNo->released() || hidKeysDown(CONTROLLER_P1_AUTO) & KEY_B))
{
hidScanInput();
if (buttonYes->released() || hidKeysDown(CONTROLLER_P1_AUTO) & KEY_A)
{
ret = true;
break;
}
drawBackground();
message->draw();
drawOutline(293, 540, 200, 80, 4, COLOR_GREY_LIGHT);
drawOutline(786, 540, 200, 80, 4, COLOR_GREY_LIGHT);
buttonYes->draw();
buttonNo->draw();
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
delete message;
delete buttonYes;
delete buttonNo;
return ret;
}
void Gui::init(void)
{
gfxInitDefault();
sprintf(ver, "v%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
backupScrollEnabled = false;
info = new Info();
info->init("", "", 0, TYPE_INFO);
titleList = new Scrollable(40, 80, 398, 560, cols);
backupList = new Scrollable(444, 80, 398, 560, cols);
buttonBackup = new Clickable(984, 476, 256, 80, COLOR_WHITE, COLOR_GREY_LIGHT, "Backup", true);
buttonRestore = new Clickable(984, 560, 256, 80, COLOR_WHITE, COLOR_GREY_LIGHT, "Restore", true);
copyList = new MessageBox(COLOR_GREY_DARK, COLOR_WHITE);
messageBox = new MessageBox(COLOR_GREY_DARK, COLOR_WHITE);
messageBox->push_message("Press A to enter target.");
messageBox->push_message("Press B to exit target or deselect all titles.");
messageBox->push_message("Press X to delete a backup.");
messageBox->push_message("Press Y to multiselect a title.");
messageBox->push_message("Hold Y to multiselect all titles.");
messageBox->push_message("Press the arrows to move between titles.");
messageBox->push_message("Press L/R to switch page.");
}
void Gui::exit(void)
{
delete info;
delete titleList;
delete backupList;
delete copyList;
delete buttonBackup;
delete buttonRestore;
delete messageBox;
gfxExit();
}
void Gui::draw(void)
{
titleList->flush();
backupList->flush();
// draw
drawBackground();
drawOutline(984, 476, 256, 164, 4, COLOR_GREY_LIGHT);
rectangle(984, 556, 256, 4, COLOR_GREY_LIGHT);
drawOutline(40, 80, 804, 560, 4, COLOR_GREY_LIGHT);
rectangle(440, 80, 4, 560, COLOR_GREY_LIGHT);
// TODO: optimize
rectangle(40, 80, 398, 560, COLOR_GREY_DARK);
rectangle(444, 80, 398, 560, COLOR_GREY_DARK);
for (size_t i = 0, sz = getTitleCount(); i < sz; i++)
{
Title title;
getTitle(title, i);
titleList->push_back(COLOR_WHITE, !backupScrollEnabled ? COLOR_BLUE : COLOR_GREY_LIGHT, title.name());
if (i == Gui::index(TITLES))
{
std::vector<std::string> dirs = title.saves();
for (size_t j = 0, amount = dirs.size(); j < amount; j++)
{
backupList->push_back(COLOR_WHITE, backupScrollEnabled ? COLOR_BLUE : COLOR_GREY_LIGHT, dirs.at(j));
}
// descriptions
u32 userw;
DrawText(font14, 980, 354, COLOR_GREY_LIGHT, "User: ");
GetTextDimensions(font14, "User: ", &userw, NULL);
DrawText(font14, 980 + userw, 354, COLOR_WHITE, Account::username(title.userId()).c_str());
// icon
drawOutline(984, 80, 256, 256, 4, COLOR_BLACK);
if (title.icon() != NULL)
{
DrawImage(984, 80, 256, 256, title.icon(), IMAGE_MODE_RGB24);
}
else
{
rectangle(984, 80, 256, 256, COLOR_WHITE);
}
}
}
titleList->invertCellColors(titleList->index());
if (backupScrollEnabled)
{
backupList->invertCellColors(backupList->index());
}
titleList->draw();
backupList->draw();
buttonBackup->draw();
buttonRestore->draw();
for (size_t k = titleList->page()*14; k < titleList->page()*14 + titleList->maxVisibleEntries(); k++)
{
if (!selEnt.empty() && std::find(selEnt.begin(), selEnt.end(), k) != selEnt.end())
{
DrawImage(398, 80+40*(k%14), 40, 40, k == titleList->index() ? checkbox_white_bin : checkbox_grey_bin, IMAGE_MODE_RGBA32);
}
}
info->draw();
if (hidKeysHeld(CONTROLLER_P1_AUTO) & KEY_MINUS)
{
messageBox->draw();
}
u32 ins_w, ins_h;
const char* instructions = "Hold - to see commands. Press + to exit.";
GetTextDimensions(font20, instructions, &ins_w, &ins_h);
DrawText(font20, ceil((1280 - ins_w) / 2), 680 + 5, COLOR_WHITE, instructions);
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
bool Gui::isBackupReleased(void)
{
return buttonBackup->released() && backupScrollEnabled;
}
bool Gui::isRestoreReleased(void)
{
return buttonRestore->released() && backupScrollEnabled;
}
bool Gui::backupScroll(void)
{
return backupScrollEnabled;
}
void Gui::backupScroll(bool enable)
{
backupScrollEnabled = enable;
}
void Gui::updateButtonsColor(void)
{
if (backupScrollEnabled)
{
buttonBackup->setColors(COLOR_WHITE, COLOR_BLACK);
buttonRestore->setColors(COLOR_WHITE, COLOR_BLACK);
}
else
{
buttonBackup->setColors(COLOR_WHITE, COLOR_GREY_LIGHT);
buttonRestore->setColors(COLOR_WHITE, COLOR_GREY_LIGHT);
}
}
void Gui::updateSelector(void)
{
if (!backupScrollEnabled)
{
titleList->updateSelection();
backupList->resetIndex();
}
else
{
backupList->updateSelection();
}
}

204
switch/source/hbkbd.cpp Normal file
View file

@ -0,0 +1,204 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "hbkbd.hpp"
static const u32 buttonSpacing = 4;
static const u32 normalWidth = 92;
static const u32 bigWidth = 116;
static const u32 height = 60;
static const u32 margintb = 20;
static const u32 marginlr = 54;
static const u32 starty = 720 - 356 + margintb;
static const std::string letters = "1234567890@qwertyuiop+asdfghjkl_:zxcvbnm,.-/";
static std::vector<Clickable*> buttons;
void hbkbd::init(void)
{
buttons.clear();
// fill with the above characters
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j < 11; j++)
{
Clickable* button = new Clickable(
marginlr + (buttonSpacing + normalWidth) * j,
starty + (buttonSpacing + height) * i,
normalWidth,
height,
COLOR_GREY_DARK,
COLOR_WHITE,
letters.substr(i*11+j, 1),
true
);
buttons.push_back(button);
}
}
Clickable* backspace = new Clickable(
marginlr + (buttonSpacing + normalWidth) * 11,
starty,
bigWidth,
height,
COLOR_WHITE,
COLOR_BLACK,
"back",
true
);
buttons.push_back(backspace);
Clickable* returnb = new Clickable(
marginlr + (buttonSpacing + normalWidth) * 11,
starty + height + 4,
bigWidth,
height * 2 + 4,
COLOR_GREY_MEDIUM,
COLOR_GREY_LIGHT,
"return",
true
);
buttons.push_back(returnb);
Clickable* ok = new Clickable(
marginlr + (buttonSpacing + normalWidth) * 11,
starty + height*3 + 4*3,
bigWidth,
height * 2 + 4,
COLOR_GREEN,
COLOR_BLACK,
"OK",
true
);
buttons.push_back(ok);
Clickable* caps = new Clickable(
marginlr + buttonSpacing + normalWidth,
starty + height*4 + 4*4,
normalWidth,
height,
COLOR_GREY_MEDIUM,
COLOR_WHITE,
"caps",
true
);
buttons.push_back(caps);
Clickable* spacebar = new Clickable(
marginlr + (buttonSpacing + normalWidth) * 3,
starty + height*4 + 4*4,
normalWidth*8 + buttonSpacing*7,
height,
COLOR_GREY_MEDIUM,
COLOR_WHITE,
"space",
true
);
buttons.push_back(spacebar);
}
void hbkbd::exit(void)
{
for (size_t i = 0, sz = buttons.size(); i < sz; i++)
{
delete buttons[i];
}
}
std::string hbkbd::keyboard(const std::string& suggestion)
{
std::string str;
while (appletMainLoop() && !(hidKeysDown(CONTROLLER_P1_AUTO) & KEY_B))
{
hidScanInput();
framebuf = gfxGetFramebuffer(&framebuf_width, NULL);
memset(framebuf, 51, gfxGetFramebufferSize());
rectangle(marginlr, 140, 1280 - marginlr*2, 84, COLOR_GREY_MEDIUM);
rectangle(0, starty - margintb, 1280, 356, COLOR_GREY_DARKER);
if (str.empty())
{
DrawText(font24, marginlr*2, 160, COLOR_GREY_LIGHT, suggestion.c_str());
}
else
{
DrawText(font24, marginlr*2, 160, COLOR_WHITE, str.c_str());
}
for (size_t i = 0, sz = buttons.size(); i < sz; i++)
{
if (buttons.at(i)->released())
{
if (buttons.at(i)->text().compare("caps") == 0)
{
std::locale loc;
bool islower = std::islower(buttons.at(11)->text()[0], loc);
for (size_t t = 0; t < letters.length(); t++)
{
std::string l = islower ? std::string(1, std::toupper(letters[t], loc)) : std::string(1, std::tolower(letters[t], loc));
buttons.at(t)->text(l);
}
}
else if (buttons.at(i)->text().compare("back") == 0)
{
if (!str.empty())
{
str.erase(str.length() - 1);
}
}
else if (buttons.at(i)->text().compare("space") == 0)
{
if (str.length() < CUSTOM_PATH_LEN)
{
str.append(" ");
}
}
else if (buttons.at(i)->text().compare("return") == 0)
{
//str.append("\n");
}
else if (buttons.at(i)->text().compare("OK") == 0)
{
return str.empty() ? suggestion : str;
}
else if (str.length() < CUSTOM_PATH_LEN)
{
str.append(buttons.at(i)->text());
}
}
buttons.at(i)->draw();
}
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
return suggestion;
}

182
switch/source/hid.cpp Normal file
View file

@ -0,0 +1,182 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "hid.hpp"
static entryType_t type;
static size_t refreshMaxEntries(int page, size_t entries)
{
return (Gui::count(type) - page*entries) > entries ? entries - 1 : Gui::count(type) - page*entries - 1;
}
static void page_back(int& page, int maxpages)
{
if (page > 0)
{
page--;
}
else if (page == 0)
{
page = maxpages - 1;
}
}
static void page_forward(int& page, int maxpages)
{
if (page < maxpages - 1)
{
page++;
}
else if (page == maxpages - 1)
{
page = 0;
}
}
entryType_t hid::entryType(void)
{
return type;
}
void hid::entryType(entryType_t v)
{
type = v;
}
void hid::index(size_t &currentEntry, int &page, size_t maxpages, size_t maxentries, const size_t entries, const size_t columns) {
maxentries--;
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_L)
{
page_back(page, maxpages);
if (currentEntry > refreshMaxEntries(page, entries))
{
currentEntry = refreshMaxEntries(page, entries);
}
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_R)
{
page_forward(page, maxpages);
if (currentEntry > refreshMaxEntries(page, entries))
{
currentEntry = refreshMaxEntries(page, entries);
}
}
if (columns > 1)
{
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_LEFT)
{
if (currentEntry > 0)
{
currentEntry--;
}
else if (currentEntry == 0)
{
page_back(page, maxpages);
currentEntry = refreshMaxEntries(page, entries);
}
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_RIGHT)
{
if (currentEntry < maxentries)
{
currentEntry++;
}
else if (currentEntry == maxentries)
{
page_forward(page, maxpages);
currentEntry = 0;
}
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_UP)
{
if (currentEntry <= columns - 1)
{
page_back(page, maxpages);
if (currentEntry > refreshMaxEntries(page, entries))
{
currentEntry = refreshMaxEntries(page, entries);
}
}
else if (currentEntry >= columns)
{
currentEntry -= columns;
}
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_DOWN)
{
if ((int)(maxentries - columns) >= 0)
{
if (currentEntry <= maxentries - columns)
{
currentEntry += columns;
}
else if (currentEntry >= maxentries - columns + 1)
{
page_forward(page, maxpages);
if (currentEntry > refreshMaxEntries(page, entries))
{
currentEntry = refreshMaxEntries(page, entries);
}
}
}
}
}
else
{
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_UP)
{
if (currentEntry > 0)
{
currentEntry--;
}
else if (currentEntry == 0)
{
page_back(page, maxpages);
currentEntry = refreshMaxEntries(page, entries);
}
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_DOWN)
{
if (currentEntry < maxentries)
{
currentEntry++;
}
else if (currentEntry == maxentries)
{
page_forward(page, maxpages);
currentEntry = 0;
}
}
}
}

85
switch/source/info.cpp Normal file
View file

@ -0,0 +1,85 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "info.hpp"
Info::Info(void)
{
mw = 480;
mh = 150;
mx = (1280 - mw) / 2;
my = 720 - mh - 48;
}
void Info::init(std::string title, std::string message, int ttl, Info_t type)
{
mTTL = ttl;
mType = type;
mRes = 0;
mTitle = title;
mMessage = message;
}
void Info::init(Result res, std::string message, int ttl, Info_t type)
{
mTTL = ttl;
mType = type;
mRes = res;
mMessage = message;
mTitle = StringUtils::format("Error: %08lX", mRes);
}
void Info::draw(void)
{
if ((mType == TYPE_INFO && mTTL > 0) || (mType == TYPE_ERROR && mTTL > 0 && mRes != 0))
{
u32 w, hres = 44, hmessage = 24 * StringUtils::lines(mMessage);
u8 alpha = mTTL > 255 ? 255 : mTTL;
color_t color, bordercolor, bgcolor;
if (mType == TYPE_ERROR)
{
color = MakeColor(255, 255, 255, alpha);
bordercolor = MakeColor(70, 70, 70, alpha);
bgcolor = MakeColor(79, 79, 79, alpha);
}
else if (mType == TYPE_INFO)
{
color = MakeColor(255, 255, 255, alpha);
bordercolor = MakeColor(70, 70, 70, alpha);
bgcolor = MakeColor(138, 138, 138, alpha);
}
GetTextDimensions(font24, mTitle.c_str(), &w, NULL);
const u32 spacing = (mh - hres - hmessage) / 3;
rectangle(mx - 2, my - 2, mw + 4, mh + 4, bordercolor);
rectangle(mx, my, mw, mh, bgcolor);
DrawText(font24, mx + (mw - w) / 2, my + spacing, color, mTitle.c_str());
DrawText(font14, mx + 10, my + 2*spacing + hres, color, mMessage.c_str());
mTTL--;
}
}

321
switch/source/io.cpp Normal file
View file

@ -0,0 +1,321 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "io.hpp"
bool io::fileExists(const std::string& path)
{
struct stat buffer;
return (stat (path.c_str(), &buffer) == 0);
}
void io::copyFile(const std::string& srcPath, const std::string& dstPath)
{
FILE* src = fopen(srcPath.c_str(), "rb");
FILE* dst = fopen(dstPath.c_str(), "wb");
if (src == NULL || dst == NULL)
{
return;
}
fseek(src, 0, SEEK_END);
u64 sz = ftell(src);
rewind(src);
size_t size;
char* buf = new char[BUFFER_SIZE];
u64 offset = 0;
size_t slashpos = srcPath.rfind("/");
std::string name = srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1);
while ((size = fread(buf, 1, BUFFER_SIZE, src)) > 0)
{
fwrite(buf, 1, size, dst);
offset += size;
Gui::drawCopy(name, offset, sz);
}
delete[] buf;
fclose(src);
fclose(dst);
}
Result io::copyDirectory(const std::string& srcPath, const std::string& dstPath)
{
Result res = 0;
bool quit = false;
Directory items(srcPath);
if (!items.good())
{
return items.error();
}
for (size_t i = 0, sz = items.size(); i < sz && !quit; i++)
{
std::string newsrc = srcPath + items.entry(i);
std::string newdst = dstPath + items.entry(i);
if (items.folder(i))
{
res = io::createDirectory(newdst);
if (R_SUCCEEDED(res))
{
newsrc += "/";
newdst += "/";
res = io::copyDirectory(newsrc, newdst);
}
else
{
quit = true;
}
}
else
{
io::copyFile(newsrc, newdst);
}
}
return 0;
}
Result io::createDirectory(const std::string& path)
{
mkdir(path.c_str(), 777);
return 0;
}
bool io::directoryExists(const std::string& path)
{
struct stat sb;
return (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode));
}
// https://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
int io::deleteFolderRecursively(const char* path)
{
DIR *d = opendir(path);
size_t path_len = strlen(path);
int r = -1;
if (d)
{
struct dirent *p;
r = 0;
while (!r && (p=readdir(d)))
{
int r2 = -1;
char *buf;
size_t len;
/* Skip the names "." and ".." as we don't want to recurse on them. */
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
{
continue;
}
len = path_len + strlen(p->d_name) + 2;
buf = new char[len];
if (buf)
{
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);
if (!stat(buf, &statbuf))
{
if (S_ISDIR(statbuf.st_mode))
{
r2 = io::deleteFolderRecursively(buf);
}
else
{
r2 = unlink(buf);
}
}
delete[] buf;
}
r = r2;
}
closedir(d);
}
if (!r)
{
r = rmdir(path);
}
if (!r)
{
return r;
}
return 0;
}
void io::backup(size_t index)
{
// check if multiple selection is enabled and don't ask for confirmation if that's the case
if (!Gui::multipleSelectionEnabled())
{
if (!Gui::askForConfirmation("Backup selected save?"))
{
return;
}
}
const size_t cellIndex = Gui::index(CELLS);
const bool isNewFolder = cellIndex == 0;
Result res = 0;
Title title;
getTitle(title, index);
FsFileSystem fileSystem;
res = FileSystem::mount(&fileSystem, title.id(), title.userId());
if (R_SUCCEEDED(res))
{
int ret = FileSystem::mount(fileSystem);
if (ret == -1)
{
FileSystem::unmount();
Gui::createError(-2, "Failed to mount save.");
return;
}
}
else
{
Gui::createError(res, "Failed to mount save.");
return;
}
std::string suggestion = DateTime::dateTimeStr() + " " + Account::username(title.userId());
std::string customPath;
if (Gui::multipleSelectionEnabled())
{
customPath = isNewFolder ? suggestion : Gui::nameFromCell(CELLS, cellIndex);
}
else
{
customPath = isNewFolder ? hbkbd::keyboard(suggestion) : Gui::nameFromCell(CELLS, cellIndex);
}
std::string dstPath = title.path() + "/" + customPath;
if (!isNewFolder || io::directoryExists(dstPath))
{
int ret = io::deleteFolderRecursively(dstPath.c_str());
if (ret != 0)
{
FileSystem::unmount();
Gui::createError((Result)ret, "Failed to delete the existing backup directory recursively.");
return;
}
}
res = io::createDirectory(dstPath);
res = io::copyDirectory("save:/", dstPath + "/");
if (R_FAILED(res))
{
FileSystem::unmount();
io::deleteFolderRecursively(dstPath.c_str());
Gui::createError(res, "Failed to backup save.");
return;
}
refreshDirectories(title.id());
FileSystem::unmount();
Gui::createInfo("Success!", "Progress correctly saved to disk.");
}
void io::restore(size_t index)
{
const size_t cellIndex = Gui::index(CELLS);
if (cellIndex == 0 || !Gui::askForConfirmation("Restore selected save?"))
{
return;
}
Result res = 0;
Title title;
getTitle(title, index);
FsFileSystem fileSystem;
res = FileSystem::mount(&fileSystem, title.id(), title.userId());
if (R_SUCCEEDED(res))
{
int ret = FileSystem::mount(fileSystem);
if (ret == -1)
{
FileSystem::unmount();
Gui::createError(-2, "Failed to mount save.");
return;
}
}
else
{
Gui::createError(res, "Failed to mount save.");
return;
}
std::string srcPath = title.path() + "/" + Gui::nameFromCell(CELLS, cellIndex) + "/";
std::string dstPath = "save:/";
res = io::deleteFolderRecursively(dstPath.c_str());
if (R_FAILED(res))
{
FileSystem::unmount();
Gui::createError(res, "Failed to delete save.");
return;
}
res = io::copyDirectory(srcPath, dstPath);
if (R_FAILED(res))
{
FileSystem::unmount();
Gui::createError(res, "Failed to restore save.");
return;
}
res = fsdevCommitDevice("save");
if (R_FAILED(res))
{
Gui::createError(res, "Failed to commit to save device.");
}
else
{
Gui::createInfo("Success!", Gui::nameFromCell(CELLS, cellIndex) + " has been restored\nsuccessfully.");
}
FileSystem::unmount();
}

149
switch/source/main.cpp Normal file
View file

@ -0,0 +1,149 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include <switch.h>
#include "thread.hpp"
#include "title.hpp"
#include "util.hpp"
#include "io.hpp"
int main(int argc, char** argv)
{
Result res = servicesInit();
if (R_FAILED(res))
{
servicesExit();
return res;
}
res = Threads::create((ThreadFunc)loadTitles);
if (R_FAILED(res))
{
Gui::createError(res, "Failed to create thread.");
}
int selectionTimer = 0;
while(appletMainLoop() && !(hidKeysDown(CONTROLLER_P1_AUTO) & KEY_PLUS))
{
hidScanInput();
u32 kdown = hidKeysDown(CONTROLLER_P1_AUTO);
if (kdown & KEY_A || kdown & KEY_RIGHT)
{
Gui::backupScroll(true);
Gui::updateButtonsColor();
hid::entryType(CELLS);
}
if (kdown & KEY_B || kdown & KEY_LEFT)
{
Gui::backupScroll(false);
Gui::updateButtonsColor();
hid::entryType(TITLES);
Gui::clearSelectedEntries();
}
if (kdown & KEY_X)
{
if (Gui::backupScroll())
{
size_t index = Gui::index(CELLS);
if (index > 0 && Gui::askForConfirmation("Delete selected backup?"))
{
Title title;
getTitle(title, Gui::index(TITLES));
std::vector<std::string> list = title.saves();
std::string path = title.path() + "/" + list.at(index);
io::deleteFolderRecursively(path.c_str());
refreshDirectories(title.id());
Gui::index(CELLS, index - 1);
}
}
}
if (kdown & KEY_Y && !(Gui::backupScroll()))
{
Gui::addSelectedEntry(Gui::index(TITLES));
}
if (hidKeysHeld(CONTROLLER_P1_AUTO) & KEY_Y && !(Gui::backupScroll()))
{
selectionTimer++;
}
else
{
selectionTimer = 0;
}
if (selectionTimer > 90)
{
Gui::clearSelectedEntries();
for (size_t i = 0, sz = getTitleCount(); i < sz; i++)
{
Gui::addSelectedEntry(i);
}
selectionTimer = 0;
}
if (Gui::isBackupReleased())
{
if (Gui::multipleSelectionEnabled())
{
Gui::resetIndex(CELLS);
std::vector<size_t> list = Gui::selectedEntries();
for (size_t i = 0, sz = list.size(); i < sz; i++)
{
io::backup(list.at(i));
}
Gui::clearSelectedEntries();
}
else
{
io::backup(Gui::index(TITLES));
}
}
if (Gui::isRestoreReleased())
{
if (Gui::multipleSelectionEnabled())
{
Gui::clearSelectedEntries();
}
else
{
io::restore(Gui::index(TITLES));
}
}
Gui::updateSelector();
Gui::draw();
}
Threads::destroy();
servicesExit();
return 0;
}

View file

@ -0,0 +1,73 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "messagebox.hpp"
MessageBox::MessageBox(color_t colorBg, color_t colorText)
{
mColorBg = colorBg;
mColorText = colorText;
mList.clear();
}
void MessageBox::push_message(const std::string& newmessage)
{
mList.push_back(newmessage);
}
void MessageBox::clear(void)
{
mList.clear();
}
void MessageBox::draw(void)
{
static const u32 spacingFromEdges = 20;
u32 w = 0, mh = 30;
u32 widths[mList.size()];
for (size_t i = 0, sz = mList.size(); i < sz; i++)
{
GetTextDimensions(font20, mList.at(i).c_str(), &widths[i], NULL);
if (widths[i] > w)
{
w = widths[i];
}
}
w += 2*spacingFromEdges;
const u32 h = mh*mList.size() + 2*spacingFromEdges;
const u32 x = (1280-w)/2;
const u32 y = (720-h)/2;
rectangle(x - 2, y - 2, w + 4, h + 4, COLOR_BLACK);
rectangle(x, y, w, h, mColorBg);
for (size_t i = 0, sz = mList.size(); i < sz; i++)
{
DrawText(font20, ceil((1280 - widths[i]) / 2), y + spacingFromEdges + mh*i, mColorText, mList.at(i).c_str());
}
}

916
switch/source/nanojpeg.c Normal file
View file

@ -0,0 +1,916 @@
// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
// version 1.3.5 (2016-11-14)
// Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler@gmx.net>
// published under the terms of the MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
// DOCUMENTATION SECTION //
// read this if you want to know what this is all about //
///////////////////////////////////////////////////////////////////////////////
// INTRODUCTION
// ============
//
// This is a minimal decoder for baseline JPEG images. It accepts memory dumps
// of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
// RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
// are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
// not supported. All YCbCr subsampling schemes with power-of-two ratios are
// supported, as are restart intervals. Progressive or lossless JPEG is not
// supported.
// Summed up, NanoJPEG should be able to decode all images from digital cameras
// and most common forms of other non-progressive JPEG images.
// The decoder is not optimized for speed, it's optimized for simplicity and
// small code. Image quality should be at a reasonable level. A bicubic chroma
// upsampling filter ensures that subsampled YCbCr images are rendered in
// decent quality. The decoder is not meant to deal with broken JPEG files in
// a graceful manner; if anything is wrong with the bitstream, decoding will
// simply fail.
// The code should work with every modern C compiler without problems and
// should not emit any warnings. It uses only (at least) 32-bit integer
// arithmetic and is supposed to be endianness independent and 64-bit clean.
// However, it is not thread-safe.
// COMPILE-TIME CONFIGURATION
// ==========================
//
// The following aspects of NanoJPEG can be controlled with preprocessor
// defines:
//
// _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example
// program.
// _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
// file for NanoJPEG. Example:
// #define _NJ_INCLUDE_HEADER_ONLY
// #include "nanojpeg.c"
// int main(void) {
// njInit();
// // your code here
// njDone();
// }
// NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy()
// functions from the standard C library (default).
// NJ_USE_LIBC=0 = Don't use the standard C library. In this mode,
// external functions njAlloc(), njFreeMem(),
// njFillMem() and njCopyMem() need to be defined
// and implemented somewhere.
// NJ_USE_WIN32=0 = Normal mode (default).
// NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and
// NJ_USE_LIBC=0, NanoJPEG will use its own
// implementations of the required C library
// functions (default if compiling with MSVC and
// NJ_USE_LIBC=0).
// NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter
// (default).
// NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling
// (bad quality, but faster and less code).
// API
// ===
//
// For API documentation, read the "header section" below.
// EXAMPLE
// =======
//
// A few pages below, you can find an example program that uses NanoJPEG to
// convert JPEG files into PGM or PPM. To compile it, use something like
// gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
// You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
// The only thing you might need is -Wno-shift-negative-value, because this
// code relies on the target machine using two's complement arithmetic, but
// the C standard does not, even though *any* practically useful machine
// nowadays uses two's complement.
///////////////////////////////////////////////////////////////////////////////
// HEADER SECTION //
// copy and pase this into nanojpeg.h if you want //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
NJ_NO_JPEG, // not a JPEG file
NJ_UNSUPPORTED, // unsupported format
NJ_OUT_OF_MEM, // out of memory
NJ_INTERNAL_ERR, // internal error
NJ_SYNTAX_ERROR, // syntax error
__NJ_FINISHED, // used internally, will never be reported
} nj_result_t;
// njInit: Initialize NanoJPEG.
// For safety reasons, this should be called at least one time before using
// using any of the other NanoJPEG functions.
void njInit(void);
// njDecode: Decode a JPEG image.
// Decodes a memory dump of a JPEG file into internal buffers.
// Parameters:
// jpeg = The pointer to the memory dump.
// size = The size of the JPEG file.
// Return value: The error code in case of failure, or NJ_OK (zero) on success.
nj_result_t njDecode(const void* jpeg, const int size);
// njGetWidth: Return the width (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetWidth() is undefined.
int njGetWidth(void);
// njGetHeight: Return the height (in pixels) of the most recently decoded
// image. If njDecode() failed, the result of njGetHeight() is undefined.
int njGetHeight(void);
// njIsColor: Return 1 if the most recently decoded image is a color image
// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
// of njGetWidth() is undefined.
int njIsColor(void);
// njGetImage: Returns the decoded image data.
// Returns a pointer to the most recently image. The memory layout it byte-
// oriented, top-down, without any padding between lines. Pixels of color
// images will be stored as three consecutive bytes for the red, green and
// blue channels. This data format is thus compatible with the PGM or PPM
// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
// If njDecode() failed, the result of njGetImage() is undefined.
unsigned char* njGetImage(void);
// njGetImageSize: Returns the size (in bytes) of the image data returned
// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
// undefined.
int njGetImageSize(void);
// njDone: Uninitialize NanoJPEG.
// Resets NanoJPEG's internal state and frees all memory that has been
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
#endif//_NANOJPEG_H
///////////////////////////////////////////////////////////////////////////////
// CONFIGURATION SECTION //
// adjust the default settings for the NJ_ defines here //
///////////////////////////////////////////////////////////////////////////////
#ifndef NJ_USE_LIBC
#define NJ_USE_LIBC 1
#endif
#ifndef NJ_USE_WIN32
#ifdef _MSC_VER
#define NJ_USE_WIN32 (!NJ_USE_LIBC)
#else
#define NJ_USE_WIN32 0
#endif
#endif
#ifndef NJ_CHROMA_FILTER
#define NJ_CHROMA_FILTER 1
#endif
///////////////////////////////////////////////////////////////////////////////
// EXAMPLE PROGRAM //
// just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) //
///////////////////////////////////////////////////////////////////////////////
#ifdef _NJ_EXAMPLE_PROGRAM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
int size;
char *buf;
FILE *f;
if (argc < 2) {
printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
return 2;
}
f = fopen(argv[1], "rb");
if (!f) {
printf("Error opening the input file.\n");
return 1;
}
fseek(f, 0, SEEK_END);
size = (int) ftell(f);
buf = (char*) malloc(size);
fseek(f, 0, SEEK_SET);
size = (int) fread(buf, 1, size, f);
fclose(f);
njInit();
if (njDecode(buf, size)) {
free((void*)buf);
printf("Error decoding the input file.\n");
return 1;
}
free((void*)buf);
f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
if (!f) {
printf("Error opening the output file.\n");
return 1;
}
fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
fwrite(njGetImage(), 1, njGetImageSize(), f);
fclose(f);
njDone();
return 0;
}
#endif
///////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION SECTION //
// you may stop reading here //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NJ_INCLUDE_HEADER_ONLY
#ifdef _MSC_VER
#define NJ_INLINE static __inline
#define NJ_FORCE_INLINE static __forceinline
#else
#define NJ_INLINE static inline
#define NJ_FORCE_INLINE static inline
#endif
#if NJ_USE_LIBC
#include <stdlib.h>
#include <string.h>
#define njAllocMem malloc
#define njFreeMem free
#define njFillMem memset
#define njCopyMem memcpy
#elif NJ_USE_WIN32
#include <windows.h>
#define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
#define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
mov edi, block
mov al, value
mov ecx, count
rep stosb
} }
NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
mov edi, dest
mov esi, src
mov ecx, count
rep movsb
} }
#else
extern void* njAllocMem(int size);
extern void njFreeMem(void* block);
extern void njFillMem(void* block, unsigned char byte, int size);
extern void njCopyMem(void* dest, const void* src, int size);
#endif
typedef struct _nj_code {
unsigned char bits, code;
} nj_vlc_code_t;
typedef struct _nj_cmp {
int cid;
int ssx, ssy;
int width, height;
int stride;
int qtsel;
int actabsel, dctabsel;
int dcpred;
unsigned char *pixels;
} nj_component_t;
typedef struct _nj_ctx {
nj_result_t error;
const unsigned char *pos;
int size;
int length;
int width, height;
int mbwidth, mbheight;
int mbsizex, mbsizey;
int ncomp;
nj_component_t comp[3];
int qtused, qtavail;
unsigned char qtab[4][64];
nj_vlc_code_t vlctab[4][65536];
int buf, bufbits;
int block[64];
int rstinterval;
unsigned char *rgb;
} nj_context_t;
static nj_context_t nj;
static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
NJ_FORCE_INLINE unsigned char njClip(const int x) {
return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
}
#define W1 2841
#define W2 2676
#define W3 2408
#define W5 1609
#define W6 1108
#define W7 565
NJ_INLINE void njRowIDCT(int* blk) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[4] << 11)
| (x2 = blk[6])
| (x3 = blk[2])
| (x4 = blk[1])
| (x5 = blk[7])
| (x6 = blk[5])
| (x7 = blk[3])))
{
blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
return;
}
x0 = (blk[0] << 11) + 128;
x8 = W7 * (x4 + x5);
x4 = x8 + (W1 - W7) * x4;
x5 = x8 - (W1 + W7) * x5;
x8 = W3 * (x6 + x7);
x6 = x8 - (W3 - W5) * x6;
x7 = x8 - (W3 + W5) * x7;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2);
x2 = x1 - (W2 + W6) * x2;
x3 = x1 + (W2 - W6) * x3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
blk[0] = (x7 + x1) >> 8;
blk[1] = (x3 + x2) >> 8;
blk[2] = (x0 + x4) >> 8;
blk[3] = (x8 + x6) >> 8;
blk[4] = (x8 - x6) >> 8;
blk[5] = (x0 - x4) >> 8;
blk[6] = (x3 - x2) >> 8;
blk[7] = (x7 - x1) >> 8;
}
NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
if (!((x1 = blk[8*4] << 8)
| (x2 = blk[8*6])
| (x3 = blk[8*2])
| (x4 = blk[8*1])
| (x5 = blk[8*7])
| (x6 = blk[8*5])
| (x7 = blk[8*3])))
{
x1 = njClip(((blk[0] + 32) >> 6) + 128);
for (x0 = 8; x0; --x0) {
*out = (unsigned char) x1;
out += stride;
}
return;
}
x0 = (blk[0] << 8) + 8192;
x8 = W7 * (x4 + x5) + 4;
x4 = (x8 + (W1 - W7) * x4) >> 3;
x5 = (x8 - (W1 + W7) * x5) >> 3;
x8 = W3 * (x6 + x7) + 4;
x6 = (x8 - (W3 - W5) * x6) >> 3;
x7 = (x8 - (W3 + W5) * x7) >> 3;
x8 = x0 + x1;
x0 -= x1;
x1 = W6 * (x3 + x2) + 4;
x2 = (x1 - (W2 + W6) * x2) >> 3;
x3 = (x1 + (W2 - W6) * x3) >> 3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
x5 -= x7;
x7 = x8 + x3;
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (181 * (x4 + x5) + 128) >> 8;
x4 = (181 * (x4 - x5) + 128) >> 8;
*out = njClip(((x7 + x1) >> 14) + 128); out += stride;
*out = njClip(((x3 + x2) >> 14) + 128); out += stride;
*out = njClip(((x0 + x4) >> 14) + 128); out += stride;
*out = njClip(((x8 + x6) >> 14) + 128); out += stride;
*out = njClip(((x8 - x6) >> 14) + 128); out += stride;
*out = njClip(((x0 - x4) >> 14) + 128); out += stride;
*out = njClip(((x3 - x2) >> 14) + 128); out += stride;
*out = njClip(((x7 - x1) >> 14) + 128);
}
#define njThrow(e) do { nj.error = e; return; } while (0)
#define njCheckError() do { if (nj.error) return; } while (0)
static int njShowBits(int bits) {
unsigned char newbyte;
if (!bits) return 0;
while (nj.bufbits < bits) {
if (nj.size <= 0) {
nj.buf = (nj.buf << 8) | 0xFF;
nj.bufbits += 8;
continue;
}
newbyte = *nj.pos++;
nj.size--;
nj.bufbits += 8;
nj.buf = (nj.buf << 8) | newbyte;
if (newbyte == 0xFF) {
if (nj.size) {
unsigned char marker = *nj.pos++;
nj.size--;
switch (marker) {
case 0x00:
case 0xFF:
break;
case 0xD9: nj.size = 0; break;
default:
if ((marker & 0xF8) != 0xD0)
nj.error = NJ_SYNTAX_ERROR;
else {
nj.buf = (nj.buf << 8) | marker;
nj.bufbits += 8;
}
}
} else
nj.error = NJ_SYNTAX_ERROR;
}
}
return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1);
}
NJ_INLINE void njSkipBits(int bits) {
if (nj.bufbits < bits)
(void) njShowBits(bits);
nj.bufbits -= bits;
}
NJ_INLINE int njGetBits(int bits) {
int res = njShowBits(bits);
njSkipBits(bits);
return res;
}
NJ_INLINE void njByteAlign(void) {
nj.bufbits &= 0xF8;
}
static void njSkip(int count) {
nj.pos += count;
nj.size -= count;
nj.length -= count;
if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR;
}
NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
return (pos[0] << 8) | pos[1];
}
static void njDecodeLength(void) {
if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR);
nj.length = njDecode16(nj.pos);
if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR);
njSkip(2);
}
NJ_INLINE void njSkipMarker(void) {
njDecodeLength();
njSkip(nj.length);
}
NJ_INLINE void njDecodeSOF(void) {
int i, ssxmax = 0, ssymax = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED);
nj.height = njDecode16(nj.pos+1);
nj.width = njDecode16(nj.pos+3);
if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR);
nj.ncomp = nj.pos[5];
njSkip(6);
switch (nj.ncomp) {
case 1:
case 3:
break;
default:
njThrow(NJ_UNSUPPORTED);
}
if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->cid = nj.pos[0];
if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR);
if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR);
njSkip(3);
nj.qtused |= 1 << c->qtsel;
if (c->ssx > ssxmax) ssxmax = c->ssx;
if (c->ssy > ssymax) ssymax = c->ssy;
}
if (nj.ncomp == 1) {
c = nj.comp;
c->ssx = c->ssy = ssxmax = ssymax = 1;
}
nj.mbsizex = ssxmax << 3;
nj.mbsizey = ssymax << 3;
nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex;
nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax;
c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
c->stride = nj.mbwidth * c->ssx << 3;
if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED);
if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * c->ssy << 3))) njThrow(NJ_OUT_OF_MEM);
}
if (nj.ncomp == 3) {
nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
}
njSkip(nj.length);
}
NJ_INLINE void njDecodeDHT(void) {
int codelen, currcnt, remain, spread, i, j;
nj_vlc_code_t *vlc;
static unsigned char counts[16];
njDecodeLength();
njCheckError();
while (nj.length >= 17) {
i = nj.pos[0];
if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
if (i & 0x02) njThrow(NJ_UNSUPPORTED);
i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value
for (codelen = 1; codelen <= 16; ++codelen)
counts[codelen - 1] = nj.pos[codelen];
njSkip(17);
vlc = &nj.vlctab[i][0];
remain = spread = 65536;
for (codelen = 1; codelen <= 16; ++codelen) {
spread >>= 1;
currcnt = counts[codelen - 1];
if (!currcnt) continue;
if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR);
remain -= currcnt << (16 - codelen);
if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
for (i = 0; i < currcnt; ++i) {
register unsigned char code = nj.pos[i];
for (j = spread; j; --j) {
vlc->bits = (unsigned char) codelen;
vlc->code = code;
++vlc;
}
}
njSkip(currcnt);
}
while (remain--) {
vlc->bits = 0;
++vlc;
}
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDQT(void) {
int i;
unsigned char *t;
njDecodeLength();
njCheckError();
while (nj.length >= 65) {
i = nj.pos[0];
if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
nj.qtavail |= 1 << i;
t = &nj.qtab[i][0];
for (i = 0; i < 64; ++i)
t[i] = nj.pos[i + 1];
njSkip(65);
}
if (nj.length) njThrow(NJ_SYNTAX_ERROR);
}
NJ_INLINE void njDecodeDRI(void) {
njDecodeLength();
njCheckError();
if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
nj.rstinterval = njDecode16(nj.pos);
njSkip(nj.length);
}
static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) {
int value = njShowBits(16);
int bits = vlc[value].bits;
if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; }
njSkipBits(bits);
value = vlc[value].code;
if (code) *code = (unsigned char) value;
bits = value & 15;
if (!bits) return 0;
value = njGetBits(bits);
if (value < (1 << (bits - 1)))
value += ((-1) << bits) + 1;
return value;
}
NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) {
unsigned char code = 0;
int value, coef = 0;
njFillMem(nj.block, 0, sizeof(nj.block));
c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL);
nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0];
do {
value = njGetVLC(&nj.vlctab[c->actabsel][0], &code);
if (!code) break; // EOB
if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR);
coef += (code >> 4) + 1;
if (coef > 63) njThrow(NJ_SYNTAX_ERROR);
nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef];
} while (coef < 63);
for (coef = 0; coef < 64; coef += 8)
njRowIDCT(&nj.block[coef]);
for (coef = 0; coef < 8; ++coef)
njColIDCT(&nj.block[coef], &out[coef], c->stride);
}
NJ_INLINE void njDecodeScan(void) {
int i, mbx, mby, sbx, sby;
int rstcount = nj.rstinterval, nextrst = 0;
nj_component_t* c;
njDecodeLength();
njCheckError();
if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
njSkip(1);
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR);
if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR);
c->dctabsel = nj.pos[1] >> 4;
c->actabsel = (nj.pos[1] & 1) | 2;
njSkip(2);
}
if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED);
njSkip(nj.length);
for (mbx = mby = 0;;) {
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c)
for (sby = 0; sby < c->ssy; ++sby)
for (sbx = 0; sbx < c->ssx; ++sbx) {
njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]);
njCheckError();
}
if (++mbx >= nj.mbwidth) {
mbx = 0;
if (++mby >= nj.mbheight) break;
}
if (nj.rstinterval && !(--rstcount)) {
njByteAlign();
i = njGetBits(16);
if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR);
nextrst = (nextrst + 1) & 7;
rstcount = nj.rstinterval;
for (i = 0; i < 3; ++i)
nj.comp[i].dcpred = 0;
}
}
nj.error = __NJ_FINISHED;
}
#if NJ_CHROMA_FILTER
#define CF4A (-9)
#define CF4B (111)
#define CF4C (29)
#define CF4D (-3)
#define CF3A (28)
#define CF3B (109)
#define CF3C (-9)
#define CF3X (104)
#define CF3Y (27)
#define CF3Z (-3)
#define CF2A (139)
#define CF2B (-11)
#define CF(x) njClip(((x) + 64) >> 7)
NJ_INLINE void njUpsampleH(nj_component_t* c) {
const int xmax = c->width - 3;
unsigned char *out, *lin, *lout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = c->height; y; --y) {
lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]);
lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]);
lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]);
for (x = 0; x < xmax; ++x) {
lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]);
lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]);
}
lin += c->stride;
lout += c->width << 1;
lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]);
lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]);
lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]);
}
c->width <<= 1;
c->stride = c->width;
njFreeMem((void*)c->pixels);
c->pixels = out;
}
NJ_INLINE void njUpsampleV(nj_component_t* c) {
const int w = c->width, s1 = c->stride, s2 = s1 + s1;
unsigned char *out, *cin, *cout;
int x, y;
out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
for (x = 0; x < w; ++x) {
cin = &c->pixels[x];
cout = &out[x];
*cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w;
*cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w;
cin += s1;
for (y = c->height - 3; y; --y) {
*cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w;
*cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w;
cin += s1;
}
cin += s1;
*cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w;
*cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w;
*cout = CF(CF2A * cin[0] + CF2B * cin[-s1]);
}
c->height <<= 1;
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#else
NJ_INLINE void njUpsample(nj_component_t* c) {
int x, y, xshift = 0, yshift = 0;
unsigned char *out, *lin, *lout;
while (c->width < nj.width) { c->width <<= 1; ++xshift; }
while (c->height < nj.height) { c->height <<= 1; ++yshift; }
out = (unsigned char*) njAllocMem(c->width * c->height);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
for (y = 0; y < c->height; ++y) {
lin = &c->pixels[(y >> yshift) * c->stride];
for (x = 0; x < c->width; ++x)
lout[x] = lin[x >> xshift];
lout += c->width;
}
c->stride = c->width;
njFreeMem((void*) c->pixels);
c->pixels = out;
}
#endif
NJ_INLINE void njConvert(void) {
int i;
nj_component_t* c;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
#if NJ_CHROMA_FILTER
while ((c->width < nj.width) || (c->height < nj.height)) {
if (c->width < nj.width) njUpsampleH(c);
njCheckError();
if (c->height < nj.height) njUpsampleV(c);
njCheckError();
}
#else
if ((c->width < nj.width) || (c->height < nj.height))
njUpsample(c);
#endif
if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR);
}
if (nj.ncomp == 3) {
// convert to RGB
int x, yy;
unsigned char *prgb = nj.rgb;
const unsigned char *py = nj.comp[0].pixels;
const unsigned char *pcb = nj.comp[1].pixels;
const unsigned char *pcr = nj.comp[2].pixels;
for (yy = nj.height; yy; --yy) {
for (x = 0; x < nj.width; ++x) {
register int y = py[x] << 8;
register int cb = pcb[x] - 128;
register int cr = pcr[x] - 128;
*prgb++ = njClip((y + 359 * cr + 128) >> 8);
*prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8);
*prgb++ = njClip((y + 454 * cb + 128) >> 8);
}
py += nj.comp[0].stride;
pcb += nj.comp[1].stride;
pcr += nj.comp[2].stride;
}
} else if (nj.comp[0].width != nj.comp[0].stride) {
// grayscale -> only remove stride
unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
int y;
for (y = nj.comp[0].height - 1; y; --y) {
njCopyMem(pout, pin, nj.comp[0].width);
pin += nj.comp[0].stride;
pout += nj.comp[0].width;
}
nj.comp[0].stride = nj.comp[0].width;
}
}
void njInit(void) {
njFillMem(&nj, 0, sizeof(nj_context_t));
}
void njDone(void) {
int i;
for (i = 0; i < 3; ++i)
if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels);
if (nj.rgb) njFreeMem((void*) nj.rgb);
njInit();
}
nj_result_t njDecode(const void* jpeg, const int size) {
njDone();
nj.pos = (const unsigned char*) jpeg;
nj.size = size & 0x7FFFFFFF;
if (nj.size < 2) return NJ_NO_JPEG;
if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG;
njSkip(2);
while (!nj.error) {
if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR;
njSkip(2);
switch (nj.pos[-1]) {
case 0xC0: njDecodeSOF(); break;
case 0xC4: njDecodeDHT(); break;
case 0xDB: njDecodeDQT(); break;
case 0xDD: njDecodeDRI(); break;
case 0xDA: njDecodeScan(); break;
case 0xFE: njSkipMarker(); break;
default:
if ((nj.pos[-1] & 0xF0) == 0xE0)
njSkipMarker();
else
return NJ_UNSUPPORTED;
}
}
if (nj.error != __NJ_FINISHED) return nj.error;
nj.error = NJ_OK;
njConvert();
return nj.error;
}
int njGetWidth(void) { return nj.width; }
int njGetHeight(void) { return nj.height; }
int njIsColor(void) { return (nj.ncomp != 1); }
unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; }
#endif // _NJ_INCLUDE_HEADER_ONLY

View file

@ -0,0 +1,131 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "scrollable.hpp"
std::string Scrollable::cellName(size_t index)
{
return mCells.at(index)->text();
}
Scrollable::Scrollable(u32 _x, u32 _y, u32 _w, u32 _h, size_t _visibleEntries)
{
mx = _x;
my = _y;
mw = _w;
mh = _h;
mVisibleEntries = _visibleEntries;
mIndex = 0;
mPage = 0;
}
Scrollable::~Scrollable(void)
{
flush();
}
void Scrollable::push_back(color_t color, color_t colorMessage, const std::string& message)
{
static const float spacing = mh / mVisibleEntries;
Clickable* cell = new Clickable(mx, my + (size() % mVisibleEntries)*spacing, mw, spacing, color, colorMessage, message, false);
mCells.push_back(cell);
}
void Scrollable::flush(void)
{
for (size_t i = 0, sz = size(); i < sz; i++)
{
delete mCells[i];
}
mCells.clear();
}
size_t Scrollable::size(void)
{
return mCells.size();
}
int Scrollable::page(void)
{
return mPage;
}
size_t Scrollable::maxVisibleEntries(void)
{
return (size() - mPage*mVisibleEntries) > mVisibleEntries ? mVisibleEntries : size() - mPage*mVisibleEntries;
}
size_t Scrollable::index(void)
{
return mIndex + mPage*mVisibleEntries;
}
void Scrollable::invertCellColors(size_t i)
{
if (i < size())
{
mCells.at(i)->invertColors();
}
}
void Scrollable::resetIndex(void)
{
mIndex = 0;
mPage = 0;
}
void Scrollable::index(size_t i)
{
mPage = i / mVisibleEntries;
mIndex = i - mPage * mVisibleEntries;
}
void Scrollable::updateSelection(void)
{
touchPosition touch;
hidTouchRead(&touch, 0);
const size_t maxentries = maxVisibleEntries();
const size_t maxpages = (size() % mVisibleEntries == 0) ? size() / mVisibleEntries : size() / mVisibleEntries + 1;
const u32 hu = maxentries * mh / mVisibleEntries;
if (hidKeysHeld(CONTROLLER_P1_AUTO) & KEY_TOUCH && touch.py > my && touch.py < my+hu && touch.px > mx && touch.px < mx+mw)
{
mIndex = (size_t)((touch.py - my)*mVisibleEntries/mh);
}
hid::index(mIndex, mPage, maxpages, maxentries, mVisibleEntries, 1);
}
void Scrollable::draw(void)
{
const size_t baseIndex = mVisibleEntries*mPage;
const size_t sz = size() - baseIndex > mVisibleEntries ? mVisibleEntries : size() - baseIndex;
for (size_t i = baseIndex; i < baseIndex + sz; i++)
{
mCells.at(i)->draw(font14, 24);
}
}

57
switch/source/thread.cpp Normal file
View file

@ -0,0 +1,57 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "thread.hpp"
static std::vector<Thread> threads;
Result Threads::create(ThreadFunc entrypoint)
{
Thread thread;
Result res = threadCreate(&thread, entrypoint, NULL, 16*1024, 0x2B, -2);
if (R_FAILED(res))
{
return res;
}
res = threadStart(&thread);
if (R_FAILED(res))
{
return res;
}
threads.push_back(thread);
return 0;
}
void Threads::destroy(void)
{
for (u32 i = 0; i < threads.size(); i++)
{
threadWaitForExit(&threads.at(i));
threadClose(&threads.at(i));
}
}

217
switch/source/title.cpp Normal file
View file

@ -0,0 +1,217 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "title.hpp"
static std::vector<Title> titles;
static void loadIcon(Title& title, NsApplicationControlData* nsacd, size_t iconsize)
{
uint8_t* imageptr = NULL;
size_t imagesize = 256*256*3;
njInit();
if (njDecode(nsacd->icon, iconsize) != NJ_OK)
{
njDone();
return;
}
if (njGetWidth() != 256 || njGetHeight() != 256 || (size_t)njGetImageSize() != imagesize || njIsColor() != 1)
{
njDone();
return;
}
imageptr = njGetImage();
if (imageptr == NULL)
{
njDone();
return;
}
title.icon(imageptr, imagesize);
imageptr = NULL;
njDone();
}
void Title::init(u64 id, u128 userID, const std::string& name)
{
mId = id;
mUserId = userID;
mName = StringUtils::containsInvalidChar(name) ? StringUtils::format("0x%016llX", mId) : StringUtils::removeForbiddenCharacters(name);
mPath = "sdmc:/switch/Checkpoint/saves/" + StringUtils::format("0x%016llX", mId) + " " + mName;
mIcon = NULL;
if (!io::directoryExists(mPath))
{
io::createDirectory(mPath);
}
refreshDirectories();
}
u64 Title::id(void)
{
return mId;
}
u128 Title::userId(void)
{
return mUserId;
}
std::string Title::name(void)
{
return mName;
}
std::string Title::path(void)
{
return mPath;
}
std::vector<std::string> Title::saves()
{
return mSaves;
}
u8* Title::icon(void)
{
return mIcon;
}
void Title::icon(u8* data, size_t iconsize)
{
mIcon = (u8*)malloc(iconsize);
std::copy(data, data + iconsize, mIcon);
}
void Title::refreshDirectories(void)
{
mSaves.clear();
Directory savelist(mPath);
if (savelist.good())
{
for (size_t i = 0, sz = savelist.size(); i < sz; i++)
{
if (savelist.folder(i))
{
mSaves.push_back(savelist.entry(i));
}
}
std::sort(mSaves.rbegin(), mSaves.rend());
mSaves.insert(mSaves.begin(), "New...");
}
else
{
Gui::createError(savelist.error(), "Couldn't retrieve the directory list for the title " + name() + ".");
}
}
void loadTitles(void)
{
titles.clear();
FsSaveDataIterator iterator;
FsSaveDataInfo info;
size_t total_entries = 0;
size_t outsize = 0;
NacpLanguageEntry* nle = NULL;
NsApplicationControlData* nsacd = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
if (nsacd == NULL)
{
return;
}
memset(nsacd, 0, sizeof(NsApplicationControlData));
Result res = fsOpenSaveDataIterator(&iterator, FsSaveDataSpaceId_NandUser);
if (R_FAILED(res))
{
return;
}
while(1)
{
res = fsSaveDataIteratorRead(&iterator, &info, 1, &total_entries);
if (R_FAILED(res) || total_entries == 0)
{
break;
}
if (info.SaveDataType == FsSaveDataType_SaveData)
{
u64 tid = info.titleID;
u128 uid = info.userID;
res = nsGetApplicationControlData(1, tid, nsacd, sizeof(NsApplicationControlData), &outsize);
if (R_SUCCEEDED(res) && !(outsize < sizeof(nsacd->nacp)))
{
res = nacpGetLanguageEntry(&nsacd->nacp, &nle);
if (R_SUCCEEDED(res) && nle != NULL)
{
Title title;
title.init(tid, uid, std::string(nle->name));
loadIcon(title, nsacd, outsize - sizeof(nsacd->nacp));
titles.push_back(title);
}
}
nle = NULL;
}
}
free(nsacd);
fsSaveDataIteratorClose(&iterator);
std::sort(titles.begin(), titles.end(), [](Title l, Title r) {
return l.name() < r.name();
});
}
void getTitle(Title &dst, size_t i)
{
if (i < getTitleCount())
{
dst = titles.at(i);
}
}
size_t getTitleCount(void)
{
return titles.size();
}
void refreshDirectories(u64 id)
{
for (size_t i = 0; i < titles.size(); i++)
{
if (titles.at(i).id() == id)
{
titles.at(i).refreshDirectories();
}
}
}

152
switch/source/util.cpp Normal file
View file

@ -0,0 +1,152 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "util.hpp"
void servicesExit(void)
{
nsExit();
Account::exit();
hbkbd::exit();
Gui::exit();
}
Result servicesInit(void)
{
Result res = 0;
res = io::createDirectory("sdmc:/switch");
res = io::createDirectory("sdmc:/switch/Checkpoint");
res = io::createDirectory("sdmc:/switch/Checkpoint/saves");
if (R_FAILED(res))
{
return res;
}
if (R_FAILED(res = Account::init()))
{
return res;
}
if (R_FAILED(res = nsInitialize()))
{
return res;
}
Gui::init();
hbkbd::init();
return 0;
}
std::string DateTime::timeStr(void)
{
time_t unixTime = time(NULL);
struct tm* timeStruct = gmtime((const time_t*)&unixTime);
return StringUtils::format("%02i:%02i:%02i", timeStruct->tm_hour, timeStruct->tm_min, timeStruct->tm_sec);
}
std::string DateTime::dateTimeStr(void)
{
time_t unixTime = time(NULL);
struct tm* timeStruct = gmtime((const time_t*)&unixTime);
return StringUtils::format("%04i%02i%02i-%02i%02i%02i", timeStruct->tm_year + 1900, timeStruct->tm_mon + 1, timeStruct->tm_mday, timeStruct->tm_hour, timeStruct->tm_min, timeStruct->tm_sec);
}
bool StringUtils::containsInvalidChar(const std::string& str)
{
for (size_t i = 0, sz = str.length(); i < sz; i++)
{
if (!isascii(str[i]))
{
return true;
}
}
return false;
}
std::string StringUtils::removeForbiddenCharacters(std::string src)
{
static const std::string illegalChars = ".,!\\/:?*\"<>|";
for (size_t i = 0; i < src.length(); i++)
{
if (illegalChars.find(src[i]) != std::string::npos)
{
src[i] = ' ';
}
}
size_t i;
for (i = src.length() - 1; i > 0 && src[i] == ' '; i--);
src.erase(i + 1, src.length() - i);
return src;
}
// https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf
std::string StringUtils::format(const std::string fmt_str, ...)
{
int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while(1)
{
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}
std::string StringUtils::sizeString(double size)
{
int i = 0;
static const char* units[] = {"B", "kB", "MB", "GB"};
while (size > 1024)
{
size /= 1024;
i++;
}
return StringUtils::format("%.*f %s", i, size, units[i]);
}
size_t StringUtils::lines(const std::string& str)
{
size_t n = 1;
for (size_t i = 0, sz = str.length(); i < sz; i++)
{
if (str[i] == '\n')
{
n++;
}
}
return n;
}