/*
*   This file is part of Luma3DS
*   Copyright (C) 2016-2017 Aurora Wright, TuxSH
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*   Additional Terms 7.b and 7.c of GPLv3 apply to this file:
*       * Requiring preservation of specified reasonable legal notices or
*         author attributions in that material or in the Appropriate Legal
*         Notices displayed by works containing it.
*       * Prohibiting misrepresentation of the origin of that material,
*         or requiring that modified versions of such material be marked in
*         reasonable ways as different from the original version.
*/

#pragma once

#include "types.h"

extern u32 kernelVersion;

struct KMutex;
struct KProcessO3DS;
struct KProcessN3DS;
union  KProcess;
struct HandleDescriptor;
struct KDebugThread;
struct KCodeSet;
struct KDebug;
struct KResourceLimit;
struct KPort;
struct KSession;
struct KLinkedListNode;
struct KThread;
struct Vtable__KAutoObject;
struct KClassToken;
struct Vtable__KBaseInterruptEvent;
struct KObjectMutex;
struct KThreadLinkedList;
struct KMutexLinkedList;
struct KPreemptionTimer;

/* 12 */
typedef struct ALIGN(4) KAutoObject
{
  struct Vtable__KAutoObject *vtable;
  u32 refCount;
} KAutoObject;

/* 10 */
typedef struct KLinkedListNode
{
  struct KLinkedListNode *next;
  struct KLinkedListNode *prev;
  void *key;
} KLinkedListNode;

/* 83 */
typedef struct KLinkedListNodePair
{
  KLinkedListNode *first;
  KLinkedListNode *last;
} KLinkedListNodePair;

/* 11 */
typedef struct KLinkedList
{
  u32 size;
  KLinkedListNodePair nodes;
} KLinkedList;

/* 13 */
typedef struct KSynchronizationObject
{
  KAutoObject autoObject;
  KLinkedList syncedThreads;
} KSynchronizationObject;

/* 91 */
typedef struct KMutexLinkedListNode
{
  struct KMutex *prev;
  struct KMutex *next;
} KMutexLinkedListNode;

/* 1 */
typedef struct ALIGN(4) KMutex
{
  KSynchronizationObject syncObject;
  KMutexLinkedListNode mutexListNode;
  u32 nbThreadsUsingThis;
  struct KThread *lockingThread;
  u32 priority;
  union KProcess *owner;
} KMutex;

/* 92 */
typedef struct KMutexLinkedList
{
  KMutex *first;
  KMutex *last;
} KMutexLinkedList;

/* 45 */
typedef struct KClassToken
{
  const char *name;
  u8 flags;
} KClassToken;

/* 44 */
typedef struct ALIGN(4) Vtable__KAutoObject
{
  void *field_0;
  void *field_4;
  void (*dtor)(KAutoObject *this);
  void *substractResource;
  KAutoObject *(*DecrementReferenceCount)(KAutoObject *this);
  union KProcess *(*GetParentProcess)(KAutoObject *this);
  KClassToken *(*GetClassToken)(KClassToken *out, KAutoObject *this); // >= 9.0 only
  const char *(*GetClassName)(KAutoObject *this); // < 9.0 only
  void *field_20;
  void *field_24;
} Vtable__KAutoObject;

/* 52 */
typedef struct KBaseInterruptEvent
{
  struct Vtable__KBaseInterruptEvent *vtable;
} KBaseInterruptEvent;

/* 55 */
typedef struct ALIGN(4) Vtable__KBaseInterruptEvent
{
  struct KSchedulableInterruptEvent *(*handleInterruptEvent)(KBaseInterruptEvent *, u32);
} Vtable__KBaseInterruptEvent;


/* 67 */
typedef struct KSynchronizationInterruptEvent
{
  KBaseInterruptEvent baseInterruptEvent;
  struct KSynchronizationInterruptEvent *next;
} KSynchronizationInterruptEvent;

/* 77 */
// This one got added a vtable func on 11.3
typedef struct KTimeableInterruptEvent
{
  KSynchronizationInterruptEvent syncInterruptEvent;
  s64 timer;
} KTimeableInterruptEvent;

/* 90 */
typedef KSynchronizationInterruptEvent KSendableInterruptEvent;

/* 68 */
typedef KSynchronizationInterruptEvent KSchedulableInterruptEvent;

/* 85 */
typedef struct KThreadLinkedListNode
{
  struct KThread *prev;
  struct KThread *next;
} KThreadLinkedListNode;


/* 93 */
typedef struct ALIGN(4) KPreemptionTimer
{
  u32 nLimitedTicks;
  u32 timer;
  u32 previousWDTValue;
  u32 wantedDuration;
} KPreemptionTimer;

