Merge branch 'master' into toggle-power-button
This commit is contained in:
commit
68b670f94f
47
README.md
47
README.md
@ -1,41 +1,36 @@
|
|||||||
# Luma3DS
|
# Luma3DS
|
||||||
*Noob-proof (N)3DS "Custom Firmware"*
|
*Noob-proof (N)3DS "Custom Firmware"*
|
||||||
|
|
||||||
## What it is
|
### What it is
|
||||||
|
**Luma3DS** is a program to patch the system software of (New) Nintendo (2)3DS handheld consoles "on the fly", adding features such as per-game language settings, debugging capabilities for developers, and removing restrictions enforced by Nintendo such as the region lock.
|
||||||
|
|
||||||
**Luma3DS** is a program to patch the system software of (New) Nintendo 3DS handheld consoles "on the fly", adding features (such as per-game language settings and debugging capabilities for developers) and removing restrictions enforced by Nintendo (such as the region lock).
|
|
||||||
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||||
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://3ds.hacks.guide/) for details on how to get your system ready.
|
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://3ds.hacks.guide/) for details on how to get your system ready.
|
||||||
|
|
||||||
Since Luma3DS v8.0, Luma3DS has its own in-game menu, triggerable by `L+Down+Select` (see the [release notes](https://github.com/AuroraWright/Luma3DS/releases/tag/v8.0)).
|
Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select</kbd> (see the [release notes](https://github.com/AuroraWright/Luma3DS/releases/tag/v8.0)).
|
||||||
|
|
||||||
---
|
#
|
||||||
|
### Compiling
|
||||||
|
* Prerequisites
|
||||||
|
1. git
|
||||||
|
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
|
||||||
|
3. [firmtool](https://github.com/TuxSH/firmtool)
|
||||||
|
4. Up-to-date devkitARM+libctru
|
||||||
|
1. Clone the repository with `git clone https://github.com/AuroraWright/Luma3DS.git`
|
||||||
|
2. Run `make`.
|
||||||
|
|
||||||
## Compiling
|
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.
|
||||||
|
|
||||||
First you need to clone the repository with: `git clone https://github.com/AuroraWright/Luma3DS.git`
|
|
||||||
To compile, you'll need a recent commit of [makerom](https://github.com/profi200/Project_CTR) added to your PATH. You'll also need to install [firmtool](https://github.com/TuxSH/firmtool), its README contains installation instructions.
|
|
||||||
You'll also need to update your libctru and devkitARM installation to their latest releases.
|
|
||||||
Then, run `make`.
|
|
||||||
The produced file is called `boot.firm` and is meant to be copied to the root of your SD card, for usage with boot9strap.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Setup / Usage / Features
|
|
||||||
|
|
||||||
|
#
|
||||||
|
### Setup / Usage / Features
|
||||||
See https://github.com/AuroraWright/Luma3DS/wiki
|
See https://github.com/AuroraWright/Luma3DS/wiki
|
||||||
|
|
||||||
---
|
#
|
||||||
|
### Credits
|
||||||
## Credits
|
|
||||||
|
|
||||||
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
||||||
|
|
||||||
---
|
#
|
||||||
|
### Licensing
|
||||||
|
This software is licensed under the terms of the GPLv3. You can find a copy of the license in the LICENSE.txt file.
|
||||||
|
|
||||||
## Licensing
|
Files in the GDB stub are instead double-licensed as MIT or "GPLv2 or any later version", in which case it's specified in the file header.
|
||||||
|
|
||||||
This software is licensed under the terms of the GPLv3.
|
|
||||||
You can find a copy of the license in the LICENSE.txt file.
|
|
||||||
|
|
||||||
Files in the GDB stub are instead double-licensed as MIT or "GPLv2 or any later version", in which case it is specified in the file header.
|
|
||||||
|
@ -28,13 +28,13 @@ INCLUDES := include
|
|||||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
DEFINES := -DARM11 -D_3DS
|
DEFINES := -DARM11 -D_3DS
|
||||||
|
|
||||||
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -O2 -mword-relocations \
|
COMMON_FLAGS = -g -Wall -Wextra -Werror -O2 -mword-relocations \
|
||||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES) $(INCLUDE)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE)
|
CFLAGS := -std=gnu11 $(COMMON_FLAGS)
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++17 $(COMMON_FLAGS)
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--section-start,.text=0x14000000
|
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--section-start,.text=0x14000000
|
||||||
|
283
sysmodules/loader/source/bps_patcher.cpp
Normal file
283
sysmodules/loader/source/bps_patcher.cpp
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
#include "bps_patcher.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <3ds/os.h>
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/services/fs.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
|
||||||
|
#include "patcher.h"
|
||||||
|
#include "strings.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "file_util.h"
|
||||||
|
|
||||||
|
namespace patcher
|
||||||
|
{
|
||||||
|
namespace Bps
|
||||||
|
{
|
||||||
|
// The BPS format uses variable length encoding for all integers.
|
||||||
|
// Realistically uint32s are more than enough for code patching.
|
||||||
|
using Number = u32;
|
||||||
|
|
||||||
|
constexpr std::size_t FooterSize = 12;
|
||||||
|
|
||||||
|
// The BPS format uses CRC32 checksums.
|
||||||
|
[[gnu::optimize("Os")]] static u32 crc32(const u8 *data, std::size_t size)
|
||||||
|
{
|
||||||
|
u32 crc = 0xFFFFFFFF;
|
||||||
|
for(std::size_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
crc ^= data[i];
|
||||||
|
for(std::size_t j = 0; j < 8; ++j)
|
||||||
|
{
|
||||||
|
u32 mask = -(crc & 1);
|
||||||
|
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility class to make keeping track of offsets and bound checks less error prone.
|
||||||
|
template <typename T>
|
||||||
|
class Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Stream(T *ptr, std::size_t size) : m_ptr{ptr}, m_size{size} {}
|
||||||
|
|
||||||
|
bool Read(void *buffer, std::size_t length)
|
||||||
|
{
|
||||||
|
if(m_offset + length > m_size)
|
||||||
|
return false;
|
||||||
|
std::memcpy(buffer, m_ptr + m_offset, length);
|
||||||
|
m_offset += length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OtherType>
|
||||||
|
[[gnu::optimize("Os")]] bool CopyFrom(Stream<OtherType> &other, std::size_t length)
|
||||||
|
{
|
||||||
|
if(m_offset + length > m_size)
|
||||||
|
return false;
|
||||||
|
if(!other.Read(m_ptr + m_offset, length))
|
||||||
|
return false;
|
||||||
|
m_offset += length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ValueType>
|
||||||
|
std::optional<ValueType> Read()
|
||||||
|
{
|
||||||
|
static_assert(std::is_pod_v<ValueType>);
|
||||||
|
ValueType val{};
|
||||||
|
if(!Read(&val, sizeof(val)))
|
||||||
|
return std::nullopt;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::optimize("Os")]] Number ReadNumber()
|
||||||
|
{
|
||||||
|
Number data = 0, shift = 1;
|
||||||
|
std::optional<u8> x;
|
||||||
|
while((x = Read<u8>()))
|
||||||
|
{
|
||||||
|
data += (*x & 0x7f) * shift;
|
||||||
|
if(*x & 0x80)
|
||||||
|
break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data() const { return m_ptr; }
|
||||||
|
std::size_t size() const { return m_size; }
|
||||||
|
std::size_t Tell() const { return m_offset; }
|
||||||
|
|
||||||
|
bool Seek(size_t offset)
|
||||||
|
{
|
||||||
|
m_offset = offset;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *m_ptr = nullptr;
|
||||||
|
std::size_t m_size = 0;
|
||||||
|
std::size_t m_offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PatchApplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PatchApplier(Stream<const u8> source, Stream<u8> target, Stream<const u8> patch)
|
||||||
|
: m_source{source}, m_target{target}, m_patch{patch}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::always_inline]] bool Apply()
|
||||||
|
{
|
||||||
|
const auto magic = *m_patch.Read<std::array<char, 4>>();
|
||||||
|
if(std::string_view(magic.data(), magic.size()) != "BPS1")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Bps::Number source_size = m_patch.ReadNumber();
|
||||||
|
const Bps::Number target_size = m_patch.ReadNumber();
|
||||||
|
const Bps::Number metadata_size = m_patch.ReadNumber();
|
||||||
|
if(source_size > m_source.size() || target_size > m_target.size() || metadata_size != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::size_t command_start_offset = m_patch.Tell();
|
||||||
|
const std::size_t command_end_offset = m_patch.size() - FooterSize;
|
||||||
|
m_patch.Seek(command_end_offset);
|
||||||
|
const u32 source_crc32 = *m_patch.Read<u32>();
|
||||||
|
const u32 target_crc32 = *m_patch.Read<u32>();
|
||||||
|
m_patch.Seek(command_start_offset);
|
||||||
|
|
||||||
|
if(crc32(m_source.data(), source_size) != source_crc32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Process all patch commands.
|
||||||
|
std::memset(m_target.data(), 0, m_target.size());
|
||||||
|
while(m_patch.Tell() < command_end_offset)
|
||||||
|
{
|
||||||
|
const bool ok = HandleCommand();
|
||||||
|
if(!ok)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc32(m_target.data(), target_size) == target_crc32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool HandleCommand()
|
||||||
|
{
|
||||||
|
const Number data = m_patch.ReadNumber();
|
||||||
|
const Number command = data & 3;
|
||||||
|
const Number length = (data >> 2) + 1;
|
||||||
|
|
||||||
|
switch(command)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return SourceRead(length);
|
||||||
|
case 1:
|
||||||
|
return TargetRead(length);
|
||||||
|
case 2:
|
||||||
|
return SourceCopy(length);
|
||||||
|
case 3:
|
||||||
|
return TargetCopy(length);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SourceRead(Number length)
|
||||||
|
{
|
||||||
|
return m_source.Seek(m_target.Tell()) && m_target.CopyFrom(m_source, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TargetRead(Number length) { return m_target.CopyFrom(m_patch, length); }
|
||||||
|
|
||||||
|
bool SourceCopy(Number length)
|
||||||
|
{
|
||||||
|
const Number data = m_patch.ReadNumber();
|
||||||
|
m_source_relative_offset += (data & 1 ? -1 : +1) * int(data >> 1);
|
||||||
|
if(!m_source.Seek(m_source_relative_offset) || !m_target.CopyFrom(m_source, length))
|
||||||
|
return false;
|
||||||
|
m_source_relative_offset += length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TargetCopy(Number length)
|
||||||
|
{
|
||||||
|
const Number data = m_patch.ReadNumber();
|
||||||
|
m_target_relative_offset += (data & 1 ? -1 : +1) * int(data >> 1);
|
||||||
|
if(m_target.Tell() + length > m_target.size())
|
||||||
|
return false;
|
||||||
|
if(m_target_relative_offset + length > m_target.size())
|
||||||
|
return false;
|
||||||
|
// Byte by byte copy.
|
||||||
|
for(size_t i = 0; i < length; ++i)
|
||||||
|
m_target.data()[m_target.Tell() + i] = m_target.data()[m_target_relative_offset++];
|
||||||
|
m_target.Seek(m_target.Tell() + length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t m_source_relative_offset = 0;
|
||||||
|
std::size_t m_target_relative_offset = 0;
|
||||||
|
Stream<const u8> m_source;
|
||||||
|
Stream<u8> m_target;
|
||||||
|
Stream<const u8> m_patch;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Bps
|
||||||
|
|
||||||
|
class ScopedAppHeap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopedAppHeap()
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
m_size = osGetMemRegionFree(MEMREGION_APPLICATION);
|
||||||
|
if(!R_SUCCEEDED(svcControlMemory(&tmp, BaseAddress, 0, m_size,
|
||||||
|
MemOp(MEMOP_ALLOC | MEMOP_REGION_APP),
|
||||||
|
MemPerm(MEMPERM_READ | MEMPERM_WRITE))))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedAppHeap()
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
svcControlMemory(&tmp, BaseAddress, 0, m_size, MEMOP_FREE, MemPerm(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr u32 BaseAddress = 0x08000000;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool ApplyCodeBpsPatch(u64 prog_id, u8 *code, u32 size)
|
||||||
|
{
|
||||||
|
char bps_path[] = "/luma/titles/0000000000000000/code.bps";
|
||||||
|
progIdToStr(bps_path + 28, prog_id);
|
||||||
|
util::File patch_file;
|
||||||
|
if(!patch_file.Open(bps_path, FS_OPEN_READ))
|
||||||
|
return true;
|
||||||
|
const u32 patch_size = u32(patch_file.GetSize().value_or(0));
|
||||||
|
|
||||||
|
// Temporarily use APPLICATION memory to store the source and patch data.
|
||||||
|
ScopedAppHeap memory;
|
||||||
|
|
||||||
|
u8 *source_data = reinterpret_cast<u8 *>(memory.BaseAddress);
|
||||||
|
u8 *patch_data = source_data + size;
|
||||||
|
std::memcpy(source_data, code, size);
|
||||||
|
if(!patch_file.Read(patch_data, patch_size, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Bps::Stream<const u8> source_stream{source_data, size};
|
||||||
|
Bps::Stream target_stream{code, size};
|
||||||
|
Bps::Stream<const u8> patch_stream{patch_data, patch_size};
|
||||||
|
Bps::PatchApplier applier{source_stream, target_stream, patch_stream};
|
||||||
|
if(!applier.Apply())
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace patcher
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
bool patcherApplyCodeBpsPatch(u64 progId, u8 *code, u32 size)
|
||||||
|
{
|
||||||
|
return patcher::ApplyCodeBpsPatch(progId, code, size);
|
||||||
|
}
|
||||||
|
}
|
12
sysmodules/loader/source/bps_patcher.h
Normal file
12
sysmodules/loader/source/bps_patcher.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
bool patcherApplyCodeBpsPatch(u64 progId, u8* code, u32 size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
76
sysmodules/loader/source/file_util.h
Normal file
76
sysmodules/loader/source/file_util.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/services/fs.h>
|
||||||
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/types.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
inline FS_Path MakePath(const char *path)
|
||||||
|
{
|
||||||
|
return {PATH_ASCII, strnlen(path, 255) + 1, path};
|
||||||
|
}
|
||||||
|
|
||||||
|
// A small wrapper to make forgetting to close a file and
|
||||||
|
// to check read lengths impossible.
|
||||||
|
class File
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
File() = default;
|
||||||
|
File(const File &other) = delete;
|
||||||
|
File &operator=(const File &) = delete;
|
||||||
|
File(File &&other) { *this = std::move(other); }
|
||||||
|
File &operator=(File &&other)
|
||||||
|
{
|
||||||
|
std::swap(m_handle, other.m_handle);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~File() { Close(); }
|
||||||
|
|
||||||
|
bool Close()
|
||||||
|
{
|
||||||
|
const bool ok = !m_handle || R_SUCCEEDED(FSFILE_Close(*m_handle));
|
||||||
|
if(ok)
|
||||||
|
m_handle = std::nullopt;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Open(const char *path, int open_flags)
|
||||||
|
{
|
||||||
|
const FS_Path archive_path = {PATH_EMPTY, 1, ""};
|
||||||
|
Handle handle;
|
||||||
|
const bool ok = R_SUCCEEDED(FSUSER_OpenFileDirectly(&handle, ARCHIVE_SDMC, archive_path,
|
||||||
|
MakePath(path), open_flags, 0));
|
||||||
|
if(ok)
|
||||||
|
m_handle = handle;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Read(void *buffer, u32 size, u64 offset)
|
||||||
|
{
|
||||||
|
u32 bytes_read = 0;
|
||||||
|
const Result res = FSFILE_Read(*m_handle, &bytes_read, offset, buffer, size);
|
||||||
|
return R_SUCCEEDED(res) && bytes_read == size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u64> GetSize() const
|
||||||
|
{
|
||||||
|
u64 size;
|
||||||
|
if(!R_SUCCEEDED(FSFILE_GetSize(*m_handle, &size)))
|
||||||
|
return std::nullopt;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<Handle> m_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace util
|
@ -1,5 +1,6 @@
|
|||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include "patcher.h"
|
#include "patcher.h"
|
||||||
|
#include "bps_patcher.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "strings.h"
|
#include "strings.h"
|
||||||
#include "romfsredir.h"
|
#include "romfsredir.h"
|
||||||
@ -369,7 +370,7 @@ bool loadTitleExheaderInfo(u64 progId, ExHeader_Info *exheaderInfo)
|
|||||||
|
|
||||||
u64 fileSize;
|
u64 fileSize;
|
||||||
|
|
||||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize != sizeof(ExHeader_Info) || fileSize != sizeof(ExHeader)) goto error;
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || (fileSize != sizeof(ExHeader_Info) && fileSize != sizeof(ExHeader))) goto error;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u64 total;
|
u64 total;
|
||||||
@ -468,13 +469,13 @@ static inline bool loadTitleLocaleConfig(u64 progId, u8 *mask, u8 *regionId, u8
|
|||||||
((buf[10] >= '0' && buf[10] <= '9') || (buf[10] >= 'a' && buf[10] <= 'f') || (buf[10] >= 'A' && buf[10] <= 'F')) &&
|
((buf[10] >= '0' && buf[10] <= '9') || (buf[10] >= 'a' && buf[10] <= 'f') || (buf[10] >= 'A' && buf[10] <= 'F')) &&
|
||||||
((buf[11] >= '0' && buf[11] <= '9') || (buf[11] >= 'a' && buf[11] <= 'f') || (buf[11] >= 'A' && buf[11] <= 'F')))
|
((buf[11] >= '0' && buf[11] <= '9') || (buf[11] >= 'a' && buf[11] <= 'f') || (buf[11] >= 'A' && buf[11] <= 'F')))
|
||||||
{
|
{
|
||||||
if (buf[10] >= '0' && buf[10] <= '9') *stateId = 16 * (buf[10] - '0');
|
if (buf[10] >= '0' && buf[10] <= '9') *stateId = 16 * (buf[10] - '0' + 10);
|
||||||
else if(buf[10] >= 'a' && buf[10] <= 'f') *stateId = 16 * (buf[10] - 'a');
|
else if(buf[10] >= 'a' && buf[10] <= 'f') *stateId = 16 * (buf[10] - 'a' + 10);
|
||||||
else if(buf[10] >= 'A' && buf[10] <= 'F') *stateId = 16 * (buf[10] - 'A');
|
else if(buf[10] >= 'A' && buf[10] <= 'F') *stateId = 16 * (buf[10] - 'A' + 10);
|
||||||
|
|
||||||
if (buf[11] >= '0' && buf[11] <= '9') *stateId += buf[11] - '0';
|
if (buf[11] >= '0' && buf[11] <= '9') *stateId += buf[11] - '0' + 10;
|
||||||
else if(buf[11] >= 'a' && buf[11] <= 'f') *stateId += buf[11] - 'a';
|
else if(buf[11] >= 'a' && buf[11] <= 'f') *stateId += buf[11] - 'a' + 10;
|
||||||
else if(buf[11] >= 'A' && buf[11] <= 'F') *stateId += buf[11] - 'A';
|
else if(buf[11] >= 'A' && buf[11] <= 'F') *stateId += buf[11] - 'A' + 10;
|
||||||
|
|
||||||
*mask |= 8;
|
*mask |= 8;
|
||||||
}
|
}
|
||||||
@ -845,6 +846,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
|||||||
|
|
||||||
if(CONFIG(PATCHGAMES))
|
if(CONFIG(PATCHGAMES))
|
||||||
{
|
{
|
||||||
|
if(!patcherApplyCodeBpsPatch(progId, code, size)) goto error;
|
||||||
if(!applyCodeIpsPatch(progId, code, size)) goto error;
|
if(!applyCodeIpsPatch(progId, code, size)) goto error;
|
||||||
|
|
||||||
if((u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
if((u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
||||||
|
@ -28,7 +28,7 @@ INCLUDES := include
|
|||||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
DEFINES := -DARM11 -D_3DS
|
DEFINES := -DARM11 -D_3DS
|
||||||
|
|
||||||
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -O2 -mword-relocations \
|
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Os -mword-relocations \
|
||||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ INCLUDES := include include/gdb include/menus include/redshift
|
|||||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
DEFINES := -DARM11 -D_3DS
|
DEFINES := -DARM11 -D_3DS
|
||||||
|
|
||||||
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Wno-unused-value -O2 -mword-relocations \
|
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Wno-unused-value -Os -mword-relocations \
|
||||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
-fomit-frame-pointer -ffunction-sections -fdata-sections -fno-math-errno \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE)
|
CFLAGS += $(INCLUDE)
|
||||||
@ -39,7 +39,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
|||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--section-start,.text=0x14000000
|
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--section-start,.text=0x14000000
|
||||||
|
|
||||||
LIBS := -lctru -lm
|
LIBS := -lctru
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
@ -27,5 +27,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
|
#include "MyThread.h"
|
||||||
|
|
||||||
void ERRF_HandleCommands(void *ctx);
|
MyThread *errDispCreateThread(void);
|
||||||
|
void ERRF_HandleCommands(void);
|
||||||
|
void errDispThreadMain(void);
|
||||||
|
@ -18,14 +18,14 @@
|
|||||||
|
|
||||||
#define MAX_DEBUG 3
|
#define MAX_DEBUG 3
|
||||||
#define MAX_DEBUG_THREAD 127
|
#define MAX_DEBUG_THREAD 127
|
||||||
#define MAX_BREAKPOINT 256
|
#define MAX_BREAKPOINT 64
|
||||||
|
|
||||||
#define MAX_TIO_OPEN_FILE 32
|
#define MAX_TIO_OPEN_FILE 32
|
||||||
|
|
||||||
// 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. Add 4 to this, for $#<checksum>, see below.
|
// 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. Add 4 to this, for $#<checksum>, see below.
|
||||||
// IDA seems to want additional bytes as well.
|
// IDA seems to want additional bytes as well.
|
||||||
// 1024 is fine enough to put all regs in the 'T' stop reply packets
|
// 1024 is fine enough to put all regs in the 'T' stop reply packets
|
||||||
#define GDB_BUF_LEN 2048
|
#define GDB_BUF_LEN 1024
|
||||||
|
|
||||||
#define GDB_HANDLER(name) GDB_Handle##name
|
#define GDB_HANDLER(name) GDB_Handle##name
|
||||||
#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name)
|
#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name)
|
||||||
|
@ -34,3 +34,4 @@ extern Menu sysconfigMenu;
|
|||||||
void SysConfigMenu_ToggleLEDs(void);
|
void SysConfigMenu_ToggleLEDs(void);
|
||||||
void SysConfigMenu_ToggleWireless(void);
|
void SysConfigMenu_ToggleWireless(void);
|
||||||
void SysConfigMenu_TogglePowerButton(void);
|
void SysConfigMenu_TogglePowerButton(void);
|
||||||
|
void SysConfigMenu_ControlWifi(void);
|
||||||
|
@ -32,13 +32,26 @@
|
|||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
|
|
||||||
|
extern Handle terminationRequestEvent;
|
||||||
|
|
||||||
static inline void assertSuccess(Result res)
|
static inline void assertSuccess(Result res)
|
||||||
{
|
{
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MyThread errDispThread;
|
||||||
|
static u8 ALIGN(8) errDispThreadStack[0xD00];
|
||||||
|
|
||||||
static char userString[0x100 + 1] = {0};
|
static char userString[0x100 + 1] = {0};
|
||||||
|
static char staticBuf[0x100 + 1] = {0};
|
||||||
|
|
||||||
|
MyThread *errDispCreateThread(void)
|
||||||
|
{
|
||||||
|
if(R_FAILED(MyThread_Create(&errDispThread, errDispThreadMain, errDispThreadStack, 0xD00, 0x18, CORE_SYSTEM)))
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
return &errDispThread;
|
||||||
|
}
|
||||||
|
|
||||||
static inline u32 ERRF_DisplayRegisterValue(u32 posX, u32 posY, const char *name, u32 value)
|
static inline u32 ERRF_DisplayRegisterValue(u32 posX, u32 posY, const char *name, u32 value)
|
||||||
{
|
{
|
||||||
@ -50,6 +63,11 @@ static inline int ERRF_FormatRegisterValue(char *out, const char *name, u32 valu
|
|||||||
return sprintf(out, "%-9s %08lx", name, value);
|
return sprintf(out, "%-9s %08lx", name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void ERRF_GetErrInfo(ERRF_FatalErrInfo* info, u32* in, u32 size)
|
||||||
|
{
|
||||||
|
memcpy(info, in, size);
|
||||||
|
}
|
||||||
|
|
||||||
static int ERRF_FormatError(char *out, ERRF_FatalErrInfo *info)
|
static int ERRF_FormatError(char *out, ERRF_FatalErrInfo *info)
|
||||||
{
|
{
|
||||||
char *outStart = out;
|
char *outStart = out;
|
||||||
@ -154,7 +172,7 @@ static int ERRF_FormatError(char *out, ERRF_FatalErrInfo *info)
|
|||||||
desc = "The System Memory has been damaged.";
|
desc = "The System Memory has been damaged.";
|
||||||
break;
|
break;
|
||||||
case ERRF_ERRTYPE_FAILURE:
|
case ERRF_ERRTYPE_FAILURE:
|
||||||
info->data.failure_mesg[0x60] = 0; // make sure the last byte in the IPC buffer is NULL
|
info->data.failure_mesg[0x5F] = 0; // make sure the last byte in the IPC buffer is NULL
|
||||||
desc = info->data.failure_mesg;
|
desc = info->data.failure_mesg;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -225,18 +243,18 @@ static Result ERRF_SaveErrorToFile(ERRF_FatalErrInfo *info)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ERRF_HandleCommands(void *ctx)
|
void ERRF_HandleCommands(void)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
ERRF_FatalErrInfo info;
|
||||||
|
|
||||||
switch(cmdbuf[0] >> 16)
|
switch(cmdbuf[0] >> 16)
|
||||||
{
|
{
|
||||||
case 1: // Throw
|
case 1: // Throw
|
||||||
{
|
{
|
||||||
ERRF_FatalErrInfo *info = (ERRF_FatalErrInfo *)(cmdbuf + 1);
|
ERRF_GetErrInfo(&info, (cmdbuf + 1), sizeof(ERRF_FatalErrInfo));
|
||||||
ERRF_SaveErrorToFile(info);
|
ERRF_SaveErrorToFile(&info);
|
||||||
if(info->type != ERRF_ERRTYPE_LOGGED || info->procId == 0)
|
if(info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0)
|
||||||
{
|
{
|
||||||
menuEnter();
|
menuEnter();
|
||||||
|
|
||||||
@ -244,7 +262,7 @@ void ERRF_HandleCommands(void *ctx)
|
|||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
|
|
||||||
ERRF_DisplayError(info);
|
ERRF_DisplayError(&info);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If we ever wanted to return:
|
If we ever wanted to return:
|
||||||
@ -258,26 +276,98 @@ void ERRF_HandleCommands(void *ctx)
|
|||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdbuf[0] = 0x10040;
|
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: // SetUserString
|
case 2: // SetUserString
|
||||||
{
|
{
|
||||||
if(cmdbuf[0] != 0x20042 || (cmdbuf[2] & 0x3C0F) != 2)
|
if(cmdbuf[0] != IPC_MakeHeader(2, 1, 2) || (cmdbuf[2] & 0x3C0F) != 2)
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x40;
|
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||||
cmdbuf[1] = 0xD9001830;
|
cmdbuf[1] = 0xD9001830;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x20040;
|
u32 sz = cmdbuf[1] <= 0x100 ? cmdbuf[1] : 0x100;
|
||||||
u32 sz = cmdbuf[1] <= 0x100 ? sz : 0x100;
|
|
||||||
memcpy(userString, cmdbuf + 3, sz);
|
memcpy(userString, cmdbuf + 3, sz);
|
||||||
userString[sz] = 0;
|
userString[sz] = 0;
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(2, 1, 0);
|
||||||
|
cmdbuf[1] = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void errDispThreadMain(void)
|
||||||
|
{
|
||||||
|
Handle handles[3];
|
||||||
|
Handle serverHandle, clientHandle, sessionHandle = 0;
|
||||||
|
|
||||||
|
u32 replyTarget = 0;
|
||||||
|
s32 index;
|
||||||
|
|
||||||
|
Result res;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
u32 *sbuf = getThreadStaticBuffers();
|
||||||
|
|
||||||
|
sbuf[0] = IPC_Desc_StaticBuffer(0x100, 0);
|
||||||
|
sbuf[1] = (u32)staticBuf;
|
||||||
|
|
||||||
|
assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "err:f", 1));
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
handles[0] = terminationRequestEvent;
|
||||||
|
handles[1] = serverHandle;
|
||||||
|
handles[2] = sessionHandle;
|
||||||
|
|
||||||
|
if(replyTarget == 0) // k11
|
||||||
|
cmdbuf[0] = 0xFFFF0000;
|
||||||
|
res = svcReplyAndReceive(&index, handles, 1 + (sessionHandle == 0 ? 1 : 2), replyTarget);
|
||||||
|
|
||||||
|
if(R_FAILED(res))
|
||||||
|
{
|
||||||
|
if((u32)res == 0xC920181A) // session closed by remote
|
||||||
|
{
|
||||||
|
svcCloseHandle(sessionHandle);
|
||||||
|
sessionHandle = 0;
|
||||||
|
replyTarget = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(index == 1)
|
||||||
|
{
|
||||||
|
Handle session;
|
||||||
|
assertSuccess(svcAcceptSession(&session, serverHandle));
|
||||||
|
|
||||||
|
if(sessionHandle == 0)
|
||||||
|
sessionHandle = session;
|
||||||
|
else
|
||||||
|
svcCloseHandle(session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERRF_HandleCommands();
|
||||||
|
replyTarget = sessionHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(!terminationRequest);
|
||||||
|
|
||||||
|
svcCloseHandle(sessionHandle);
|
||||||
|
svcCloseHandle(clientHandle);
|
||||||
|
svcCloseHandle(serverHandle);
|
||||||
|
}
|
||||||
|
@ -136,9 +136,9 @@ GDBContext *GDB_SelectAvailableContext(GDBServer *server, u16 minPort, u16 maxPo
|
|||||||
{
|
{
|
||||||
ctx->flags |= GDB_FLAG_SELECTED;
|
ctx->flags |= GDB_FLAG_SELECTED;
|
||||||
ctx->localPort = port;
|
ctx->localPort = port;
|
||||||
|
ctx->parent = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->parent = server;
|
|
||||||
GDB_UnlockAllContexts(server);
|
GDB_UnlockAllContexts(server);
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
@ -90,11 +90,15 @@ void __appInit()
|
|||||||
|
|
||||||
if (R_FAILED(pmDbgInit()))
|
if (R_FAILED(pmDbgInit()))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
|
if (R_FAILED(acInit()))
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is called after main exits
|
// this is called after main exits
|
||||||
void __appExit()
|
void __appExit()
|
||||||
{
|
{
|
||||||
|
acExit();
|
||||||
pmDbgExit();
|
pmDbgExit();
|
||||||
fsExit();
|
fsExit();
|
||||||
svcCloseHandle(*fsRegGetSessionHandle());
|
svcCloseHandle(*fsRegGetSessionHandle());
|
||||||
@ -166,7 +170,6 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "err:f", 1, ERRF_HandleCommands, true },
|
|
||||||
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
@ -194,12 +197,14 @@ int main(void)
|
|||||||
|
|
||||||
MyThread *menuThread = menuCreateThread();
|
MyThread *menuThread = menuCreateThread();
|
||||||
MyThread *taskRunnerThread = taskRunnerCreateThread();
|
MyThread *taskRunnerThread = taskRunnerCreateThread();
|
||||||
|
MyThread *errDispThread = errDispCreateThread();
|
||||||
|
|
||||||
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
|
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
MyThread_Join(menuThread, -1LL);
|
MyThread_Join(menuThread, -1LL);
|
||||||
MyThread_Join(taskRunnerThread, -1LL);
|
MyThread_Join(taskRunnerThread, -1LL);
|
||||||
|
MyThread_Join(errDispThread, -1LL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -125,12 +125,12 @@ u32 waitCombo(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static MyThread menuThread;
|
static MyThread menuThread;
|
||||||
static u8 ALIGN(8) menuThreadStack[0x3000];
|
static u8 ALIGN(8) menuThreadStack[0x1000];
|
||||||
static u8 batteryLevel = 255;
|
static u8 batteryLevel = 255;
|
||||||
|
|
||||||
MyThread *menuCreateThread(void)
|
MyThread *menuCreateThread(void)
|
||||||
{
|
{
|
||||||
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x3000, 52, CORE_SYSTEM)))
|
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
return &menuThread;
|
return &menuThread;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ static u8 ReadWriteBuffer8 = 0;
|
|||||||
static bool Cheat_Write8(const Handle processHandle, u32 offset, u8 value)
|
static bool Cheat_Write8(const Handle processHandle, u32 offset, u8 value)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 1 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr < 0x01E82000)
|
||||||
{
|
{
|
||||||
cheatPage[addr - 0x01E81000] = value;
|
cheatPage[addr - 0x01E81000] = value;
|
||||||
return true;
|
return true;
|
||||||
@ -184,7 +184,7 @@ static bool Cheat_Write8(const Handle processHandle, u32 offset, u8 value)
|
|||||||
static bool Cheat_Write16(const Handle processHandle, u32 offset, u16 value)
|
static bool Cheat_Write16(const Handle processHandle, u32 offset, u16 value)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 2 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr + 1 < 0x01E82000)
|
||||||
{
|
{
|
||||||
*(u16*)(cheatPage + addr - 0x01E81000) = value;
|
*(u16*)(cheatPage + addr - 0x01E81000) = value;
|
||||||
return true;
|
return true;
|
||||||
@ -200,7 +200,7 @@ static bool Cheat_Write16(const Handle processHandle, u32 offset, u16 value)
|
|||||||
static bool Cheat_Write32(const Handle processHandle, u32 offset, u32 value)
|
static bool Cheat_Write32(const Handle processHandle, u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 4 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr + 3 < 0x01E82000)
|
||||||
{
|
{
|
||||||
*(u32*)(cheatPage + addr - 0x01E81000) = value;
|
*(u32*)(cheatPage + addr - 0x01E81000) = value;
|
||||||
return true;
|
return true;
|
||||||
@ -216,7 +216,7 @@ static bool Cheat_Write32(const Handle processHandle, u32 offset, u32 value)
|
|||||||
static bool Cheat_Read8(const Handle processHandle, u32 offset, u8* retValue)
|
static bool Cheat_Read8(const Handle processHandle, u32 offset, u8* retValue)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 1 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr < 0x01E82000)
|
||||||
{
|
{
|
||||||
*retValue = cheatPage[addr - 0x01E81000];
|
*retValue = cheatPage[addr - 0x01E81000];
|
||||||
return true;
|
return true;
|
||||||
@ -233,7 +233,7 @@ static bool Cheat_Read8(const Handle processHandle, u32 offset, u8* retValue)
|
|||||||
static bool Cheat_Read16(const Handle processHandle, u32 offset, u16* retValue)
|
static bool Cheat_Read16(const Handle processHandle, u32 offset, u16* retValue)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 2 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr + 1 < 0x01E82000)
|
||||||
{
|
{
|
||||||
*retValue = *(u16*)(cheatPage + addr - 0x01E81000);
|
*retValue = *(u16*)(cheatPage + addr - 0x01E81000);
|
||||||
return true;
|
return true;
|
||||||
@ -250,7 +250,7 @@ static bool Cheat_Read16(const Handle processHandle, u32 offset, u16* retValue)
|
|||||||
static bool Cheat_Read32(const Handle processHandle, u32 offset, u32* retValue)
|
static bool Cheat_Read32(const Handle processHandle, u32 offset, u32* retValue)
|
||||||
{
|
{
|
||||||
u32 addr = *activeOffset() + offset;
|
u32 addr = *activeOffset() + offset;
|
||||||
if (addr >= 0x01E81000 && addr + 4 < 0x01E82000)
|
if (addr >= 0x01E81000 && addr + 3 < 0x01E82000)
|
||||||
{
|
{
|
||||||
*retValue = *(u32*)(cheatPage + addr - 0x01E81000);
|
*retValue = *(u32*)(cheatPage + addr - 0x01E81000);
|
||||||
return true;
|
return true;
|
||||||
|
@ -336,6 +336,7 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
cantStart = R_FAILED(res) || !isSocURegistered;
|
cantStart = R_FAILED(res) || !isSocURegistered;
|
||||||
|
|
||||||
int utcOffset = 12;
|
int utcOffset = 12;
|
||||||
|
int utcOffsetMinute = 0;
|
||||||
int absOffset;
|
int absOffset;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -344,14 +345,15 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
|
|
||||||
absOffset = utcOffset - 12;
|
absOffset = utcOffset - 12;
|
||||||
absOffset = absOffset < 0 ? -absOffset : absOffset;
|
absOffset = absOffset < 0 ? -absOffset : absOffset;
|
||||||
posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Current UTC offset: %c%02d", utcOffset < 12 ? '-' : '+', absOffset);
|
posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Current UTC offset: %c%02d%02d", utcOffset < 12 ? '-' : '+', absOffset, utcOffsetMinute);
|
||||||
posY = Draw_DrawFormattedString(10, posY + SPACING_Y, COLOR_WHITE, "Use DPAD Left/Right to change offset.\nPress A when done.") + SPACING_Y;
|
posY = Draw_DrawFormattedString(10, posY + SPACING_Y, COLOR_WHITE, "Use DPAD Left/Right to change hour offset.\nUse DPAD Up/Down to change minute offset.\nPress A when done.") + SPACING_Y;
|
||||||
|
|
||||||
input = waitInput();
|
input = waitInput();
|
||||||
|
|
||||||
if(input & BUTTON_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
|
if(input & BUTTON_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
|
||||||
if(input & BUTTON_RIGHT) utcOffset = (utcOffset + 1) % 24;
|
if(input & BUTTON_RIGHT) utcOffset = (utcOffset + 1) % 24;
|
||||||
|
if(input & BUTTON_UP) utcOffsetMinute = (utcOffsetMinute + 1) % 60;
|
||||||
|
if(input & BUTTON_DOWN) utcOffsetMinute = (60 + utcOffsetMinute - 1) % 60;
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
@ -371,6 +373,7 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
t += 3600 * utcOffset;
|
t += 3600 * utcOffset;
|
||||||
|
t += 60 * utcOffsetMinute;
|
||||||
gmtime_r(&t, &localt);
|
gmtime_r(&t, &localt);
|
||||||
res = ntpSetTimeDate(&localt);
|
res = ntpSetTimeDate(&localt);
|
||||||
}
|
}
|
||||||
|
@ -55,37 +55,38 @@ typedef struct {
|
|||||||
u8 z;
|
u8 z;
|
||||||
} Pixel;
|
} Pixel;
|
||||||
|
|
||||||
|
static u16 g_c[0x600];
|
||||||
|
static Pixel g_px[0x400];
|
||||||
|
|
||||||
void applyColorSettings(color_setting_t* cs)
|
void applyColorSettings(color_setting_t* cs)
|
||||||
{
|
{
|
||||||
u16 c[0x600];
|
|
||||||
Pixel px[0x400];
|
|
||||||
u8 i = 0;
|
u8 i = 0;
|
||||||
|
|
||||||
memset(c, 0, sizeof(c));
|
memset(g_c, 0, sizeof(g_c));
|
||||||
memset(px, 0, sizeof(px));
|
memset(g_px, 0, sizeof(g_px));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
px[i].r = i;
|
g_px[i].r = i;
|
||||||
px[i].g = i;
|
g_px[i].g = i;
|
||||||
px[i].b = i;
|
g_px[i].b = i;
|
||||||
px[i].z = 0;
|
g_px[i].z = 0;
|
||||||
} while(++i);
|
} while(++i);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
*(c + i + 0x000) = px[i].r | (px[i].r << 8);
|
*(g_c + i + 0x000) = g_px[i].r | (g_px[i].r << 8);
|
||||||
*(c + i + 0x100) = px[i].g | (px[i].g << 8);
|
*(g_c + i + 0x100) = g_px[i].g | (g_px[i].g << 8);
|
||||||
*(c + i + 0x200) = px[i].b | (px[i].b << 8);
|
*(g_c + i + 0x200) = g_px[i].b | (g_px[i].b << 8);
|
||||||
} while(++i);
|
} while(++i);
|
||||||
|
|
||||||
colorramp_fill(c + 0x000, c + 0x100, c + 0x200, 0x100, cs);
|
colorramp_fill(g_c + 0x000, g_c + 0x100, g_c + 0x200, 0x100, cs);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
px[i].r = *(c + i + 0x000) >> 8;
|
g_px[i].r = *(g_c + i + 0x000) >> 8;
|
||||||
px[i].g = *(c + i + 0x100) >> 8;
|
g_px[i].g = *(g_c + i + 0x100) >> 8;
|
||||||
px[i].b = *(c + i + 0x200) >> 8;
|
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
||||||
} while(++i);
|
} while(++i);
|
||||||
|
|
||||||
writeLut((u32*)px);
|
writeLut((u32*)g_px);
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu screenFiltersMenu = {
|
Menu screenFiltersMenu = {
|
||||||
@ -143,10 +144,10 @@ void screenFiltersSetTemperature(int temperature)
|
|||||||
memset(&cs, 0, sizeof(cs));
|
memset(&cs, 0, sizeof(cs));
|
||||||
|
|
||||||
cs.temperature = temperature;
|
cs.temperature = temperature;
|
||||||
cs.gamma[0] = 1.0F;
|
/*cs.gamma[0] = 1.0F;
|
||||||
cs.gamma[1] = 1.0F;
|
cs.gamma[1] = 1.0F;
|
||||||
cs.gamma[2] = 1.0F;
|
cs.gamma[2] = 1.0F;
|
||||||
cs.brightness = 1.0F;
|
cs.brightness = 1.0F;*/
|
||||||
|
|
||||||
applyColorSettings(&cs);
|
applyColorSettings(&cs);
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,12 @@
|
|||||||
|
|
||||||
Menu sysconfigMenu = {
|
Menu sysconfigMenu = {
|
||||||
"System configuration menu",
|
"System configuration menu",
|
||||||
.nbItems = 3,
|
.nbItems = 4,
|
||||||
{
|
{
|
||||||
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
||||||
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
||||||
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
||||||
|
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,15 +149,69 @@ void SysConfigMenu_ToggleWireless(void)
|
|||||||
while(!terminationRequest);
|
while(!terminationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysConfigMenu_TogglePowerButton(void)
|
static void SysConfigMenu_ForceWifiConnection(int slot)
|
||||||
{
|
{
|
||||||
u32 mcuIRQMask;
|
char ssid[0x20 + 1] = {0};
|
||||||
|
|
||||||
|
acuConfig config = {0};
|
||||||
|
ACU_CreateDefaultConfig(&config);
|
||||||
|
ACU_SetNetworkArea(&config, 2);
|
||||||
|
ACU_SetAllowApType(&config, 1 << slot);
|
||||||
|
ACU_SetRequestEulaVersion(&config);
|
||||||
|
|
||||||
|
Handle connectEvent = 0;
|
||||||
|
svcCreateEvent(&connectEvent, RESET_ONESHOT);
|
||||||
|
|
||||||
|
bool forcedConnection = false;
|
||||||
|
if(R_SUCCEEDED(ACU_ConnectAsync(&config, connectEvent)))
|
||||||
|
{
|
||||||
|
if(R_SUCCEEDED(svcWaitSynchronization(connectEvent, -1)))
|
||||||
|
{
|
||||||
|
ACU_GetSSID(ssid);
|
||||||
|
forcedConnection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svcCloseHandle(connectEvent);
|
||||||
|
|
||||||
|
char infoString[80] = {0};
|
||||||
|
u32 infoStringColor = forcedConnection ? COLOR_GREEN : COLOR_RED;
|
||||||
|
if(forcedConnection)
|
||||||
|
sprintf(infoString, "Succesfully forced a connection to: %s", ssid);
|
||||||
|
else
|
||||||
|
sprintf(infoString, "Failed to connect to slot %d", slot + 1);
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_DrawString(10, 10, COLOR_TITLE, "System configuration menu");
|
||||||
|
Draw_DrawString(10, 30, infoStringColor, infoString);
|
||||||
|
Draw_DrawString(10, 40, COLOR_WHITE, "Press B to go back.");
|
||||||
|
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
|
if(pressed & BUTTON_B)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(!terminationRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysConfigMenu_TogglePowerButton(void)
|
||||||
|
{
|
||||||
|
u32 mcuIRQMask;
|
||||||
|
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
mcuHwcInit();
|
mcuHwcInit();
|
||||||
MCUHWC_ReadRegister(0x18, (u8*)&mcuIRQMask, 4);
|
MCUHWC_ReadRegister(0x18, (u8*)&mcuIRQMask, 4);
|
||||||
mcuHwcExit();
|
mcuHwcExit();
|
||||||
@ -169,10 +224,10 @@ void SysConfigMenu_TogglePowerButton(void)
|
|||||||
|
|
||||||
Draw_DrawString(10, 50, COLOR_WHITE, "Current status:");
|
Draw_DrawString(10, 50, COLOR_WHITE, "Current status:");
|
||||||
Draw_DrawString(100, 50, (((mcuIRQMask & 0x00000001) == 0x00000001) ? COLOR_RED : COLOR_GREEN), (((mcuIRQMask & 0x00000001) == 0x00000001) ? " DISABLED" : " ENABLED "));
|
Draw_DrawString(100, 50, (((mcuIRQMask & 0x00000001) == 0x00000001) ? COLOR_RED : COLOR_GREEN), (((mcuIRQMask & 0x00000001) == 0x00000001) ? " DISABLED" : " ENABLED "));
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & BUTTON_A)
|
||||||
@ -188,3 +243,61 @@ void SysConfigMenu_TogglePowerButton(void)
|
|||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!terminationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SysConfigMenu_ControlWifi(void)
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
int slot = 0;
|
||||||
|
char slotString[12] = {0};
|
||||||
|
sprintf(slotString, ">1< 2 3 ");
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_DrawString(10, 10, COLOR_TITLE, "System configuration menu");
|
||||||
|
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to force a connection to slot:");
|
||||||
|
Draw_DrawString(10, 40, COLOR_WHITE, slotString);
|
||||||
|
Draw_DrawString(10, 60, COLOR_WHITE, "Press B to go back.");
|
||||||
|
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
|
if(pressed & BUTTON_A)
|
||||||
|
{
|
||||||
|
SysConfigMenu_ForceWifiConnection(slot);
|
||||||
|
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
}
|
||||||
|
else if(pressed & BUTTON_LEFT)
|
||||||
|
{
|
||||||
|
slotString[slot * 4] = ' ';
|
||||||
|
slotString[(slot * 4) + 2] = ' ';
|
||||||
|
slot--;
|
||||||
|
if(slot == -1)
|
||||||
|
slot = 2;
|
||||||
|
slotString[slot * 4] = '>';
|
||||||
|
slotString[(slot * 4) + 2] = '<';
|
||||||
|
}
|
||||||
|
else if(pressed & BUTTON_RIGHT)
|
||||||
|
{
|
||||||
|
slotString[slot * 4] = ' ';
|
||||||
|
slotString[(slot * 4) + 2] = ' ';
|
||||||
|
slot++;
|
||||||
|
if(slot == 3)
|
||||||
|
slot = 0;
|
||||||
|
slotString[slot * 4] = '>';
|
||||||
|
slotString[(slot * 4) + 2] = '<';
|
||||||
|
}
|
||||||
|
else if(pressed & BUTTON_B)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(!terminationRequest);
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <math.h>
|
//#include <math.h>
|
||||||
|
|
||||||
#include "redshift/redshift.h"
|
#include "redshift/redshift.h"
|
||||||
|
|
||||||
@ -282,8 +282,10 @@ interpolate_color(float a, const float *c1, const float *c2, float *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Helper macro used in the fill functions */
|
/* Helper macro used in the fill functions */
|
||||||
#define F(Y, C) pow((Y) * setting->brightness * \
|
#define F(Y, C) ((Y) * white_point[C])
|
||||||
white_point[C], 1.0/setting->gamma[C])
|
|
||||||
|
/*#define F(Y, C) pow((Y) * setting->brightness * \
|
||||||
|
white_point[C], 1.0/setting->gamma[C])*/
|
||||||
|
|
||||||
void
|
void
|
||||||
colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b,
|
colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b,
|
||||||
@ -324,4 +326,4 @@ colorramp_fill_float(float *gamma_r, float *gamma_g, float *gamma_b,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef F
|
#undef F
|
||||||
|
@ -28,7 +28,7 @@ INCLUDES := include
|
|||||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
DEFINES := -DARM11 -D_3DS
|
DEFINES := -DARM11 -D_3DS
|
||||||
|
|
||||||
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -O2 -mword-relocations \
|
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Os -mword-relocations \
|
||||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ Result ReceiveNotification(SessionData *sessionData, u32 *notificationId)
|
|||||||
|
|
||||||
if(processData == NULL || !processData->notificationEnabled || processData->nbPendingNotifications == 0)
|
if(processData == NULL || !processData->notificationEnabled || processData->nbPendingNotifications == 0)
|
||||||
{
|
{
|
||||||
if(processData->nbPendingNotifications)
|
if(processData != NULL && processData->nbPendingNotifications)
|
||||||
*notificationId = 0;
|
*notificationId = 0;
|
||||||
return 0xD8806404;
|
return 0xD8806404;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user