mirror of
https://github.com/wiiu-env/SDHotSwapModule.git
synced 2024-11-13 07:05:14 +01:00
First commit
This commit is contained in:
commit
cd19e89db5
67
.clang-format
Normal file
67
.clang-format
Normal file
@ -0,0 +1,67 @@
|
||||
# Generated from CLion C/C++ Code Style settings
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 8
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
65
.github/workflows/ci.yml
vendored
Normal file
65
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: CI-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: clang-format
|
||||
run: |
|
||||
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: build binary
|
||||
run: |
|
||||
docker build . -t builder
|
||||
docker run --rm -v ${PWD}:/project builder make
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: binary
|
||||
path: "*.wms"
|
||||
deploy-binary:
|
||||
needs: build-binary
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Get environment variables
|
||||
id: get_repository_name
|
||||
run: |
|
||||
echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV
|
||||
echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV
|
||||
- uses: actions/download-artifact@master
|
||||
with:
|
||||
name: binary
|
||||
- name: zip artifact
|
||||
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||
release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
body: |
|
||||
Not a stable release:
|
||||
${{ github.event.head_commit.message }}
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||
asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||
asset_content_type: application/zip
|
25
.github/workflows/pr.yml
vendored
Normal file
25
.github/workflows/pr.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: CI-PR
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: clang-format
|
||||
run: |
|
||||
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: build binary
|
||||
run: |
|
||||
docker build . -t builder
|
||||
docker run --rm -v ${PWD}:/project builder make
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: binary
|
||||
path: "*.wms"
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.cbp
|
||||
*.elf
|
||||
*.layout
|
||||
*.rpx
|
||||
build/
|
||||
*.save-failed
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.wms
|
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM wiiuenv/devkitppc:20220303
|
||||
|
||||
COPY --from=wiiuenv/libkernel:20211031 /artifacts $DEVKITPRO
|
||||
COPY --from=wiiuenv/wiiumodulesystem:20220123 /artifacts $DEVKITPRO
|
||||
COPY --from=wiiuenv/libsdutils:20220303 /artifacts $DEVKITPRO
|
||||
|
||||
WORKDIR project
|
146
Makefile
Normal file
146
Makefile
Normal file
@ -0,0 +1,146 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
include $(DEVKITPRO)/wums/share/wums_rules
|
||||
|
||||
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||
WUT_ROOT := $(DEVKITPRO)/wut
|
||||
#-------------------------------------------------------------------------------
|
||||
# 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
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := SDHotSwapModule
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := source
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -Wall -Wextra -O0 -ffunction-sections\
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS)
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
CXXFLAGS += -DDEBUG -g
|
||||
CFLAGS += -DDEBUG -g
|
||||
endif
|
||||
|
||||
LIBS := -lwums -lwut -lkernel
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#-------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(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)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).wms $(TARGET).elf
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#-------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).wms
|
||||
|
||||
$(OUTPUT).wms : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.s
|
||||
@echo $(notdir $<)
|
||||
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
[![CI-Release](https://github.com/wiiu-env/SDHotSwapModule/actions/workflows/ci.yml/badge.svg)](https://github.com/wiiu-env/SDHotSwapModule/actions/workflows/ci.yml)
|
||||
|
||||
## SDHotSwapModule
|
||||
|
||||
Automatically (un)mounts the sd card when the sd card was inserted/ejected. See [libsdutils](https://github.com/wiiu-env/libsdutils) to have an easy way to register a callback for these events.
|
||||
|
||||
## Usage
|
||||
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
|
||||
|
||||
1. Copy the file `SDHotSwapModule.wms` into `sd:/wiiu/environments/[ENVIRONMENT]/modules`.
|
||||
2. Requires the [WUMSLoader](https://github.com/wiiu-env/WUMSLoader) in `sd:/wiiu/environments/[ENVIRONMENT]/modules/setup`.
|
||||
|
||||
## Building using the Dockerfile
|
||||
|
||||
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
|
||||
|
||||
```
|
||||
# Build docker image (only needed once)
|
||||
docker build . -t sdhotswapmodule-builder
|
||||
|
||||
# make
|
||||
docker run -it --rm -v ${PWD}:/project sdhotswapmodule-builder make
|
||||
|
||||
# make clean
|
||||
docker run -it --rm -v ${PWD}:/project sdhotswapmodule-builder make clean
|
||||
```
|
||||
|
||||
## Format the code via docker
|
||||
|
||||
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i`
|
52
source/exports.cpp
Normal file
52
source/exports.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "exports.h"
|
||||
#include <cstring>
|
||||
#include <sdutils/sdutils.h>
|
||||
#include <wums.h>
|
||||
|
||||
#define MAX_HANDLERS 16
|
||||
static SDAttachHandlerFn sHandlers[MAX_HANDLERS] = {nullptr};
|
||||
|
||||
void callAttachCallbacks(SDUtilsAttachStatus status) {
|
||||
int i;
|
||||
for (i = 0; i < MAX_HANDLERS; ++i) {
|
||||
if (sHandlers[i]) {
|
||||
sHandlers[i](status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanUpAttachCallbacks() {
|
||||
memset(sHandlers, 0, sizeof(sHandlers));
|
||||
}
|
||||
|
||||
bool SDUtilsAddAttachHandler(SDAttachHandlerFn fn) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_HANDLERS; ++i) {
|
||||
if (sHandlers[i] == fn) {
|
||||
return true;
|
||||
}
|
||||
if (!sHandlers[i]) {
|
||||
sHandlers[i] = fn;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDUtilsRemoveAttachHandler(SDAttachHandlerFn fn) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_HANDLERS; ++i) {
|
||||
if (sHandlers[i] == fn) {
|
||||
sHandlers[i] = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WUMS_EXPORT_FUNCTION(SDUtilsAddAttachHandler);
|
||||
WUMS_EXPORT_FUNCTION(SDUtilsRemoveAttachHandler);
|
6
source/exports.h
Normal file
6
source/exports.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <sdutils/sdutils.h>
|
||||
|
||||
void callAttachCallbacks(SDUtilsAttachStatus status);
|
||||
|
||||
void cleanUpAttachCallbacks();
|
36
source/logger.c
Normal file
36
source/logger.c
Normal file
@ -0,0 +1,36 @@
|
||||
#ifdef DEBUG
|
||||
#include <stdint.h>
|
||||
#include <whb/log_cafe.h>
|
||||
#include <whb/log_module.h>
|
||||
#include <whb/log_udp.h>
|
||||
|
||||
uint32_t moduleLogInit = false;
|
||||
uint32_t cafeLogInit = false;
|
||||
uint32_t udpLogInit = false;
|
||||
#endif // DEBUG
|
||||
|
||||
void initLogging() {
|
||||
#ifdef DEBUG
|
||||
if (!(moduleLogInit = WHBLogModuleInit())) {
|
||||
cafeLogInit = WHBLogCafeInit();
|
||||
udpLogInit = WHBLogUdpInit();
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void deinitLogging() {
|
||||
#ifdef DEBUG
|
||||
if (moduleLogInit) {
|
||||
WHBLogModuleDeinit();
|
||||
moduleLogInit = false;
|
||||
}
|
||||
if (cafeLogInit) {
|
||||
WHBLogCafeDeinit();
|
||||
cafeLogInit = false;
|
||||
}
|
||||
if (udpLogInit) {
|
||||
WHBLogUdpDeinit();
|
||||
udpLogInit = false;
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
43
source/logger.h
Normal file
43
source/logger.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
|
||||
do { \
|
||||
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
|
||||
do { \
|
||||
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
|
||||
#endif
|
||||
|
||||
void initLogging();
|
||||
|
||||
void deinitLogging();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
105
source/main.cpp
Normal file
105
source/main.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "exports.h"
|
||||
#include "logger.h"
|
||||
#include "sdcard.h"
|
||||
#include <condition_variable>
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <mutex>
|
||||
#include <sdutils/sdutils.h>
|
||||
#include <thread>
|
||||
#include <wums.h>
|
||||
|
||||
WUMS_MODULE_EXPORT_NAME("homebrew_sdhotswap");
|
||||
WUMS_MODULE_SKIP_INIT_FINI();
|
||||
|
||||
// This NEEDS to be on the heap. Global DTOR are never called for modules
|
||||
// but we need a fresh instance of this condition_variable on each app change.
|
||||
std::condition_variable *cv = nullptr;
|
||||
std::mutex *cv_m = nullptr;
|
||||
|
||||
std::thread *mountThread = nullptr;
|
||||
bool sStopThread = false;
|
||||
bool sIsSDInsertedAndMounted = false;
|
||||
|
||||
int mount_thread() {
|
||||
std::unique_lock<std::mutex> lk(*cv_m);
|
||||
// Wait until the main thread has checked the sd status once.
|
||||
cv->wait(lk);
|
||||
while (!sStopThread) {
|
||||
auto newStatus = IsSDCardInserted();
|
||||
if (newStatus != sIsSDInsertedAndMounted) {
|
||||
if (newStatus) {
|
||||
if (MountSDCard()) {
|
||||
callAttachCallbacks(SDUTILS_ATTACH_MOUNTED);
|
||||
sIsSDInsertedAndMounted = true;
|
||||
}
|
||||
} else {
|
||||
if (UnmountSDCard()) {
|
||||
callAttachCallbacks(SDUTILS_ATTACH_UNMOUNTED);
|
||||
sIsSDInsertedAndMounted = false;
|
||||
}
|
||||
}
|
||||
OSMemoryBarrier();
|
||||
}
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(100));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
WUMS_APPLICATION_STARTS() {
|
||||
initLogging();
|
||||
|
||||
sStopThread = false;
|
||||
|
||||
cv = new std::condition_variable;
|
||||
cv_m = new std::mutex;
|
||||
|
||||
OSMemoryBarrier();
|
||||
|
||||
mountThread = new std::thread(mount_thread);
|
||||
auto nativeHandle = (OSThread *) mountThread->native_handle();
|
||||
OSSetThreadName(nativeHandle, "SDHotSwapModuleThread");
|
||||
while (!OSSetThreadAffinity(nativeHandle, OS_THREAD_ATTRIB_AFFINITY_CPU2)) {
|
||||
OSSleepTicks(OSMillisecondsToTicks(16));
|
||||
}
|
||||
|
||||
auto check = InitialSDCheck();
|
||||
if (check < 0) {
|
||||
// On error stop thread.
|
||||
sStopThread = true;
|
||||
} else {
|
||||
// Otherwise it retuns the current status.
|
||||
sIsSDInsertedAndMounted = check == 1;
|
||||
}
|
||||
|
||||
OSMemoryBarrier();
|
||||
|
||||
DEBUG_FUNCTION_LINE("Wake up the thread");
|
||||
// Now we can wake up the thread!
|
||||
cv->notify_all();
|
||||
}
|
||||
|
||||
WUMS_APPLICATION_ENDS() {
|
||||
sStopThread = true;
|
||||
OSMemoryBarrier();
|
||||
cv->notify_all();
|
||||
|
||||
if (mountThread != nullptr) {
|
||||
mountThread->join();
|
||||
|
||||
delete mountThread;
|
||||
mountThread = nullptr;
|
||||
}
|
||||
|
||||
DeInitSDCheck();
|
||||
|
||||
delete cv;
|
||||
delete cv_m;
|
||||
cv = nullptr;
|
||||
cv_m = nullptr;
|
||||
|
||||
OSMemoryBarrier();
|
||||
deinitLogging();
|
||||
}
|
101
source/sdcard.cpp
Normal file
101
source/sdcard.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "exports.h"
|
||||
#include "logger.h"
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <mutex>
|
||||
|
||||
static FSClient sClient;
|
||||
std::mutex *mutex;
|
||||
|
||||
bool sFSClientAdded = false;
|
||||
|
||||
int InitialSDCheck() {
|
||||
mutex = new (std::nothrow) std::mutex;
|
||||
if (!mutex) {
|
||||
return -1;
|
||||
}
|
||||
FSStatus result = FSAddClient(&sClient, FS_ERROR_FLAG_ALL);
|
||||
if (result != FS_STATUS_OK) {
|
||||
return -2;
|
||||
} else {
|
||||
sFSClientAdded = true;
|
||||
OSMemoryBarrier();
|
||||
FSCmdBlock fsCmd;
|
||||
FSInitCmdBlock(&fsCmd);
|
||||
FSMountSource mountSource;
|
||||
return FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) == FS_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void DeInitSDCheck() {
|
||||
if (mutex) {
|
||||
delete mutex;
|
||||
mutex = nullptr;
|
||||
}
|
||||
|
||||
if (sFSClientAdded) {
|
||||
FSDelClient(&sClient, FS_ERROR_FLAG_ALL);
|
||||
memset(&sClient, 0, sizeof(sClient));
|
||||
sFSClientAdded = false;
|
||||
}
|
||||
|
||||
cleanUpAttachCallbacks();
|
||||
|
||||
OSMemoryBarrier();
|
||||
}
|
||||
|
||||
int IsSDCardInserted() {
|
||||
if (!sFSClientAdded || !mutex) {
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> lk(*mutex);
|
||||
FSCmdBlock fsCmd;
|
||||
FSMountSource mountSource;
|
||||
FSInitCmdBlock(&fsCmd);
|
||||
memset(&mountSource, 0, sizeof(mountSource));
|
||||
return FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) == FS_STATUS_OK;
|
||||
}
|
||||
|
||||
int MountSDCard() {
|
||||
if (!sFSClientAdded || !mutex) {
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> lk(*mutex);
|
||||
FSCmdBlock fsCmd;
|
||||
FSInitCmdBlock(&fsCmd);
|
||||
FSMountSource mountSource;
|
||||
char mountPath[0x80];
|
||||
|
||||
if (FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) != FS_STATUS_OK) {
|
||||
DEBUG_FUNCTION_LINE("No SD Card found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FSStatus res;
|
||||
if ((res = FSMount(&sClient, &fsCmd, &mountSource, mountPath, sizeof(mountPath), FS_ERROR_FLAG_ALL)) != FS_STATUS_OK) {
|
||||
DEBUG_FUNCTION_LINE("FSMount failed %d", res);
|
||||
return 0;
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Mounted SD Card");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int UnmountSDCard() {
|
||||
if (!sFSClientAdded || !mutex) {
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> lk(*mutex);
|
||||
FSCmdBlock fsCmd;
|
||||
FSInitCmdBlock(&fsCmd);
|
||||
|
||||
FSStatus res = FS_STATUS_OK;
|
||||
while (res == FS_STATUS_OK) {
|
||||
res = FSUnmount(&sClient, &fsCmd, "/vol/external01", FS_ERROR_FLAG_ALL);
|
||||
if (res != FS_STATUS_OK && res != FS_STATUS_NOT_FOUND) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Unmounted SD Card");
|
||||
|
||||
return 1;
|
||||
}
|
27
source/sdcard.h
Normal file
27
source/sdcard.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
int InitialSDCheck();
|
||||
|
||||
void DeInitSDCheck();
|
||||
|
||||
/**
|
||||
* @return < 0 on error.
|
||||
* 0 when no FAT32 formatted sd card is inserted
|
||||
* 1 when a FAT32 formatted sd card is inserted
|
||||
*/
|
||||
|
||||
int IsSDCardInserted();
|
||||
|
||||
/**
|
||||
* @return < 0 on error.
|
||||
* 0 when mounting failed,
|
||||
* 1 when mounting was successful.
|
||||
*/
|
||||
int MountSDCard();
|
||||
|
||||
/**
|
||||
* @return < 0 on error.
|
||||
* 0 when unmounting failed,
|
||||
* 1 when unmounting was successful.
|
||||
*/
|
||||
int UnmountSDCard();
|
Loading…
Reference in New Issue
Block a user