/* 15 */
typedef struct PACKED ALIGN(4) KThread
{
  KSynchronizationObject syncObject;
  KTimeableInterruptEvent timeableInterruptEvent;
  KSendableInterruptEvent sendableInterruptEvent;
  KSchedulableInterruptEvent schedulableInterruptEvent;
  u8 schedulingMask;
  u8 syncing_1;
  bool shallTerminate;
  u8 syncing_3;
  struct KDebugThread *debugThread;
  u32 basePriority;
  KSynchronizationObject *objectThreadIsWaitingOn;
  Result resultObjectWaited_2;
  struct KObjectMutex *mutex;
  void *arbitrationAddr;
  KLinkedList objectsToWaitFor;
  KMutexLinkedList *mutexList;
  KLinkedList mutexesUsed;
  s32 dynamicPriority;
  u32 coreId;
  KPreemptionTimer *preemptionTimer;
  u32 unknown_1;
  bool isAlive;
  bool isEnded;
  u8 affinityMask, padding;
  union KProcess *ownerProcess;
  u32 threadId;
  void *svcRegisterStorage;
  void *endOfThreadContext;
  s32 idealProcessor;
  void *threadLocalStorage;
  void *threadLocalStorageKernel;
  void *unknown_5;
  KThreadLinkedListNode threadListNode;
  struct KThreadLinkedList *stolenThreadList;
  s32 highestPriority;
} KThread;

/* 79 */
typedef struct KObjectMutex
{
  KThread *owner;
  s16 tryAcquireCounter;
  s16 contextSwitchCounter;
} KObjectMutex;



/* 120 */
typedef enum ProcessStatus
{
  PROCESS_AVAILABLE = 0x1,
  PROCESS_CLOSING = 0x2,
  PROCESS_EXITED = 0x3,
} ProcessStatus;

/* 3 */
typedef struct ALIGN(4) HandleDescriptor
{
  u32 info;
  KAutoObject *pointer;
} HandleDescriptor;

/* 22 */
typedef struct KProcessHandleTable
{
  HandleDescriptor *handleTable;
  s16 maxHandleCount;
  s16 highestHandleCount;
  HandleDescriptor *nextOpenHandleDescriptor;
  s16 totalHandlesUsed;
  s16 handlesInUseCount;
  KObjectMutex mutex;
  HandleDescriptor internalTable[40];
} KProcessHandleTable;

/* 4 */
typedef struct ALIGN(4) KDebugThread
{
  KThread *linkedThread;
  bool usedSvcBreak;
  bool debugLocked;
  bool shallBeDebugLocked;
  bool scheduledInOrOut;
  bool threadAttached;
  bool syscallInOurOut;
  u8 paddingA[2];
  u32 threadExitReason;
  u32 creatorThreadId;
  u32 FAR;
} KDebugThread;

/* 84 */
typedef struct KThreadLinkedList
{
  KThread *first;
  KThread *last;
} KThreadLinkedList;

typedef enum {
  DBGEVENT_ATTACH_PROCESS = 0,  ///< Process attached event.
  DBGEVENT_ATTACH_THREAD  = 1,  ///< Thread attached event.
  DBGEVENT_EXIT_THREAD    = 2,  ///< Thread exit event.
  DBGEVENT_EXIT_PROCESS   = 3,  ///< Process exit event.
  DBGEVENT_EXCEPTION      = 4,  ///< Exception event.
  DBGEVENT_DLL_LOAD       = 5,  ///< DLL load event.
  DBGEVENT_DLL_UNLOAD     = 6,  ///< DLL unload event.
  DBGEVENT_SCHEDULE_IN    = 7,  ///< Schedule in event.
  DBGEVENT_SCHEDULE_OUT   = 8,  ///< Schedule out event.
  DBGEVENT_SYSCALL_IN     = 9,  ///< Syscall in event.
  DBGEVENT_SYSCALL_OUT    = 10, ///< Syscall out event.
  DBGEVENT_OUTPUT_STRING  = 11, ///< Output string event.
  DBGEVENT_MAP            = 12, ///< Map event.
} DebugEventType;

/* 80 */
typedef struct KRecursiveLock
{
  KThread *owner;
  u32 lockCount;
} KRecursiveLock;

typedef enum {
  EXCEVENT_UNDEFINED_INSTRUCTION = 0, ///< Undefined instruction.
  EXCEVENT_PREFETCH_ABORT        = 1, ///< Prefetch abort.
  EXCEVENT_DATA_ABORT            = 2, ///< Data abort (other than the below kind).
  EXCEVENT_UNALIGNED_DATA_ACCESS = 3, ///< Unaligned data access.
  EXCEVENT_ATTACH_BREAK          = 4, ///< Attached break.
  EXCEVENT_STOP_POINT            = 5, ///< Stop point reached.
  EXCEVENT_USER_BREAK            = 6, ///< User break occurred.
  EXCEVENT_DEBUGGER_BREAK        = 7, ///< Debugger break occurred.
  EXCEVENT_UNDEFINED_SYSCALL     = 8, ///< Undefined syscall.
} ExceptionEventType;

