Update and commit missing files
This commit is contained in:
parent
d06770063c
commit
19b8e017ef
8
.idea/markdown-exported-files.xml
Normal file
8
.idea/markdown-exported-files.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownExportedFiles">
|
||||
<htmlFiles />
|
||||
<imageFiles />
|
||||
<otherFiles />
|
||||
</component>
|
||||
</project>
|
@ -1,13 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<app version="1">
|
||||
<name>TCPgecko</name>
|
||||
<coder>wj44, BullyWiiPlaza</coder>
|
||||
<version>2.2</version>
|
||||
<short_description>WiiU RAM Hacking</short_description>
|
||||
<long_description>A memory editor that does magical things to your games. In order to apply Cafe Codes (real-time cheats) use JGecko U.
|
||||
Special Thanks to:
|
||||
Chadderz, Marionumber1 - TCPGecko codehandler
|
||||
pwsincd - icon and xml
|
||||
CosmoCortney - codehandler for cheat codes, xml
|
||||
</long_description>
|
||||
</app>
|
||||
|
||||
<app>
|
||||
<name>TCP Gecko</name>
|
||||
<coder>BullyWiiPlaza, wj44, dimok, Chadderz, Marionumber1</coder>
|
||||
<version>2.3</version>
|
||||
<short_description>WiiU RAM Hacking</short_description>
|
||||
<long_description>A memory editor that does magical things to your games. In order to develop and apply real-time
|
||||
cheats use JGecko U.
|
||||
|
||||
Special thanks to:
|
||||
Chadderz, Marionumber1 - Original TCP Gecko Installer
|
||||
dimok - Homebrew Launcher
|
||||
kinnay - Diibugger
|
||||
pwsincd - Icon and XML
|
||||
CosmoCortney - Cheat code handler
|
||||
</long_description>
|
||||
</app>
|
16
src/address.c
Normal file
16
src/address.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include "address.h"
|
||||
#include "dynamic_libs/os_functions.h"
|
||||
|
||||
int validateAddressRange(int starting_address, int ending_address) {
|
||||
return __OSValidateAddressSpaceRange(1, (void *) starting_address, ending_address - starting_address + 1);
|
||||
}
|
||||
|
||||
bool isValidDataAddress(int address) {
|
||||
return OSIsAddressValid((const void *) address)
|
||||
&& address >= 0x10000000
|
||||
&& address < 0x50000000;
|
||||
}
|
||||
|
||||
int roundUpToAligned(int number) {
|
||||
return (number + 3) & ~0x03;
|
||||
}
|
12
src/address.h
Normal file
12
src/address.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef TCPGECKO_ADDRESS_H
|
||||
#define TCPGECKO_ADDRESS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
int validateAddressRange(int starting_address, int ending_address);
|
||||
|
||||
bool isValidDataAddress(int address);
|
||||
|
||||
int roundUpToAligned(int number);
|
||||
|
||||
#endif
|
53
src/assertions.h
Normal file
53
src/assertions.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef TCPGECKO_ASSERTIONS_H
|
||||
#define TCPGECKO_ASSERTIONS_H
|
||||
|
||||
#define ASSERT_MINIMUM_HOLDS(actual, minimum, variableName) \
|
||||
if(actual < minimum) { \
|
||||
char buffer[100] = {0}; \
|
||||
__os_snprintf(buffer, 100, "%s: Limit exceeded (minimum: %i, actual: %i)", variableName, minimum, actual); \
|
||||
OSFatal(buffer); \
|
||||
} \
|
||||
|
||||
#define ASSERT_MAXIMUM_HOLDS(maximum, actual, variableName) \
|
||||
if(actual > maximum) { \
|
||||
char buffer[100] = {0}; \
|
||||
__os_snprintf(buffer, 100, "%s: Limit exceeded (maximum: %i, actual: %i)", variableName, maximum, actual); \
|
||||
OSFatal(buffer); \
|
||||
} \
|
||||
|
||||
#define ASSERT_FUNCTION_SUCCEEDED(returnValue, functionName) \
|
||||
if (returnValue < 0) { \
|
||||
char buffer[100] = {0}; \
|
||||
__os_snprintf(buffer, 100, "%s failed with return value: %i", functionName, returnValue); \
|
||||
OSFatal(buffer); \
|
||||
} \
|
||||
|
||||
#define ASSERT_VALID_EFFECTIVE_ADDRESS(effectiveAddress, message) \
|
||||
if(!OSIsAddressValid((void *) effectiveAddress)) { \
|
||||
char buffer[100] = {0}; \
|
||||
__os_snprintf(buffer, 100, "Address %04x invalid: %s", effectiveAddress, message); \
|
||||
OSFatal(buffer); \
|
||||
}
|
||||
|
||||
#define ASSERT_INTEGER(actual, expected, name) \
|
||||
if(actual != expected) { \
|
||||
char buffer[50] = {0}; \
|
||||
__os_snprintf(buffer, 50, "%s assertion failed: %i == %i", name, actual, expected); \
|
||||
OSFatal(buffer); \
|
||||
}
|
||||
|
||||
#define ASSERT_STRING(actual, expected) \
|
||||
if(strcmp(actual, expected) != 0) { \
|
||||
char buffer[50] = {0}; \
|
||||
__os_snprintf(buffer, 50, "String assertion failed: \"%s\" == \"%s\"", actual, expected); \
|
||||
OSFatal(buffer); \
|
||||
}
|
||||
|
||||
#define ASSERT_ALLOCATED(variable, name) \
|
||||
if(variable == 0) { \
|
||||
char buffer[50] = {0}; \
|
||||
__os_snprintf(buffer, 50, "%s allocation failed", name); \
|
||||
OSFatal(buffer); \
|
||||
}
|
||||
|
||||
#endif
|
37
src/disassembler.c
Normal file
37
src/disassembler.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include "disassembler.h"
|
||||
#include "assertions.h"
|
||||
#include "dynamic_libs/os_functions.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
char *disassemblerBuffer;
|
||||
void *disassemblerBufferPointer;
|
||||
|
||||
#define DISASSEMBLER_BUFFER_SIZE 0x1024
|
||||
|
||||
void formatDisassembled(char *format, ...) {
|
||||
if (!disassemblerBuffer) {
|
||||
disassemblerBuffer = malloc(DISASSEMBLER_BUFFER_SIZE);
|
||||
ASSERT_ALLOCATED(disassemblerBuffer, "Disassembler buffer")
|
||||
disassemblerBufferPointer = disassemblerBuffer;
|
||||
}
|
||||
|
||||
va_list variableArguments;
|
||||
va_start(variableArguments, format);
|
||||
char *temporaryBuffer;
|
||||
int printedBytesCount = vasprintf(&temporaryBuffer, format, variableArguments);
|
||||
ASSERT_ALLOCATED(temporaryBuffer, "Temporary buffer")
|
||||
ASSERT_MINIMUM_HOLDS(printedBytesCount, 1, "Printed bytes count")
|
||||
va_end(variableArguments);
|
||||
|
||||
// Do not smash the buffer
|
||||
long projectedSize = (void *) disassemblerBuffer - disassemblerBufferPointer + printedBytesCount;
|
||||
if (projectedSize < DISASSEMBLER_BUFFER_SIZE) {
|
||||
memcpy(disassemblerBuffer, temporaryBuffer, printedBytesCount);
|
||||
disassemblerBuffer += printedBytesCount;
|
||||
}
|
||||
|
||||
free(temporaryBuffer);
|
||||
}
|
6
src/disassembler.h
Normal file
6
src/disassembler.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef TCPGECKO_DISASSEMBLER_H
|
||||
#define TCPGECKO_DISASSEMBLER_H
|
||||
|
||||
void formatDisassembled(char *format, ...);
|
||||
|
||||
#endif
|
@ -2,7 +2,7 @@
|
||||
#include "dynamic_libs/gx2_functions.h"
|
||||
#include "dynamic_libs/socket_functions.h"
|
||||
#include "common/common.h"
|
||||
#include "pygecko.h"
|
||||
#include "tcp_gecko.h"
|
||||
#include "main.h"
|
||||
#include "utils/logger.h"
|
||||
#include "title.h"
|
||||
|
113
src/kernel.h
Normal file
113
src/kernel.h
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel/syscalls.h"
|
||||
#include "assertions.h"
|
||||
#include "dynamic_libs/os_functions.h"
|
||||
#include "tcp_gecko.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
unsigned char *kernelCopyBuffer[sizeof(int)];
|
||||
|
||||
// TODO Variable size, not hard-coded
|
||||
unsigned char *kernelCopyBufferOld[DATA_BUFFER_SIZE];
|
||||
|
||||
void kernelCopyData(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) {
|
||||
if (length > DATA_BUFFER_SIZE) {
|
||||
OSFatal("Kernel copy buffer size exceeded");
|
||||
}
|
||||
|
||||
memcpy(kernelCopyBufferOld, sourceBuffer, length);
|
||||
SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &kernelCopyBufferOld, length);
|
||||
DCFlushRange(destinationBuffer, (u32) length);
|
||||
}
|
||||
|
||||
void kernelCopyInt(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) {
|
||||
memcpy(kernelCopyBuffer, sourceBuffer, length);
|
||||
unsigned int destinationAddress = (unsigned int) OSEffectiveToPhysical(destinationBuffer);
|
||||
SC0x25_KernelCopyData(destinationAddress, (unsigned int) &kernelCopyBuffer, length);
|
||||
DCFlushRange(destinationBuffer, (u32) length);
|
||||
}
|
||||
|
||||
void writeKernelMemory(const void *address, uint32_t value) {
|
||||
((int *) kernelCopyBuffer)[0] = value;
|
||||
kernelCopyInt((unsigned char *) address, (unsigned char *) kernelCopyBuffer, sizeof(int));
|
||||
}
|
||||
|
||||
int readKernelMemory(const void *address) {
|
||||
// For addresses in that range use Chadderz' function to avoid crashing
|
||||
if (address > (const void *) 0xF0000000) {
|
||||
log_print("Using Chadderz' kern_read()...\n");
|
||||
return kern_read(address);
|
||||
}
|
||||
|
||||
log_print("Using dimok's kernelCopy()...\n");
|
||||
unsigned char *readBuffer[sizeof(int)];
|
||||
kernelCopyInt((unsigned char *) readBuffer, (unsigned char *) address, sizeof(int));
|
||||
|
||||
return ((int *) readBuffer)[0];
|
||||
}
|
||||
|
||||
#define KERNEL_COPY_SOURCE_ADDRESS 0x10100000
|
||||
|
||||
int kernelCopyService(int argc, void *argv) {
|
||||
while (true) {
|
||||
// Read the destination address from the source address
|
||||
int destinationAddress = *(int *) KERNEL_COPY_SOURCE_ADDRESS;
|
||||
|
||||
// Avoid crashing
|
||||
if (OSIsAddressValid((const void *) destinationAddress)) {
|
||||
// Perform memory copy
|
||||
unsigned char *valueBuffer = (unsigned char *) (KERNEL_COPY_SOURCE_ADDRESS + 4);
|
||||
kernelCopyInt((unsigned char *) destinationAddress, valueBuffer, 4);
|
||||
|
||||
// "Consume" address and value for synchronization with the code handler for instance
|
||||
*(int *) KERNEL_COPY_SOURCE_ADDRESS = 0;
|
||||
*(((int *) KERNEL_COPY_SOURCE_ADDRESS) + 1) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startKernelCopyService() {
|
||||
unsigned int stack = (unsigned int) memalign(0x40, 0x100);
|
||||
ASSERT_ALLOCATED(stack, "Kernel copy thread stack")
|
||||
stack += 0x100;
|
||||
void *thread = memalign(0x40, 0x1000);
|
||||
ASSERT_ALLOCATED(thread, "Kernel copy thread")
|
||||
|
||||
int status = OSCreateThread(thread, kernelCopyService, 1, NULL, (u32) stack + sizeof(stack), sizeof(stack), 31,
|
||||
OS_THREAD_ATTR_AFFINITY_CORE1 | OS_THREAD_ATTR_PINNED_AFFINITY | OS_THREAD_ATTR_DETACH);
|
||||
ASSERT_INTEGER(status, 1, "Creating kernel copy thread")
|
||||
// OSSetThreadName(thread, "Kernel Copier");
|
||||
OSResumeThread(thread);
|
||||
}
|
||||
|
||||
#define MINIMUM_KERNEL_COMPARE_LENGTH 4
|
||||
#define KERNEL_MEMORY_COMPARE_STEP_SIZE 1
|
||||
|
||||
int kernelMemoryCompare(const void *sourceBuffer,
|
||||
const void *destinationBuffer,
|
||||
int length) {
|
||||
if (length < MINIMUM_KERNEL_COMPARE_LENGTH) {
|
||||
ASSERT_MINIMUM_HOLDS(length, MINIMUM_KERNEL_COMPARE_LENGTH, "length");
|
||||
}
|
||||
|
||||
bool loopEntered = false;
|
||||
|
||||
while (kern_read(sourceBuffer) == kern_read(destinationBuffer)) {
|
||||
loopEntered = true;
|
||||
sourceBuffer = (char *) sourceBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE;
|
||||
destinationBuffer = (char *) destinationBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE;
|
||||
length -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
|
||||
|
||||
if (length <= MINIMUM_KERNEL_COMPARE_LENGTH - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loopEntered) {
|
||||
sourceBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
|
||||
destinationBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
|
||||
}
|
||||
|
||||
return kern_read(sourceBuffer) - kern_read(destinationBuffer);
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
#include <string.h>
|
||||
#include "common/common.h"
|
||||
#include "common/kernel_defs.h"
|
||||
#include "kernel/kernel_functions.h"
|
||||
#include "kernel/syscalls.h"
|
||||
#include "pygecko.h"
|
||||
#include "../common/kernel_defs.h"
|
||||
#include "../kernel/kernel_functions.h"
|
||||
|
||||
/* our retain data */
|
||||
ReducedCosAppXmlInfo cosAppXmlInfoStruct;
|
||||
|
13
src/main.c
13
src/main.c
@ -44,10 +44,6 @@ int Menu_Main(void) {
|
||||
InitVPadFunctionPointers();
|
||||
InitSysFunctionPointers();
|
||||
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
log_print("Patching functions\n");
|
||||
applyFunctionPatches();
|
||||
|
||||
if (strcasecmp("men.rpx", cosAppXmlInfoStruct.rpx_name) == 0) {
|
||||
return EXIT_RELAUNCH_ON_LOAD;
|
||||
} else if (strlen(cosAppXmlInfoStruct.rpx_name) > 0 &&
|
||||
@ -141,15 +137,20 @@ int Menu_Main(void) {
|
||||
break;
|
||||
}
|
||||
|
||||
// A Button
|
||||
// A Button pressed
|
||||
if (pressedButtons & VPAD_BUTTON_A) {
|
||||
unsigned int physicalCodeHandlerAddress = (unsigned int) OSEffectiveToPhysical(
|
||||
(void *) CODE_HANDLER_INSTALL_ADDRESS);
|
||||
SC0x25_KernelCopyData((u32) physicalCodeHandlerAddress, (int) codeHandler, codeHandlerLength);
|
||||
SC0x25_KernelCopyData((u32) physicalCodeHandlerAddress, (unsigned int) codeHandler, codeHandlerLength);
|
||||
DCFlushRange((const void *) CODE_HANDLER_INSTALL_ADDRESS, (u32) codeHandlerLength);
|
||||
|
||||
isCodeHandlerInstalled = true;
|
||||
launchMethod = TCP_GECKO;
|
||||
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
log_print("Patching functions\n");
|
||||
applyFunctionPatches();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,7 @@
|
||||
|
||||
static volatile int executionCounter = 0;
|
||||
|
||||
declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, s32
|
||||
scan_target) {
|
||||
declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, s32 scan_target) {
|
||||
if (executionCounter > 120) {
|
||||
GX2Surface surface = colorBuffer->surface;
|
||||
/*s32 format = surface.format;
|
||||
@ -53,8 +52,7 @@ declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *
|
||||
jpeg.img_id = 0;
|
||||
}*/
|
||||
|
||||
log_printf("GX2CopyColorBufferToScanBuffer {surface width:%d, height:%d, image size:%d, image data:%x}\n",
|
||||
surface.width, surface.height, surface.image_size, surface.image_data);
|
||||
log_printf("GX2CopyColorBufferToScanBuffer {surface width:%d, height:%d, image size:%d, image data:%x}\n", surface.width, surface.height, surface.image_size, surface.image_data);
|
||||
|
||||
executionCounter = 0;
|
||||
}
|
||||
@ -65,7 +63,7 @@ declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *
|
||||
}
|
||||
|
||||
FunctionHook method_hooks_gx2[] __attribute__((section(".data"))) = {
|
||||
makeFunctionHook(GX2CopyColorBufferToScanBuffer, LIB_GX2, STATIC_FUNCTION)
|
||||
// makeFunctionHook(GX2CopyColorBufferToScanBuffer, LIB_GX2, STATIC_FUNCTION)
|
||||
};
|
||||
|
||||
u32 method_hooks_size_gx2 __attribute__((section(".data"))) = sizeof(method_hooks_gx2) / sizeof(FunctionHook);
|
||||
|
50
src/pause.h
Normal file
50
src/pause.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "assertions.h"
|
||||
#include "dynamic_libs/os_functions.h"
|
||||
#include "common/fs_defs.h"
|
||||
#include "kernel.h"
|
||||
|
||||
int (*AVMGetDRCScanMode)(int);
|
||||
|
||||
unsigned long getConsoleStatePatchAddress() {
|
||||
if (AVMGetDRCScanMode) {
|
||||
log_print("Already acquired!\n");
|
||||
} else {
|
||||
// Acquire the RPL and function
|
||||
log_print("Acquiring...\n");
|
||||
unsigned int avm_handle;
|
||||
OSDynLoad_Acquire("avm.rpl", &avm_handle);
|
||||
ASSERT_ALLOCATED(avm_handle, "avm.rpl")
|
||||
OSDynLoad_FindExport((u32) avm_handle, 0, "AVMGetDRCScanMode", &AVMGetDRCScanMode);
|
||||
ASSERT_ALLOCATED(AVMGetDRCScanMode, "AVMGetDRCScanMode")
|
||||
log_print("Acquired!\n");
|
||||
}
|
||||
|
||||
return (unsigned long) (AVMGetDRCScanMode + 0x44);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PAUSED = 0x38000001,
|
||||
RUNNING = 0x38000000
|
||||
} ConsoleState;
|
||||
|
||||
void writeConsoleState(ConsoleState state) {
|
||||
// Get the value to write
|
||||
int patchValue = state;
|
||||
log_printf("Patch value: %x\n", patchValue);
|
||||
|
||||
// Write the value
|
||||
unsigned int patchAddress = getConsoleStatePatchAddress();
|
||||
log_printf("Patch address: %x\n", patchAddress);
|
||||
kernelCopyData((unsigned char *) patchAddress, (unsigned char *) &patchValue, 4);
|
||||
}
|
||||
|
||||
bool isConsolePaused() {
|
||||
unsigned int patchAddress = getConsoleStatePatchAddress();
|
||||
log_printf("Patch address: %x\n", patchAddress);
|
||||
int value = *(unsigned int *) patchAddress;
|
||||
|
||||
return value == PAUSED;
|
||||
}
|
@ -35,7 +35,31 @@ typedef struct OSContext {
|
||||
uint32_t exception_specific0;
|
||||
uint32_t exception_specific1;
|
||||
|
||||
/* There is actually a lot more here but we don't need the rest*/
|
||||
u32 exception_type;
|
||||
u32 reserved;
|
||||
|
||||
double fpscr;
|
||||
double fpr[32];
|
||||
|
||||
u16 spinLockCount;
|
||||
u16 state;
|
||||
|
||||
u32 gqr[8];
|
||||
u32 pir;
|
||||
double psf[32];
|
||||
|
||||
u64 coretime[3];
|
||||
u64 starttime;
|
||||
|
||||
u32 error;
|
||||
u32 attributes;
|
||||
|
||||
u32 pmc1;
|
||||
u32 pmc2;
|
||||
u32 pmc3;
|
||||
u32 pmc4;
|
||||
u32 mmcr0;
|
||||
u32 mmcr1;
|
||||
} OSContext;
|
||||
|
||||
#define CPU_STACK_TRACE_DEPTH 10
|
||||
|
154
src/system/hardware_breakpoints.h
Normal file
154
src/system/hardware_breakpoints.h
Normal file
@ -0,0 +1,154 @@
|
||||
#include "../utils/stringify.h"
|
||||
#include "../dynamic_libs/os_functions.h"
|
||||
#include "threads.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../main.h"
|
||||
#include "utilities.h"
|
||||
#include "software_breakpoints.h"
|
||||
#include "../common/kernel_types.h"
|
||||
|
||||
#ifndef TCPGECKO_BREAKPOINTS_H
|
||||
#define TCPGECKO_BREAKPOINTS_H
|
||||
|
||||
// Special purpose registers
|
||||
#define IABR 0x3F2
|
||||
#define DABR 0x3F5
|
||||
|
||||
// http://www.ds.ewi.tudelft.nl/vakken/in1006/instruction-set/mtspr.html
|
||||
#define mtspr(spr, value) \
|
||||
__asm__ __volatile__ ("mtspr %0, %1" : : "K" (spr), "r" (value)) \
|
||||
|
||||
|
||||
// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.alangref/idalangref_isync_ics_instrs.htm
|
||||
static inline void isync() {
|
||||
__asm__ __volatile__ ("isync" : : : "memory");
|
||||
}
|
||||
|
||||
// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.alangref/idalangref_eieio_instrs.htm
|
||||
static inline void eieio() {
|
||||
__asm__ __volatile__ ("eieio" : : : "memory");
|
||||
}
|
||||
|
||||
// https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.alangref/idalangref_rfi_retfinter_instrs.htm
|
||||
static inline void rfi() {
|
||||
__asm__ __volatile__ ("rfi" : : : "memory");
|
||||
}
|
||||
|
||||
// https://www.manualslib.com/manual/606065/Ibm-Powerpc-750gx.html?page=64
|
||||
static inline void setIABR(unsigned int address) {
|
||||
mtspr(IABR, address);
|
||||
eieio();
|
||||
isync();
|
||||
}
|
||||
|
||||
static inline int getIABRAddress() {
|
||||
return mfspr(IABR);
|
||||
}
|
||||
|
||||
static inline int getDABRAddress(void *interruptedContext) {
|
||||
OSContext *context = (OSContext *) interruptedContext;
|
||||
return (int) context->srr0; // Offset 0xA4
|
||||
}
|
||||
|
||||
static inline int getIABRMatch(void *interruptedContext) {
|
||||
OSContext *context = (OSContext *) interruptedContext;
|
||||
return (int) context->exception_specific1; // Offset 0x98
|
||||
}
|
||||
|
||||
unsigned char breakPointHandler(void *interruptedContext);
|
||||
|
||||
void registerBreakPointHandler() {
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
log_print("Registering breakpoint handler...\n");
|
||||
// TODO Not working, never called?
|
||||
// OSSetExceptionCallback((u8) OS_EXCEPTION_DSI, &breakPointHandler);
|
||||
// OSSetExceptionCallback((u8) OS_EXCEPTION_ISI, &breakPointHandler);
|
||||
// OSSetExceptionCallback((u8) OS_EXCEPTION_PROGRAM, &breakPointHandler);
|
||||
OSSetExceptionCallbackEx((u8) OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, (u8) OS_EXCEPTION_PROGRAM, &breakPointHandler);
|
||||
// __OSSetInterruptHandler((u8) OS_EXCEPTION_PROGRAM, &breakPointHandler);
|
||||
log_print("Breakpoint handler(s) registered!\n");
|
||||
}
|
||||
|
||||
/*void forceDebuggerInitialized() {
|
||||
unsigned char patchBytes[] = {0x38, 0x60, 0x00, 0x01};
|
||||
patchFunction(OSIsDebuggerInitialized, (char *) patchBytes, sizeof(patchBytes), 0x1C);
|
||||
}
|
||||
|
||||
void forceDebuggerPresent() {
|
||||
unsigned char patchBytes[] = {0x38, 0x60, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00};
|
||||
patchFunction(OSIsDebuggerPresent, (char *) patchBytes, sizeof(patchBytes), 0x0);
|
||||
}*/
|
||||
|
||||
static inline void setupBreakpointSupport() {
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
/*log_print("Clear and enable...\n");
|
||||
__OSClearAndEnableInterrupt();
|
||||
log_print("Restore...\n");
|
||||
OSRestoreInterrupts();
|
||||
log_print("Enable...\n");
|
||||
OSEnableInterrupts();
|
||||
forceDebuggerPresent();
|
||||
forceDebuggerInitialized();*/
|
||||
|
||||
registerBreakPointHandler();
|
||||
}
|
||||
|
||||
void setDataBreakpoint(int address, bool read, bool write) {
|
||||
setupBreakpointSupport();
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
log_print("Setting DABR...\n");
|
||||
OSSetDABR(1, address, read, write);
|
||||
log_print("DABR set\n");
|
||||
int enabled = OSIsInterruptEnabled();
|
||||
log_printf("Interrupts enabled: %i\n", enabled);
|
||||
}
|
||||
|
||||
void setInstructionBreakpoint(unsigned int address) {
|
||||
setupBreakpointSupport();
|
||||
|
||||
// int returnedAddress;
|
||||
|
||||
log_print("Setting IABR #1...\n");
|
||||
// OSSetIABR(1, address);
|
||||
setIABR(address);
|
||||
log_print("IABR set #1...\n");
|
||||
/*
|
||||
// TODO Causes crash
|
||||
returnedAddress = getIABRAddress();
|
||||
log_printf("IABR spr value: %08x\n", returnedAddress);
|
||||
|
||||
log_print("Setting IABR #2...\n");
|
||||
setIABR(address);
|
||||
log_print("IABR set #2...\n");
|
||||
returnedAddress = mfspr(IABR);
|
||||
log_printf("IABR spr value: %08x\n", returnedAddress);*/
|
||||
}
|
||||
|
||||
unsigned char breakPointHandler(void *interruptedContext) {
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
|
||||
// Check for data breakpoints
|
||||
int dataAddress = getDABRAddress(interruptedContext);
|
||||
if (OSIsAddressValid((const void *) dataAddress)) {
|
||||
log_printf("Data breakpoint address: %x08\n", dataAddress);
|
||||
} else {
|
||||
log_printf("Data breakpoint invalid address: %x08\n", dataAddress);
|
||||
|
||||
// Check for instruction breakpoints
|
||||
int instructionAddress = getIABRMatch(interruptedContext);
|
||||
if (OSIsAddressValid((const void *) instructionAddress)) {
|
||||
log_printf("Instruction breakpoint address: %x08\n", dataAddress);
|
||||
} else {
|
||||
log_print("Instruction breakpoint failed!\n");
|
||||
}
|
||||
}
|
||||
|
||||
setDataBreakpoint(0, false, false);
|
||||
setInstructionBreakpoint(0);
|
||||
|
||||
rfi();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
38
src/system/threads.c
Normal file
38
src/system/threads.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "threads.h"
|
||||
#include "../utils/linked_list.h"
|
||||
#include "../dynamic_libs/os_functions.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../main.h"
|
||||
|
||||
struct node *getAllThreads() {
|
||||
log_init(COMPUTER_IP_ADDRESS);
|
||||
|
||||
struct node *threads = NULL;
|
||||
int currentThreadAddress = OSGetCurrentThread();
|
||||
log_printf("Thread address: %08x\n", currentThreadAddress);
|
||||
int iterationThreadAddress = currentThreadAddress;
|
||||
int temporaryThreadAddress;
|
||||
|
||||
// Follow "previous thread" pointers back to the beginning
|
||||
while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + PREVIOUS_THREAD)) != 0) {
|
||||
log_printf("Temporary thread address going backwards: %08x\n", temporaryThreadAddress);
|
||||
iterationThreadAddress = temporaryThreadAddress;
|
||||
}
|
||||
|
||||
// Now iterate over all threads
|
||||
while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + NEXT_THREAD)) != 0) {
|
||||
// Grab the thread's address
|
||||
log_printf("Temporary thread address going forward: %08x\n", temporaryThreadAddress);
|
||||
threads = insert(threads, (void *) iterationThreadAddress);
|
||||
log_printf("Inserted: %08x\n", iterationThreadAddress);
|
||||
iterationThreadAddress = temporaryThreadAddress;
|
||||
}
|
||||
|
||||
// The previous while would skip the last thread so add it as well
|
||||
threads = insert(threads, (void *) iterationThreadAddress);
|
||||
log_printf("Inserted: %08x\n", iterationThreadAddress);
|
||||
|
||||
reverse(&threads);
|
||||
|
||||
return threads;
|
||||
}
|
10
src/system/threads.h
Normal file
10
src/system/threads.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef TCPGECKO_THREADS_H
|
||||
#define TCPGECKO_THREADS_H
|
||||
|
||||
#define THREAD_SIZE 0x6A0
|
||||
#define PREVIOUS_THREAD 0x390
|
||||
#define NEXT_THREAD 0x38C
|
||||
|
||||
struct node *getAllThreads();
|
||||
|
||||
#endif
|
@ -17,7 +17,7 @@ void patchFunction(void *function, char *patchBytes, unsigned int patchBytesSize
|
||||
log_print("Patching function...\n");
|
||||
void *patchAddress = function + functionOffset;
|
||||
log_printf("Patch address: %p\n", patchAddress);
|
||||
kernelCopy((unsigned char *) patchAddress, (unsigned char *) patchBytes, patchBytesSize);
|
||||
kernelCopyInt((unsigned char *) patchAddress, (unsigned char *) patchBytes, patchBytesSize);
|
||||
log_print("Successfully patched!\n");
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "tcp_gecko.h"
|
||||
#include <iosuhax.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
@ -10,7 +11,6 @@
|
||||
#include "main.h"
|
||||
#include "dynamic_libs/socket_functions.h"
|
||||
#include "dynamic_libs/gx2_functions.h"
|
||||
#include "kernel/syscalls.h"
|
||||
#include "dynamic_libs/fs_functions.h"
|
||||
#include "utils/logger.h"
|
||||
#include "system/memory.h"
|
||||
@ -18,6 +18,7 @@
|
||||
#include "utils/linked_list.h"
|
||||
#include "address.h"
|
||||
#include "system/stack.h"
|
||||
#include "pause.h"
|
||||
|
||||
void *client;
|
||||
void *commandBlock;
|
||||
@ -68,6 +69,7 @@ struct pygecko_bss_t {
|
||||
#define COMMAND_REMOVE_ALL_BREAKPOINTS 0xA6
|
||||
#define COMMAND_POKE_REGISTERS 0xA7
|
||||
#define COMMAND_GET_STACK_TRACE 0xA8
|
||||
#define COMMAND_GET_ENTRY_POINT_ADDRESS 0xB1
|
||||
#define COMMAND_RUN_KERNEL_COPY_SERVICE 0xCD
|
||||
#define COMMAND_IOSU_HAX_READ_FILE 0xD0
|
||||
#define COMMAND_GET_VERSION_HASH 0xE0
|
||||
@ -76,13 +78,12 @@ struct pygecko_bss_t {
|
||||
#define errno (*__gh_errno_ptr())
|
||||
#define MSG_DONT_WAIT 32
|
||||
#define EWOULDBLOCK 6
|
||||
#define DATA_BUFFER_SIZE 0x5000
|
||||
// #define WRITE_SCREEN_MESSAGE_BUFFER_SIZE 100
|
||||
#define SERVER_VERSION "06/02/2017"
|
||||
#define SERVER_VERSION "06/03/2017"
|
||||
#define ONLY_ZEROS_READ 0xB0
|
||||
#define NON_ZEROS_READ 0xBD
|
||||
|
||||
#define VERSION_HASH 0x39C9444B
|
||||
#define VERSION_HASH 0x3AC9444B
|
||||
|
||||
ZEXTERN int ZEXPORT
|
||||
deflateEnd OF((z_streamp
|
||||
@ -100,61 +101,11 @@ int flush
|
||||
|
||||
// ########## Being kernel_copy.h ############
|
||||
|
||||
// TODO Variable size, not hard-coded
|
||||
unsigned char *memcpy_buffer[DATA_BUFFER_SIZE];
|
||||
|
||||
void pygecko_memcpy(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) {
|
||||
memcpy(memcpy_buffer, sourceBuffer, length);
|
||||
SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &memcpy_buffer,
|
||||
length);
|
||||
DCFlushRange(destinationBuffer, (u32) length);
|
||||
}
|
||||
|
||||
// ########## End kernel_copy.h ############
|
||||
|
||||
// ########## Being pause.h ############
|
||||
// ########## Begin pause.h ############
|
||||
|
||||
int (*AVMGetDRCScanMode)(int);
|
||||
|
||||
unsigned long getConsoleStatePatchAddress() {
|
||||
if (AVMGetDRCScanMode) {
|
||||
log_print("Already acquired!\n");
|
||||
} else {
|
||||
// Acquire the RPL and function
|
||||
log_print("Acquiring...\n");
|
||||
unsigned int avm_handle;
|
||||
OSDynLoad_Acquire("avm.rpl", (u32 *) &avm_handle);
|
||||
ASSERT_ALLOCATED(avm_handle, "avm.rpl")
|
||||
OSDynLoad_FindExport((u32) avm_handle, 0, "AVMGetDRCScanMode", &AVMGetDRCScanMode);
|
||||
ASSERT_ALLOCATED(AVMGetDRCScanMode, "AVMGetDRCScanMode")
|
||||
log_print("Acquired!\n");
|
||||
}
|
||||
|
||||
return (unsigned long) (AVMGetDRCScanMode + 0x44);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PAUSED = 0x38000001, RUNNING = 0x38000000
|
||||
} ConsoleState;
|
||||
|
||||
void writeConsoleState(ConsoleState state) {
|
||||
// Get the value to write
|
||||
int patchValue = state;
|
||||
log_printf("Patch value: %x\n", patchValue);
|
||||
|
||||
// Write the value
|
||||
unsigned int patchAddress = getConsoleStatePatchAddress();
|
||||
log_printf("Patch address: %x\n", patchAddress);
|
||||
pygecko_memcpy((unsigned char *) patchAddress, (unsigned char *) &patchValue, 4);
|
||||
}
|
||||
|
||||
bool isConsolePaused() {
|
||||
unsigned int patchAddress = getConsoleStatePatchAddress();
|
||||
log_printf("Patch address: %x\n", patchAddress);
|
||||
int value = *(unsigned int *) patchAddress;
|
||||
|
||||
return value == PAUSED;
|
||||
}
|
||||
|
||||
// ########## End pause.h ############
|
||||
|
||||
@ -327,7 +278,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
destinationAddress = ((int *) buffer)[0];
|
||||
value = ((int *) buffer)[1];
|
||||
|
||||
pygecko_memcpy((unsigned char *) destinationAddress, (unsigned char *) &value, 4);
|
||||
kernelCopyData((unsigned char *) destinationAddress, (unsigned char *) &value, 4);
|
||||
break;
|
||||
}
|
||||
case COMMAND_READ_MEMORY: {
|
||||
@ -374,15 +325,18 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
|
||||
startingAddress += length;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case COMMAND_READ_MEMORY_KERNEL: {
|
||||
const unsigned char *startingAddress, *endingAddress, *useKernRead;
|
||||
ret = recvwait(bss, clientfd, buffer, 3 * sizeof(int));
|
||||
CHECK_ERROR(ret < 0)
|
||||
startingAddress = ((const unsigned char **) buffer)[0];
|
||||
endingAddress = ((const unsigned char **) buffer)[1];
|
||||
useKernRead = ((const unsigned char **) buffer)[2];
|
||||
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (receiving data)")
|
||||
|
||||
int bufferIndex = 0;
|
||||
startingAddress = ((const unsigned char **) buffer)[bufferIndex++];
|
||||
endingAddress = ((const unsigned char **) buffer)[bufferIndex++];
|
||||
useKernRead = ((const unsigned char **) buffer)[bufferIndex];
|
||||
|
||||
while (startingAddress != endingAddress) {
|
||||
int length = (int) (endingAddress - startingAddress);
|
||||
@ -407,12 +361,11 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
ret = sendByte(bss, clientfd, ONLY_ZEROS_READ);
|
||||
CHECK_ERROR(ret < 0)
|
||||
} else {
|
||||
// Send the real bytes now
|
||||
buffer[0] = NON_ZEROS_READ;
|
||||
|
||||
if (useKernRead) {
|
||||
for (int offset = 0; offset < length; offset += sizeof(int)) {
|
||||
*((int *) (buffer + 1) + offset / sizeof(int)) = readKernelMemory(startingAddress + offset);
|
||||
for (int offset = 0; offset < length; offset += 4) {
|
||||
*((int *) (buffer + 1) + offset / 4) = readKernelMemory(startingAddress + offset);
|
||||
}
|
||||
} else {
|
||||
memcpy(buffer + 1, startingAddress, length);
|
||||
@ -430,6 +383,77 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
startingAddress += length;
|
||||
}
|
||||
break;
|
||||
|
||||
/* const unsigned char *startingAddress, *endingAddress, *useKernRead;
|
||||
ret = recvwait(bss, clientfd, buffer, 3 * sizeof(int));
|
||||
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (receiving data)")
|
||||
|
||||
int bufferIndex = 0;
|
||||
startingAddress = ((const unsigned char **) buffer)[bufferIndex++];
|
||||
endingAddress = ((const unsigned char **) buffer)[bufferIndex++];
|
||||
useKernRead = ((const unsigned char **) buffer)[bufferIndex];
|
||||
|
||||
while (startingAddress != endingAddress) {
|
||||
log_printf("Reading memory from %08x to %08x with kernel %i\n", startingAddress, endingAddress,
|
||||
useKernRead);
|
||||
|
||||
unsigned int length = (unsigned int) (endingAddress - startingAddress);
|
||||
|
||||
// Do not smash the buffer
|
||||
if (length > DATA_BUFFER_SIZE) {
|
||||
length = DATA_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
// Figure out if all bytes are zero to possibly avoid sending them
|
||||
log_print("Checking for all zero bytes...\n");
|
||||
unsigned int rangeIterationIndex = 0;
|
||||
for (; rangeIterationIndex < length; rangeIterationIndex++) {
|
||||
int character = useKernRead ? readKernelMemory(startingAddress + rangeIterationIndex)
|
||||
: startingAddress[rangeIterationIndex];
|
||||
if (character != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_print("Preparing to send...\n");
|
||||
if (rangeIterationIndex == length) {
|
||||
// No need to send all zero bytes for performance
|
||||
log_print("All zero...\n");
|
||||
ret = sendByte(bss, clientfd, ONLY_ZEROS_READ);
|
||||
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (only zero bytes read byte)")
|
||||
log_print("Sent!\n");
|
||||
} else {
|
||||
// Send the real bytes now
|
||||
log_print("Real bytes...\n");
|
||||
buffer[0] = NON_ZEROS_READ;
|
||||
|
||||
if (useKernRead) {
|
||||
// kernelCopy(buffer + 1, (unsigned char *) startingAddress, length);
|
||||
for (unsigned int offset = 0; offset < length; offset += sizeof(int)) {
|
||||
*((int *) (buffer + 1) + offset / sizeof(int)) = readKernelMemory(
|
||||
startingAddress + offset);
|
||||
log_printf("Offset: %x\n", offset);
|
||||
}
|
||||
|
||||
log_print("Done kernel reading!\n");
|
||||
} else {
|
||||
log_print("Memory copying...\n");
|
||||
memcpy(buffer + 1, startingAddress, length);
|
||||
log_print("Done copying!\n");
|
||||
}
|
||||
|
||||
log_print("Sending everything...\n");
|
||||
ret = sendwait(bss, clientfd, buffer, length + 1);
|
||||
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (read bytes buffer)")
|
||||
log_print("Sent!\n");
|
||||
}
|
||||
|
||||
startingAddress += length;
|
||||
}
|
||||
|
||||
log_print("Done reading...\n");
|
||||
|
||||
break;*/
|
||||
}
|
||||
case COMMAND_VALIDATE_ADDRESS_RANGE: {
|
||||
ret = recvwait(bss, clientfd, buffer, 8);
|
||||
@ -671,7 +695,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
|
||||
ret = recvwait(bss, clientfd, buffer, length);
|
||||
CHECK_ERROR(ret < 0)
|
||||
pygecko_memcpy(current_address, buffer, (unsigned int) length);
|
||||
kernelCopyData(current_address, buffer, (unsigned int) length);
|
||||
|
||||
current_address += length;
|
||||
}
|
||||
@ -1209,7 +1233,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
|
||||
// Write the assembly to an executable code region
|
||||
int destinationAddress = 0x10000000 - DATA_BUFFER_SIZE;
|
||||
pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
|
||||
kernelCopyData((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
|
||||
|
||||
// Execute the assembly from there
|
||||
void (*function)() = (void (*)()) destinationAddress;
|
||||
@ -1217,7 +1241,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
|
||||
// Clear the memory contents again
|
||||
memset((void *) buffer, 0, DATA_BUFFER_SIZE);
|
||||
pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
|
||||
kernelCopyData((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1336,7 +1360,23 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) {
|
||||
break;
|
||||
}
|
||||
case COMMAND_POKE_REGISTERS: {
|
||||
log_print("Receiving poke registers data...\n");
|
||||
int gprSize = 4 * 32;
|
||||
int fprSize = 8 * 32;
|
||||
ret = recvwait(bss, clientfd, buffer, gprSize + fprSize);
|
||||
log_print("Poking registers...\n");
|
||||
memcpy((void *) crashContext.gpr, (const void *) buffer, gprSize);
|
||||
memcpy((void *) crashContext.fpr, (const void *) buffer, fprSize);
|
||||
|
||||
break;
|
||||
}
|
||||
case COMMAND_GET_ENTRY_POINT_ADDRESS: {
|
||||
u32 *entryPointAddress = (u32 *) *((u32 *) OS_SPECIFICS->addr_OSTitle_main_entry);
|
||||
((u32 *) buffer)[0] = (u32) entryPointAddress;
|
||||
ret = sendwait(bss, clientfd, buffer, sizeof(int));
|
||||
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Entry point address)");
|
||||
|
||||
break;
|
||||
}
|
||||
case COMMAND_RUN_KERNEL_COPY_SERVICE: {
|
||||
if (!kernelCopyServiceStarted) {
|
@ -1,15 +1,13 @@
|
||||
#ifndef _PYGECKO_H_
|
||||
#define _PYGECKO_H_
|
||||
#pragma once
|
||||
|
||||
/* Main */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DATA_BUFFER_SIZE 0x5000
|
||||
|
||||
void startTCPGecko(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
17
src/title.c
Normal file
17
src/title.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include "dynamic_libs/os_functions.h"
|
||||
#include "title.h"
|
||||
|
||||
bool isRunningTitleID(unsigned long long int japaneseTitleID) {
|
||||
unsigned long long int currentTitleID = (unsigned long long int) OSGetTitleID();
|
||||
return currentTitleID == japaneseTitleID // JAP
|
||||
|| currentTitleID == japaneseTitleID + 0x100 // USA
|
||||
|| currentTitleID == japaneseTitleID + 0x200; // EUR
|
||||
}
|
||||
|
||||
bool isRunningAllowedTitleID() {
|
||||
return OSGetTitleID != 0
|
||||
&& !isRunningTitleID(TITLE_ID_MII_VERSE)
|
||||
&& !isRunningTitleID(TITLE_ID_MII_MAKER)
|
||||
&& !isRunningTitleID(TITLE_ID_BAYONETTA_2)
|
||||
&& !isRunningTitleID(TITLE_ID_INTERNET_BROWSER);
|
||||
}
|
11
src/title.h
Normal file
11
src/title.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef TCPGECKO_TITLE_H
|
||||
#define TCPGECKO_TITLE_H
|
||||
|
||||
#define TITLE_ID_MII_VERSE 0x000500301001600A
|
||||
#define TITLE_ID_MII_MAKER 0x000500101004A000
|
||||
#define TITLE_ID_BAYONETTA_2 0x0005000010172500
|
||||
#define TITLE_ID_INTERNET_BROWSER 0x000500301001200A
|
||||
|
||||
bool isRunningAllowedTitleID();
|
||||
|
||||
#endif
|
51
src/utils/linked_list.c
Normal file
51
src/utils/linked_list.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "linked_list.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void destroy(struct node *list) {
|
||||
struct node *currentNode = list;
|
||||
|
||||
while (currentNode != NULL) {
|
||||
struct node *previousNode = currentNode;
|
||||
currentNode = currentNode->next;
|
||||
free(previousNode);
|
||||
}
|
||||
}
|
||||
|
||||
struct node *insert(struct node *list, void *data) {
|
||||
size_t structureSize = sizeof(struct node);
|
||||
struct node *addedNode = (struct node *) malloc(structureSize);
|
||||
|
||||
addedNode->data = data;
|
||||
addedNode->next = list;
|
||||
list = addedNode;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int length(struct node *list) {
|
||||
int length = 0;
|
||||
struct node *current;
|
||||
|
||||
for (current = list; current != NULL; current = current->next) {
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void reverse(struct node **list) {
|
||||
struct node *previous = NULL;
|
||||
struct node *current = *list;
|
||||
struct node *next;
|
||||
|
||||
while (current != NULL) {
|
||||
next = current->next;
|
||||
current->next = previous;
|
||||
previous = current;
|
||||
current = next;
|
||||
}
|
||||
|
||||
*list = previous;
|
||||
}
|
17
src/utils/linked_list.h
Normal file
17
src/utils/linked_list.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef LINKED_LIST_LINKED_DATA_LIST_H
|
||||
#define LINKED_LIST_LINKED_DATA_LIST_H
|
||||
|
||||
struct node {
|
||||
void *data;
|
||||
struct node *next;
|
||||
};
|
||||
|
||||
void destroy(struct node *list);
|
||||
|
||||
struct node *insert(struct node *list, void *data);
|
||||
|
||||
int length(struct node *list);
|
||||
|
||||
void reverse(struct node **list);
|
||||
|
||||
#endif
|
8
src/utils/stringify.h
Normal file
8
src/utils/stringify.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef TCPGECKO_STRINGIFY_H
|
||||
#define TCPGECKO_STRINGIFY_H
|
||||
|
||||
// http://elixir.free-electrons.com/linux/v2.6.24/source/include/linux/stringify.h#L9
|
||||
#define __stringify_1(x) #x
|
||||
#define __stringify(x) __stringify_1(x)
|
||||
|
||||
#endif
|
BIN
tcpgecko.elf
BIN
tcpgecko.elf
Binary file not shown.
Loading…
Reference in New Issue
Block a user