/* 6 */
typedef struct ALIGN(4) KDebug
{
  KSynchronizationObject syncObject;
  KSendableInterruptEvent sendableInterruptEvent;
  bool processDebugEventSignaled;
  bool debugStringFlag;
  bool isSignalingDebugEvent;
  bool isPaused;
  DebugEventType debugEventType;
  u32 field_24;
  KThread *lockingThread;
  u32 debugEventFlags;
  u32 stopPointType;
  KLinkedList eventInfos;
  KLinkedList infoForEventsToContinue;
  union KProcess *owner;
  KThread *debuggedThread;
  KThread *threadUsingContinueDebugEvent;
  u32 coreIDOfThreadUsingContinueDebugEvent;
  KLinkedList debuggedThreads;
  KLinkedList threadsOfProcess;
  KRecursiveLock recursiveLock;
  KThread *threadUsingSvcBreak;
  bool noExceptionDebugEventOccured;
  bool exceptionDebugEventSignaled;
  bool shouldUnlockAll;
  bool exceptionDebugEventOccuredWhilePaused;
  bool userBreakEventOccuredWhilePaused;
  bool processHasEnded;
  bool processHasBeenTerminated;
  bool pendingSvcBreakEvent;
  u32 *regdump;
  ExceptionEventType exceptionInfo;
  u16 nbExceptions;
  u16 svcID;
  u16 nbPendingEvents;
  u16 nbPendingExitProcessEvents;
  const char *debugString;
  u32 debugStringLength;
} KDebug;

/* 7 */
typedef struct KResourceLimit
{
  KAutoObject autoObject;
  s32 maxResources[10];
  s32 currentResources[10];
  KObjectMutex objMutex;
  KPreemptionTimer preemptionTimer;
  u32 padding;
} KResourceLimit;

/* 64 */
typedef struct SectionInfo
{
  void *loadAddress;
  u32 nbPages;
} SectionInfo;

/* 23 */
typedef struct KCodeSetMemDescriptor
{
  SectionInfo section;
  struct KLinkedList blocks;
} KCodeSetMemDescriptor;

/* 5 */
typedef struct PACKED ALIGN(4) KCodeSet
{
  KAutoObject autoObject;
  KCodeSetMemDescriptor textSection;
  KCodeSetMemDescriptor rodataSection;
  KCodeSetMemDescriptor dataSection;
  u32 nbTextPages;
  u32 nbRodataPages;
  u32 nbRwPages;
  char processName[8];
  u16 unknown_1;
  u16 unknown_2;
  u64 titleId;
} KCodeSet;

/* 30 */
typedef struct KServerPort
{
  KSynchronizationObject syncObject;
  KLinkedList serverSessions;
  struct KPort *parentKPort;
} KServerPort;

/* 31 */
typedef struct KClientPort
{
  KSynchronizationObject syncObject;
  s16 connectionCount;
  s16 maxConnectionCount;
  struct KPort *parentKPort;
} KClientPort;

/* 8 */
typedef struct KPort
{
  KAutoObject autoObject;
  KServerPort serverPort;
  KClientPort clientPort;
} KPort;

/* 33 */
typedef struct KServerSession
{
  KSynchronizationObject syncObject;
  struct KSession *parentSession;
  KThread *lastStolenThread;
  KThread *firstStolenThread;
  KThread *originatingThread;
} KServerSession;

/* 32 */
typedef struct KClientSession
{
  KSynchronizationObject syncObject;
  struct KSession *parentSession;
  u32 status;
  KClientPort *clientPort;
} KClientSession;

/* 9 */
typedef struct KSession
{
  KAutoObject autoObject;
  KServerSession serverSession;
  KClientSession clientSession;
} KSession;

/* 82 */
typedef struct KUserBindableInterruptEvent
{
  KSchedulableInterruptEvent schedulableInterruptEvent;
  s32 interruptID;
} KUserBindableInterruptEvent;

/* 14 */
typedef struct ALIGN(4) KEvent
{
  KSynchronizationObject syncObject;
  KUserBindableInterruptEvent userBindableInterruptEvent;
  bool isSignaled;
  bool manualClear;
  u8 resetType;
  u8 unused;
  union KProcess *owner;
} KEvent;

/* 16 */
typedef enum MemOp
{
  MEMOP_FREE = 0x1,
  MEMOP_RESERVE = 0x2,
  MEMOP_COMMIT = 0x3,
  MEMOP_MAP = 0x4,
  MEMOP_UNMAP = 0x5,
  MEMOP_PROTECT = 0x6,
  MEMOP_REGION_APP = 0x100,
  MEMOP_REGION_SYSTEM = 0x200,
  MEMOP_REGION_BASE = 0x300,
  MEMOP_LINEAR = 0x10000,
} MemOp;

/* 17 */
typedef enum MemPerm
{
  MEMPERM_NONE = 0x0,
  MEMPERM_R = 0x1,
  MEMPERM_W = 0x2,
  MEMPERM_RW = 0x3,
  MEMPERM_X = 0x4,
  MEMPERM_RX = 0x5,
  MEMPERM_WX = 0x6,
  MEMPERM_RWX = 0x7,
  MEMPERM_DONTCARE = 0x10000000,
} MemPerm;

/* 18 */
typedef struct KObjectLink
{
  void *nextAvailable;
  void *first;
  u32 kheapAllocSize;
  u32 size;
} KObjectLink;

/* 19 */
typedef struct KObjectList
{
  KLinkedList list;
  KObjectMutex mutex;
  s16 totalObjectsCreated;
  s16 padding2;
  KObjectLink objectInfo;
} KObjectList;

/* 21 */
typedef struct KSemaphore
{
  KSynchronizationObject syncObject;
  KUserBindableInterruptEvent userBindableInterruptEvent;
  u32 count;
  u32 maxCount;
  union KProcess *owner;
} KSemaphore;

/* 24 */
typedef struct KBlockInfo
{
  void *memSectionStartKVaddr;
  u32 pageCount;
} KBlockInfo;

/* 25 */
typedef struct KMemoryBlock
{
  void *baseAddr;
  u32 sizeInPages;
  u32 permissions;
  u32 state;
  u32 zero;
} KMemoryBlock;

/* 28 */
typedef struct ALIGN(4) KScheduler
{
  KSchedulableInterruptEvent interruptEvent;
  u32 threadSwitchAttempts;
  bool contextSwitchNeeded;
  bool contextSwitchStartedDuringInterrupt;
  bool triggerCrossCoreInterrupt;
  bool postInterruptReschedulingNeeded;
  s16 coreNumber;
  s16 managedThreadsCount;
  u32 highPriorityThreadsBitfield;
  u32 lowPriorityThreadsBitfield;
  KThread *schedulerThread;
  KThreadLinkedList listOfHandledThreads;
  KThreadLinkedList threadsByPriority[64];
} KScheduler;

/* 46 */
typedef struct PACKED CodeSetInfo
{
  char name[8];
  u16 unknown_1;
  u16 unknown_2;
  u32 unknown_3;
  void *textAddr;
  u32 textSize;
  void *rodataAddr;
  u32 rodataSize;
  void *rwAddr;
  u32 rwSize;
  u32 totalTextPages;
  u32 totalRodataPages;
  u32 totalRwPages;
  u32 unknown_4;
  u64 programId;
} CodeSetInfo;

/* 53 */
typedef struct ALIGN(4) InterruptData
{
  KBaseInterruptEvent *interruptEvent;
  bool disableUponReceipt;
  bool isDisabled;
  u8 priority;
  u8 padding;
} InterruptData;

/* 59 */
typedef struct KFIQInterruptEvent
{
  KSchedulableInterruptEvent schedulableInterruptEvent;
  KEvent *event;
  bool isLevelHighActive;
  bool otherFlag;
} KFIQInterruptEvent;

/* 61 */
typedef enum TLBOperation
{
  NO_TLB_OPERATION = 0x0,
  FLUSH_TLB_ENTRIES_ON_ASID_MATCH = 0x1,
  FLUSH_ENTIRE_TLB = 0x2,
  FLUSH_TLB_ENTRIES_ON_MVA_MATCH = 0x3,
  TLB_RESET_CONTEXT = 0x4,
} TLBOperation;

/* 63 */
typedef struct TLBOperationTarget
{
  union KProcessHwInfo *contextInfo;
  void *VA;
} TLBOperationTarget;

/* 62 */
typedef struct KTLBOperationsInterruptEvent
{
  KBaseInterruptEvent interruptEvent;
  TLBOperationTarget target;
  TLBOperation operation;
} KTLBOperationsInterruptEvent;

/* 65 */
typedef struct KObjectName
{
  char name[12];
  KClientPort *parentClientPort;
} KObjectName;

/* 74 */
typedef enum MemState
{
  MEMSTATE_FREE = 0x0,
  MEMSTATE_RESERVED = 0x1,
  MEMSTATE_IO = 0x2,
  MEMSTATE_STATIC = 0x3,
  MEMSTATE_CODE = 0x4,
  MEMSTATE_PRIVATE = 0x5,
  MEMSTATE_SHARED = 0x6,
  MEMSTATE_CONTINUOUS = 0x7,
  MEMSTATE_ALIASED = 0x8,
  MEMSTATE_ALIAS = 0x9,
  MEMSTATE_ALIAS_CODE = 0xA,
  MEMSTATE_LOCKED = 0xB,
} MemState;

/* 75 */
typedef struct MemoryInfo
{
  void *baseVA;
  u32 size;
  u32 permissions;
  MemState state;
  u32 padding;
} MemoryInfo;

/* 76 */
typedef struct PageInfo
{
  u32 flags;
} PageInfo;

/* 78 */
typedef struct KTimerAndWDTManager
{
  KSynchronizationInterruptEvent syncInterruptEvent;
  u32 counter;
  u32 previousCounter;
  KTimeableInterruptEvent innerInterruptEvent;
  KRecursiveLock recursiveLock;
} KTimerAndWDTManager;

typedef enum ResetType
{
  RESET_ONESHOT = 0x0,
  RESET_STICKY = 0x1,
  RESET_PULSE = 0x2,
} ResetType;

/* 81 */
typedef struct PACKED ALIGN(4) KTimer
{
  KSynchronizationObject syncObject;
  KTimeableInterruptEvent timeableInterruptEvent;
  bool signaled;
  ResetType resetType;
  u16 padding;
  s64 interval;
  s64 currentValue;
  union KProcess *owner;
} KTimer;

/* 86 */
typedef struct KSchedulableInterruptEventLinkedList
{
  KSchedulableInterruptEvent *first;
  KSchedulableInterruptEvent *last;
  u32 unused;
  KThread *handlingKernelThread;
} KSchedulableInterruptEventLinkedList;

/* 87 */
typedef KSchedulableInterruptEvent KThreadTerminationInterruptEvent;

/* 88 */
typedef KSchedulableInterruptEvent KThreadExitInterruptEvent;

/* 89 */
typedef struct ALIGN(4) KInterruptEventMailbox
{
  u32 mailboxID;
  KSendableInterruptEvent *first;
  KSendableInterruptEvent *last;
  bool isBusy;
  KThread *handlingThread;
} KInterruptEventMailbox;

/* 94 */
typedef enum LimitableResource
{
  LIMITABLE_RESOURCE_MAX_PRIORITY = 0x0,
  LIMITABLE_RESOURCE_MAX_COMMIT = 0x1,
  LIMITABLE_RESOURCE_MAX_THREAD = 0x2,
  LIMITABLE_RESOURCE_MAX_EVENT = 0x3,
  LIMITABLE_RESOURCE_MAX_MUTEX = 0x4,
  LIMITABLE_RESOURCE_MAX_SEMAPHORE = 0x5,
  LIMITABLE_RESOURCE_MAX_TIMER = 0x6,
  LIMITABLE_RESOURCE_MAX_SHAREDMEMORY = 0x7,
  LIMITABLE_RESOURCE_MAX_ADDRESSARBITER = 0x8,
  LIMITABLE_RESOURCE_MAX_CPUTIME = 0x9,
  LIMITABLE_RESOURCE_END = 0xA,
  LIMITABLE_RESOURCE_MAX_BIT = 0x80000000,
} LimitableResource;

/* 99 */
typedef struct ALIGN(4) CpuRegisters
{
  u32 r[13];
  u32 sp;
  u32 lr;
  u32 pc;
  u32 cpsr;
} CpuRegisters;

/* 100 */
typedef struct FpuRegisters
{
  union
  {
      struct PACKED { double d[16]; };
      float  s[32];
  };
  u32 fpscr;
  u32 fpexc;
} FpuRegisters;

/* 98 */
typedef struct ThreadContext
{
  CpuRegisters cpuRegisters;
  FpuRegisters fpuRegisters;
} ThreadContext;

/* 101 */
typedef struct KAttachProcessDebugEventInfo
{
  union KProcess *process;
} KAttachProcessDebugEventInfo;

/* 102 */
typedef struct KAttachThreadDebugEventInfo
{
  u32 creatorThreadId;
  void *threadLocalStorage;
  u32 *processEntrypoint;
} KAttachThreadDebugEventInfo;


/// Reasons for an exit thread event.
typedef enum {
  EXITTHREAD_EVENT_EXIT              = 0, ///< Thread exited.
  EXITTHREAD_EVENT_TERMINATE         = 1, ///< Thread terminated.
  EXITTHREAD_EVENT_EXIT_PROCESS      = 2, ///< Process exited either normally or due to an uncaught exception.
  EXITTHREAD_EVENT_TERMINATE_PROCESS = 3, ///< Process has been terminated by @ref svcTerminateProcess.
} ExitThreadEventReason;

/// Reasons for an exit process event.
typedef enum {
  EXITPROCESS_EVENT_EXIT                = 0, ///< Process exited either normally or due to an uncaught exception.
  EXITPROCESS_EVENT_TERMINATE           = 1, ///< Process has been terminated by @ref svcTerminateProcess.
  EXITPROCESS_EVENT_DEBUG_TERMINATE     = 2, ///< Process has been terminated by @ref svcTerminateDebugProcess.
} ExitProcessEventReason;

/* 103 */
typedef struct KExitThreadDebugEventInfo
{
  ExitThreadEventReason reason;
} KExitThreadDebugEventInfo;

/* 104 */
typedef struct KExitProcessDebugEventInfo
{
  ExitProcessEventReason reason;
} KExitProcessDebugEventInfo;

/// Stop point types
typedef enum StopPointType
{
    STOPPOINT_SVC_FF        = 0, ///< See @ref SVC_STOP_POINT.
    STOPPOINT_BREAKPOINT    = 1, ///< Breakpoint.
    STOPPOINT_WATCHPOINT    = 2, ///< Watchpoint.
} StopPointType;

/* 105 */
typedef struct KFaultExceptionDebugEventInfo
{
  u32 faultInfo;
  StopPointType stopPointType;
} KFaultExceptionDebugEventInfo;

/// Reasons for a user break.
typedef enum UserBreakType
{
    USERBREAK_PANIC         = 0, ///< Panic.
    USERBREAK_ASSERT        = 1, ///< Assertion failed.
    USERBREAK_USER          = 2, ///< User related.
    USERBREAK_LOAD_RO       = 3, ///< Load RO.
    USERBREAK_UNLOAD_RO     = 4, ///< Unload RO.
} UserBreakType;

/* 106 */
typedef struct KUserBreakExceptionDebugEventInfo
{
  UserBreakType reason;
  u32 croInfo, croInfoSize;
} KUserBreakExceptionDebugEventInfo;

/* 107 */
typedef struct KDebuggerBreakDebugEventInfo
{
  s32 threadIds[4];
} KDebuggerBreakDebugEventInfo;

/* 109 */
typedef struct KExceptionDebugEventInfo
{
  ExceptionEventType type;
  void *address;
  u32 category;
  union
  {
    KFaultExceptionDebugEventInfo fault;
    KUserBreakExceptionDebugEventInfo userBreak;
    KDebuggerBreakDebugEventInfo debuggerBreak;
  };
} KExceptionDebugEventInfo;

/* 110 */
typedef struct KScheduleDebugEventInfo
{
  u64 clockTick;
  u32 cpuId;
  u32 unk[5];
  u32 unkEventInfo;
  u32 padding;
} KScheduleDebugEventInfo;

/* 111 */
typedef struct KSyscallDebugEventInfo
{
  u64 clockTick;
  u32 svcId;
} KSyscallDebugEventInfo;

/* 112 */
typedef struct KOutputStringDebugEventInfo
{
  const char *str;
  u32 size;
} KOutputStringDebugEventInfo;

/* 113 */
typedef struct KMapDebugEventInfo
{
  void *address;
  u32 size;
  MemPerm permissions;
  MemState state;
} KMapDebugEventInfo;

/* 115 */
typedef struct KEventInfo
{
  DebugEventType type;
  u32 threadId;
  u32 flags;
  bool processIsBeingAttached;
  bool needsToBeContinued;
  bool other;
  bool isProcessed;
  union
  {
    KAttachProcessDebugEventInfo attachProcess;
    KAttachThreadDebugEventInfo attachThread;
    KExitThreadDebugEventInfo exitThread;
    KExitProcessDebugEventInfo exitProcess;
    KExceptionDebugEventInfo exception;
    KScheduleDebugEventInfo schedule;
    KSyscallDebugEventInfo syscall;
    KOutputStringDebugEventInfo outputString;
    KMapDebugEventInfo map;
  };
} KEventInfo;

typedef struct ALIGN(0x1000) KCoreObjectContext
{
  KThread *volatile currentThread;
  union KProcess *volatile currentProcess;
  KScheduler *volatile currentScheduler;
  KSchedulableInterruptEventLinkedList *volatile currentSchedulableInterruptEventLinkedList;
  KThread *volatile lastKThreadEncounteringExceptionPtr;
  u8 padding[4];
  KThread schedulerThread;
  KScheduler schedulerInstance;
  KSchedulableInterruptEventLinkedList schedulableInterruptEventLinkedListInstance;
  u8 padding2[2164];
  u32 unkStack[291];
} KCoreObjectContext;

typedef struct KCoreContext
{
  u32 gap0[1024];
  u32 L1Table_exceptionStack[4096];
  u32 gap1[1024];
  u32 schedulerThreadContext[1024];
  u32 gap2[1024];
  KCoreObjectContext objectContext;
} KCoreContext;

static KCoreContext * const currentCoreContext = (KCoreContext *)0xFFFF1000;
extern KCoreContext *coreCtxs;

#define DEFINE_CONSOLE_SPECIFIC_STRUCTS(console, nbCores)
/* 60 */
typedef struct ALIGN(4) KProcessHwInfoN3DS
{
  KObjectMutex mutex;
  u32 processTLBEntriesNeedToBeFlushedOnCore[4];
  KLinkedList ownedKMemoryBlocks;
  u32 unknown_3;
  u32 field_28;
  u32 translationTableBase;
  u8 contextId;
  bool globalTLBFlushRequired;
  bool isCurrentlyLoadedApp;
  u32 unknown_5;
  void *endOfUserlandVmem;
  void *linearVAUserlandBase;
  u32 unknown_6;
  u32 mmuTableSize;
  u32 *mmuTableVA;
} KProcessHwInfoN3DS;

typedef struct ALIGN(4) KProcessHwInfoO3DS8x
{
  KObjectMutex mutex;
  u32 processTLBEntriesNeedToBeFlushedOnCore[2];
  KLinkedList ownedKMemoryBlocks;
  u32 unknown_3;
  u32 field_28;
  u32 translationTableBase;
  u8 contextId;
  bool globalTLBFlushRequired;
  bool isCurrentlyLoadedApp;
  u32 unknown_5;
  void *endOfUserlandVmem;
  void *linearVAUserlandBase;
  u32 unknown_6;
  u32 mmuTableSize;
  u32 *mmuTableVA;
} KProcessHwInfoO3DS8x;

typedef struct ALIGN(4) KProcessHwInfoO3DSPre8x
{
  KObjectMutex mutex;
  u32 processTLBEntriesNeedToBeFlushedOnCore[2];
  KLinkedList ownedKMemoryBlocks;
  u32 unknown_3;
  u32 field_28;
  u32 translationTableBase;
  u8 contextId;
  bool globalTLBFlushRequired;
  bool isCurrentlyLoadedApp;
  u32 unknown_5;
  void *endOfUserlandVmem;
  u32 mmuTableSize;
  u32 *mmuTableVA;
} KProcessHwInfoO3DSPre8x;

#define INSTANCIATE_KPROCESS(sys) \
typedef struct KProcess##sys\
{\
  KSynchronizationObject syncObject;\
  KSendableInterruptEvent sendableInterruptEvent;\
  KProcessHwInfo##sys hwInfo;\
  u32 totalThreadContextSize;\
  KLinkedList threadLocalPages;\
  u32 unknown_7;\
  s32 idealProcessor;\
  KDebug *debug;\
  KResourceLimit *resourceLimits;\
  ProcessStatus status;\
  u8 affinityMask;\
  u16 padding;\
  s16 threadCount;\
  s16 maxThreadCount;\
  u32 svcAccessControlMask[4];\
  u32 interruptFlags[4];\
  u32 kernelFlags;\
  u16 handleTableSize;\
  u16 kernelReleaseVersion;\
  KCodeSet *codeSet;\
  u32 processId;\
  s64 creationTime;\
  KThread *mainThread;\
  u32 interruptEnabledFlags[4];\
  KProcessHandleTable handleTable;\
  u8 gap234[52];\
  u64 unused;\
} KProcess##sys;

INSTANCIATE_KPROCESS(N3DS);
INSTANCIATE_KPROCESS(O3DS8x);
INSTANCIATE_KPROCESS(O3DSPre8x);

typedef union KProcessHwInfo
{
    KProcessHwInfoN3DS N3DS;
    KProcessHwInfoO3DS8x O3DS8x;
    KProcessHwInfoO3DSPre8x O3DSPre8x;
} KProcessHwInfo;

typedef union KProcess
{
    KProcessN3DS N3DS;
    KProcessO3DS8x O3DS8x;
    KProcessO3DSPre8x O3DSPre8x;
} KProcess;

/* 54 */
typedef struct InterruptManagerN3DS
{
  InterruptData privateInterrupts[4][32];
  InterruptData publicEvents[96];
  KObjectMutex mutex;
} InterruptManagerN3DS;

typedef struct InterruptManagerO3DS
{
  InterruptData privateInterrupts[2][32];
  InterruptData publicEvents[96];
  KObjectMutex mutex;
} InterruptManagerO3DS;

typedef union InterruptManager
{
    InterruptManagerN3DS N3DS;
    InterruptManagerO3DS O3DS;
} InterruptManager;

typedef struct AddressRange
{
  void *begin;
  void *end;
} AddressRange;

typedef struct KAsyncCacheMaintenanceInterruptEvent
{
  KSchedulableInterruptEvent schedulableInterruptEvent;
  const KThread *handlingThread;
} KAsyncCacheMaintenanceInterruptEvent;

typedef struct KCacheMaintenanceInterruptEventN3DS
{
  KBaseInterruptEvent baseInterruptEvent;
  u8 cacheMaintenanceOperation;
  bool async;
  s8 remaining;
  KThread *operatingThread;
  AddressRange addrRange;
  KThreadLinkedListNode *threadListNode;
  KThreadLinkedList *threadList;
  KAsyncCacheMaintenanceInterruptEvent asyncInterruptEventsPerCore[4];
} KCacheMaintenanceInterruptEventN3DS;

typedef struct KCacheMaintenanceInterruptEventO3DS
{
  KBaseInterruptEvent baseInterruptEvent;
  u8 cacheMaintenanceOperation;
  bool async;
  s8 remaining;
  KThread *operatingThread;
  AddressRange addrRange;
  KThreadLinkedListNode *threadListNode;
  KThreadLinkedList *threadList;
  KAsyncCacheMaintenanceInterruptEvent asyncInterruptEventsPerCore[2];
} KCacheMaintenanceInterruptEventO3DS;

typedef union KCacheMaintenanceInterruptEvent
{
    KCacheMaintenanceInterruptEventN3DS N3DS;
    KCacheMaintenanceInterruptEventO3DS O3DS;
} KCacheMaintenanceInterruptEvent;

typedef struct FcramLayout
{
  void *applicationAddr;
  u32 applicationSize;
  void *systemAddr;
  u32 systemSize;
  void *baseAddr;
  u32 baseSize;
} FcramLayout;

extern bool isN3DS;
extern void *officialSVCs[0x7E];

#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(classname##O3DS8x, field) :\
offsetof(classname##O3DSPre8x, field)))

#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? &(obj)->O3DS8x.field :\
&(obj)->O3DSPre8x.field))

#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? (type *)(&(obj)->O3DS8x.field) :\
(type *)(&(obj)->O3DSPre8x.field)))

#define KPROCESS_OFFSETOF(field)                    KPROCESSRELATED_OFFSETOFF(KProcess, field)
#define KPROCESS_GET_PTR(obj, field)                KPROCESSRELATED_GET_PTR(obj, field)
#define KPROCESS_GET_PTR_TYPE(type, obj, field)     KPROCESSRELATED_GET_PTR_TYPE(type, obj, field)
#define KPROCESS_GET_RVALUE(obj, field)             *(KPROCESS_GET_PTR(obj, field))
#define KPROCESS_GET_RVALUE_TYPE(type, obj, field)  *(KPROCESS_GET_PTR_TYPE(type, obj, field))

#define KPROCESSHWINFO_OFFSETOF(field)                    KPROCESSRELATED_OFFSETOFF(KProcessHwInfo, field)
#define KPROCESSHWINFO_GET_PTR(obj, field)                KPROCESSRELATED_GET_PTR(obj, field)
#define KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field)     KPROCESSRELATED_GET_PTR_TYPE(type, obj, field)
#define KPROCESSHWINFO_GET_RVALUE(obj, field)             *(KPROCESSHWINFO_GET_PTR(obj, field))
#define KPROCESSHWINFO_GET_RVALUE_TYPE(type, obj, field)  *(KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field))

static inline u32 idOfProcess(KProcess *process)
{
    return KPROCESS_GET_RVALUE(process, processId);
}

static inline KProcessHwInfo *hwInfoOfProcess(KProcess *process)
{
    return KPROCESS_GET_PTR_TYPE(KProcessHwInfo, process, hwInfo);
}

static inline KCodeSet *codeSetOfProcess(KProcess *process)
{
    return KPROCESS_GET_RVALUE(process, codeSet);
}

static inline KProcessHandleTable *handleTableOfProcess(KProcess *process)
{
    return KPROCESS_GET_PTR(process, handleTable);
}

static inline KDebug *debugOfProcess(KProcess *process)
{
    return KPROCESS_GET_RVALUE(process, debug);
}

static inline const char *classNameOfAutoObject(KAutoObject *object)
{
    const char *name;
    if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
    {
        KClassToken tok;
        object->vtable->GetClassToken(&tok, object);
        name = tok.name;
    }
    else
        name = object->vtable->GetClassName(object);
    return name;
}

extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);

static inline Result createHandleForProcess(Handle *out, KProcess *process, KAutoObject *obj)
{
    u8 token;
    if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
    {
        KClassToken tok;
        obj->vtable->GetClassToken(&tok, obj);
        token = tok.flags;
    }
    else
        token = (u8)(u32)(obj->vtable->GetClassName(obj));

    KProcessHandleTable *handleTable = handleTableOfProcess(process);
    return KProcessHandleTable__CreateHandle(handleTable, out, obj, token);
}

static inline Result createHandleForThisProcess(Handle *out, KAutoObject *obj)
{
    return createHandleForProcess(out, currentCoreContext->objectContext.currentProcess, obj);
}
