Merge branch 'dynos' into unstable

This commit is contained in:
MysterD 2022-03-10 18:17:59 -08:00
commit e81808c314
40 changed files with 7255 additions and 11 deletions

View file

@ -543,6 +543,9 @@ DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(
# Segment elf files
SEG_FILES := $(SEGMENT_ELF_FILES) $(ACTOR_ELF_FILES) $(LEVEL_ELF_FILES)
# Dynos
include dynos.mk
##################### Compiler Options #######################
INCLUDE_CFLAGS := -I include -I $(BUILD_DIR) -I $(BUILD_DIR)/include -I src -I . $(EXTRA_INCLUDES)
ENDIAN_BITWIDTH := $(BUILD_DIR)/endian-and-bitwidth

15
data/dynos.c.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef DYNOS_C_H
#define DYNOS_C_H
#ifndef __cplusplus
#include "dynos.h"
void *dynos_update_cmd (void *cmd);
void dynos_update_gfx ();
void dynos_update_opt (void *pad);
s32 dynos_gfx_import_texture (void **output, void *ptr, s32 tile, void *grapi, void **hashmap, void *pool, s32 *poolpos, s32 poolsize);
void dynos_gfx_swap_animations(void *ptr);
#endif
#endif

702
data/dynos.cpp.h Normal file
View file

@ -0,0 +1,702 @@
#ifndef DYNOS_CPP_H
#define DYNOS_CPP_H
#ifdef __cplusplus
#include "dynos.h"
extern "C" {
#include "engine/math_util.h"
}
#define FUNCTION_CODE (u32) 0x434E5546
#define POINTER_CODE (u32) 0x52544E50
//
// Enums
//
enum {
DATA_TYPE_NONE = 0,
DATA_TYPE_LIGHT,
DATA_TYPE_TEXTURE,
DATA_TYPE_VERTEX,
DATA_TYPE_DISPLAY_LIST,
DATA_TYPE_GEO_LAYOUT,
DATA_TYPE_ANIMATION_VALUE,
DATA_TYPE_ANIMATION_INDEX,
DATA_TYPE_ANIMATION,
DATA_TYPE_ANIMATION_TABLE,
DATA_TYPE_GFXDYNCMD,
DATA_TYPE_UNUSED,
};
enum {
DOPT_NONE = 0,
DOPT_TOGGLE,
DOPT_CHOICE,
DOPT_SCROLL,
DOPT_BIND,
DOPT_BUTTON,
DOPT_SUBMENU,
// These ones are used by the Warp to Level built-in submenu
DOPT_CHOICELEVEL,
DOPT_CHOICEAREA,
DOPT_CHOICESTAR,
DOPT_CHOICEPARAM,
};
//
// DynOS Array
// A vector-like array, implemented to be processed really fast, but cannot handle C++ complex classes like std::string
//
template <typename T>
class Array {
public:
inline Array() : mBuffer(NULL), mCount(0), mCapacity(0) {
}
inline Array(const std::initializer_list<T> &aList) : mBuffer(NULL), mCount(0), mCapacity(0) {
Resize(aList.size());
memcpy(mBuffer, aList.begin(), mCount * sizeof(T));
}
inline Array(const T *aBegin, const T *aEnd) : mBuffer(NULL), mCount(0), mCapacity(0) {
Resize(aEnd - aBegin);
memcpy(mBuffer, aBegin, mCount * sizeof(T));
}
inline Array(const Array &aOther) : mBuffer(NULL), mCount(0), mCapacity(0) {
Resize(aOther.mCount);
memcpy(mBuffer, aOther.mBuffer, mCount * sizeof(T));
}
inline void operator=(const Array &aOther) {
Resize(aOther.mCount);
memcpy(mBuffer, aOther.mBuffer, mCount * sizeof(T));
}
inline ~Array() {
Clear();
}
public:
void Resize(s32 aCount) {
if (aCount > mCapacity) {
mCapacity = MAX(aCount, MAX(16, mCapacity * 2));
T *_Buffer = (T *) calloc(mCapacity, sizeof(T));
if (mBuffer) {
memcpy(_Buffer, mBuffer, mCount * sizeof(T));
free(mBuffer);
}
mBuffer = _Buffer;
}
mCount = aCount;
}
void Add(const T& aItem) {
Resize(mCount + 1);
mBuffer[mCount - 1] = aItem;
}
void Remove(s32 aIndex) {
memmove(mBuffer + aIndex, mBuffer + aIndex + 1, (mCount - aIndex - 1) * sizeof(T));
mCount--;
}
void Pop() {
mCount--;
}
void RemoveAll() {
mCount = 0;
}
void Clear() {
if (mBuffer) free(mBuffer);
mBuffer = NULL;
mCount = 0;
mCapacity = 0;
}
s32 Find(const T& aItem) const {
for (s32 i = 0; i != mCount; ++i) {
if (mBuffer[i] == aItem) {
return i;
}
}
return -1;
}
template <typename Predicate>
s32 FindIf(Predicate aPredicate) const {
for (s32 i = 0; i != mCount; ++i) {
if (aPredicate(mBuffer[i])) {
return i;
}
}
return -1;
}
public:
inline const T *begin() const { return mBuffer; }
inline const T *end() const { return mBuffer + mCount; }
inline T *begin() { return mBuffer; }
inline T *end() { return mBuffer + mCount; }
inline const T &operator[](s32 aIndex) const { return mBuffer[aIndex]; }
inline T &operator[](s32 aIndex) { return mBuffer[aIndex]; }
inline s32 Count() const { return mCount; }
inline bool Empty() const { return mCount == 0; }
public:
void Read(FILE *aFile) {
s32 _Length = 0; fread(&_Length, sizeof(s32), 1, aFile);
Resize(_Length);
fread(mBuffer, sizeof(T), _Length, aFile);
}
void Write(FILE *aFile) const {
fwrite(&mCount, sizeof(s32), 1, aFile);
fwrite(mBuffer, sizeof(T), mCount, aFile);
}
private:
T *mBuffer;
s32 mCount;
s32 mCapacity;
};
//
// DynOS String
// A fixed-size string that doesn't require heap memory allocation
//
#define STRING_SIZE 127
class String {
public:
inline String() : mCount(0) {
mBuffer[0] = 0;
}
inline String(const char *aString) : mCount(0) {
if (aString) {
u64 _Length = strlen(aString);
mCount = MIN(_Length, STRING_SIZE - 1);
memcpy(mBuffer, aString, _Length);
}
mBuffer[mCount] = 0;
}
template <typename... Args>
inline String(const char *aFmt, Args... aArgs) : mCount(0) {
snprintf(mBuffer, STRING_SIZE, aFmt, aArgs...);
mCount = (u8) strlen(mBuffer);
mBuffer[mCount] = 0;
}
inline String(const String &aOther) : mCount(0) {
mCount = aOther.mCount;
memcpy(mBuffer, aOther.mBuffer, mCount);
mBuffer[mCount] = 0;
}
inline void operator=(const String &aOther) {
mCount = aOther.mCount;
memcpy(mBuffer, aOther.mBuffer, mCount);
mBuffer[mCount] = 0;
}
public:
void Add(char aChar) {
if (mCount == STRING_SIZE - 1) return;
mBuffer[mCount++] = aChar;
mBuffer[mCount] = 0;
}
void Remove(s32 aIndex) {
memmove(mBuffer + aIndex, mBuffer + aIndex + 1, (mCount-- - aIndex - 1));
mBuffer[mCount] = 0;
}
void RemoveAll() {
mCount = 0;
mBuffer[0] = 0;
}
void Clear() {
mCount = 0;
mBuffer[0] = 0;
}
s32 Find(char aChar, s32 aStart = 0) const {
for (u8 i = (u8) aStart; i < mCount; ++i) {
if (mBuffer[i] == aChar) {
return (s32) i;
}
}
return -1;
}
s32 Find(const char *aString, s32 aStart = 0) const {
const char *_Ptr = strstr(mBuffer + aStart, aString);
if (_Ptr) return (s32) (_Ptr - mBuffer);
return -1;
}
s32 FindLast(char aChar) const {
for (u8 i = mCount; i != 0; --i) {
if (mBuffer[i - 1] == aChar) {
return (s32) (i - 1);
}
}
return -1;
}
String SubString(s32 aStart, s32 aCount = STRING_SIZE - 1) const {
if (aStart >= mCount) return String();
if (aCount < 0) aCount = STRING_SIZE - 1;
aCount = MIN(aCount, mCount - aStart);
String _String;
_String.mCount = aCount;
memcpy(_String.mBuffer, mBuffer + aStart, aCount);
_String.mBuffer[aCount] = 0;
return _String;
}
public:
inline const char *begin() const { return mBuffer; }
inline const char *end() const { return mBuffer + mCount; }
inline char *begin() { return mBuffer; }
inline char *end() { return mBuffer + mCount; }
inline const char &operator[](s32 aIndex) const { return mBuffer[aIndex]; }
inline char &operator[](s32 aIndex) { return mBuffer[aIndex]; }
inline s32 Length() const { return (s32) mCount; }
inline bool Empty() const { return mCount == 0; }
public:
bool operator==(const char *aString) const {
if (strlen(aString) != mCount) return false;
for (u8 i = 0; i != mCount; ++i) {
if (aString[i] != mBuffer[i]) {
return false;
}
}
return true;
}
bool operator==(const String &aOther) const {
if (aOther.mCount != mCount) return false;
for (u8 i = 0; i != mCount; ++i) {
if (aOther.mBuffer[i] != mBuffer[i]) {
return false;
}
}
return true;
}
bool operator!=(const char *aString) const {
if (strlen(aString) != mCount) return true;
for (u8 i = 0; i != mCount; ++i) {
if (aString[i] != mBuffer[i]) {
return true;
}
}
return false;
}
bool operator!=(const String &aOther) const {
if (aOther.mCount != mCount) return true;
for (u8 i = 0; i != mCount; ++i) {
if (aOther.mBuffer[i] != mBuffer[i]) {
return true;
}
}
return false;
}
public:
void Read(FILE *aFile) {
fread(&mCount, sizeof(u8), 1, aFile);
fread(mBuffer, sizeof(char), mCount, aFile);
mBuffer[mCount] = 0;
}
void Write(FILE *aFile) const {
fwrite(&mCount, sizeof(u8), 1, aFile);
fwrite(mBuffer, sizeof(char), mCount, aFile);
}
s32 ParseInt() const {
s32 i = 0;
if (mBuffer[1] == 'x') {
sscanf(mBuffer + 2, "%x", &i);
} else {
sscanf(mBuffer, "%d", &i);
}
return i;
}
f32 ParseFloat() const {
f32 f = 0.f;
sscanf(mBuffer, "%f", &f);
return f;
}
private:
char mBuffer[STRING_SIZE];
u8 mCount;
};
static_assert(sizeof(String) == (STRING_SIZE + 1), "sizeof(String) must be (STRING_SIZE + 1)");
//
// Types
//
template <typename U, typename V>
using Pair = std::pair<U, V>;
typedef std::string SysPath;
class NoCopy {
protected:
NoCopy() {}
~NoCopy() {}
private:
NoCopy(const NoCopy &) = delete;
void operator=(const NoCopy &) = delete;
};
struct TexData : NoCopy {
Array<u8> mPngData;
Array<u8> mRawData;
s32 mRawWidth = -1;
s32 mRawHeight = -1;
s32 mRawFormat = -1;
s32 mRawSize = -1;
bool mUploaded = false;
};
struct AnimData : NoCopy {
s16 mFlags = 0;
s16 mUnk02 = 0;
s16 mUnk04 = 0;
s16 mUnk06 = 0;
s16 mUnk08 = 0;
Pair<String, s16> mUnk0A;
Pair<String, Array<s16>> mValues;
Pair<String, Array<u16>> mIndex;
u32 mLength = 0;
};
template <typename T>
struct DataNode : NoCopy {
String mName;
T* mData = NULL;
u32 mSize = 0;
Array<String> mTokens;
u64 mModelIdentifier = 0;
u64 mLoadIndex = 0;
};
template <typename T>
using DataNodes = Array<DataNode<T>*>;
struct GfxContext {
DataNode<TexData>* mCurrentTexture = NULL;
DataNode<TexData>* mCurrentPalette = NULL;
};
template <typename T>
using AnimBuffer = Pair<String, Array<T>>;
struct GfxData : NoCopy {
// Model data
DataNodes<Lights1> mLights;
DataNodes<TexData> mTextures;
DataNodes<Vtx> mVertices;
DataNodes<Gfx> mDisplayLists;
DataNodes<GeoLayout> mGeoLayouts;
// Animation data
Array<AnimBuffer<s16> *> mAnimValues;
Array<AnimBuffer<u16> *> mAnimIndices;
DataNodes<AnimData> mAnimations;
Array<Pair<String, void *>> mAnimationTable;
// Current
u64 mLoadIndex = 0;
s32 mErrorCount = 0;
u32 mModelIdentifier = 0;
SysPath mPackFolder;
Array<void *> mPointerList;
GfxContext mGfxContext;
Array<GfxContext> mGeoNodeStack;
};
struct ActorGfx {
GfxData *mGfxData = NULL;
GraphNode *mGraphNode = NULL;
s32 mPackIndex = 0;
};
struct PackData {
SysPath mPath;
};
typedef Pair<String, const u8 *> Label;
struct DynosOption : NoCopy {
String mName;
String mConfigName; // Name used in the config file
Label mLabel;
Label mTitle; // Full caps label, displayed with colored font
DynosOption *mPrev;
DynosOption *mNext;
DynosOption *mParent;
bool mDynos; // true from create, false from convert
u8 mType;
// TOGGLE
struct Toggle : NoCopy {
bool *mTog;
} mToggle;
// CHOICE
struct Choice : NoCopy {
Array<Label> mChoices;
s32 *mIndex;
} mChoice;
// SCROLL
struct Scroll : NoCopy {
s32 mMin;
s32 mMax;
s32 mStep;
s32 *mValue;
} mScroll;
// BIND
struct Bind : NoCopy {
u32 mMask;
u32 *mBinds;
s32 mIndex;
} mBind;
// BUTTON
struct Button : NoCopy {
String mFuncName;
} mButton;
// SUBMENU
struct Submenu : NoCopy {
DynosOption *mChild;
bool mEmpty;
} mSubMenu;
};
typedef bool (*DynosLoopFunc)(DynosOption *, void *);
//
// Utils
//
template <typename T>
T* New(u64 aCount = 1llu) {
T *_Ptr = (T *) calloc(aCount, sizeof(T));
for (u64 i = 0; i != aCount; ++i) {
new (_Ptr + i) T(); // Calls the constructor of type T at address (_Ptr + i)
}
return _Ptr;
}
template <typename T>
void Delete(T *& aPtr) {
if (aPtr) {
aPtr->~T();
free(aPtr);
}
aPtr = NULL;
}
template <typename T>
T *CopyBytes(const T *aPtr, u64 aSize) {
T *_Ptr = (T *) calloc(1, aSize);
memcpy(_Ptr, aPtr, aSize);
return _Ptr;
}
template <typename T = void>
Array<String> Split(const char *aBuffer, const String &aDelimiters, const String &aEndCharacters = {}, bool aHandleDoubleQuotedStrings = false) {
Array<String> _Tokens;
String _Token;
bool _TreatSpaceAsChar = false;
u64 _Length = strlen(aBuffer);
for (u64 i = 0; i <= _Length; ++i) {
if (i == _Length || aDelimiters.Find(aBuffer[i]) != -1) {
if (aBuffer[i] == ' ' && _TreatSpaceAsChar) {
_Token.Add(aBuffer[i]);
} else {
if (!_Token.Empty()) {
_Tokens.Add(_Token);
_Token.Clear();
}
if (aEndCharacters.Find(aBuffer[i]) != -1) {
break;
}
}
} else {
if (aBuffer[i] == '\"' && aHandleDoubleQuotedStrings) {
_TreatSpaceAsChar = !_TreatSpaceAsChar;
} else {
_Token.Add(aBuffer[i]);
}
}
}
return _Tokens;
}
template <typename T>
T ReadBytes(FILE* aFile) {
T _Item = { 0 };
fread(&_Item, sizeof(T), 1, aFile);
return _Item;
}
template <typename T>
void WriteBytes(FILE* aFile, const T& aItem) {
fwrite(&aItem, sizeof(T), 1, aFile);
}
template <typename... Args>
void PrintNoNewLine(const char *aFmt, Args... aArgs) {
printf(aFmt, aArgs...);
fflush(stdout);
}
template <typename... Args>
void Print(const char *aFmt, Args... aArgs) {
printf(aFmt, aArgs...);
printf("\r\n");
fflush(stdout);
}
#define PrintError(...) { \
if (aGfxData->mErrorCount == 0) Print(" ERROR!"); \
Print(__VA_ARGS__); \
aGfxData->mErrorCount++; \
}
template <typename... Args>
SysPath fstring(const char *aFmt, Args... aArgs) {
char buffer[1024];
snprintf(buffer, 1024, aFmt, aArgs...);
return SysPath(buffer);
}
// Wraps the static into a function, to call the constructor on first use
#define STATIC_STORAGE(type, name) \
static type &__##name() { \
static type s##name; \
return s##name; \
}
//
// Main
//
void DynOS_UpdateOpt(void *aPad);
void *DynOS_UpdateCmd(void *aCmd);
void DynOS_UpdateGfx();
bool DynOS_IsTransitionActive();
void DynOS_ReturnToMainMenu();
//
// Opt
//
s32 DynOS_Opt_GetValue(const String &aName);
void DynOS_Opt_SetValue(const String &aName, s32 aValue);
void DynOS_Opt_AddAction(const String &aFuncName, bool (*aFuncPtr)(const char *), bool aOverwrite);
void DynOS_Opt_Init();
void DynOS_Opt_InitVanilla(DynosOption *&aOptionsMenu);
void DynOS_Opt_Update(OSContPad *aPad);
bool DynOS_Opt_ControllerUpdate(DynosOption *aOpt, void *aData);
s32 DynOS_Opt_ControllerGetKeyPressed();
void DynOS_Opt_LoadConfig(DynosOption *aMenu);
void DynOS_Opt_SaveConfig(DynosOption *aMenu);
void DynOS_Opt_DrawMenu(DynosOption *aCurrentOption, DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu);
void DynOS_Opt_DrawPrompt(DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu);
//
// Gfx
//
u8 *DynOS_Gfx_TextureConvertToRGBA32(const u8 *aData, u64 aLength, s32 aFormat, s32 aSize, const u8 *aPalette);
bool DynOS_Gfx_ImportTexture(void **aOutput, void *aPtr, s32 aTile, void *aGfxRApi, void **aHashMap, void *aPool, u32 *aPoolPos, u32 aPoolSize);
Array<ActorGfx> &DynOS_Gfx_GetActorList();
Array<PackData *> &DynOS_Gfx_GetPacks();
#ifdef COOP
Array<bool> &DynOS_Gfx_GetPacksEnabled();
#endif
Array<String> DynOS_Gfx_Init();
void DynOS_Gfx_Update();
void DynOS_Gfx_SwapAnimations(void *aPtr);
bool DynOS_Gfx_WriteBinary(const SysPath &aOutputFilename, GfxData *aGfxData);
GfxData *DynOS_Gfx_LoadFromBinary(const SysPath &aPackFolder, const char *aActorName);
void DynOS_Gfx_Free(GfxData *aGfxData);
void DynOS_Gfx_GeneratePack(const SysPath &aPackFolder);
//
// String
//
u8 *DynOS_String_Convert(const char *aString, bool aHeapAlloc);
u8 *DynOS_String_Decapitalize(u8 *aStr64);
s32 DynOS_String_Length(const u8 *aStr64);
s32 DynOS_String_WidthChar64(u8 aChar64);
s32 DynOS_String_Width(const u8 *aStr64);
//
// Geo
//
s32 DynOS_Geo_GetActorCount();
const char *DynOS_Geo_GetActorName(s32 aIndex);
const void *DynOS_Geo_GetActorLayout(s32 aIndex);
s32 DynOS_Geo_GetActorIndex(const void *aGeoLayout);
void *DynOS_Geo_GetFunctionPointerFromName(const String &aName);
void *DynOS_Geo_GetFunctionPointerFromIndex(s32 aIndex);
s32 DynOS_Geo_GetFunctionIndex(const void *aPtr);
void *DynOS_Geo_GetGraphNode(const void *aGeoLayout, bool aKeepInMemory);
//
// Levels
//
s32 DynOS_Level_GetCount();
const s32 *DynOS_Level_GetList();
s32 DynOS_Level_GetCourse(s32 aLevel);
const void *DynOS_Level_GetScript(s32 aLevel);
const u8 *DynOS_Level_GetName(s32 aLevel, bool aDecaps, bool aAddCourseNumber);
const u8 *DynOS_Level_GetActName(s32 aLevel, s32 aAct, bool aDecaps, bool aAddStarNumber);
const u8 *DynOS_Level_GetAreaName(s32 aLevel, s32 aArea, bool aDecaps);
s16 *DynOS_Level_GetWarp(s32 aLevel, s32 aArea, u8 aWarpId);
s16 *DynOS_Level_GetWarpEntry(s32 aLevel, s32 aArea);
s16 *DynOS_Level_GetWarpDeath(s32 aLevel, s32 aArea);
//
// Warps
//
void *DynOS_Warp_Update(void *aCmd, bool aIsLevelInitDone);
bool DynOS_Warp_ToLevel(s32 aLevel, s32 aArea, s32 aAct);
bool DynOS_Warp_RestartLevel();
bool DynOS_Warp_ExitLevel(s32 aDelay);
bool DynOS_Warp_ToCastle(s32 aLevel);
void DynOS_Warp_SetParam(s32 aLevel, s32 aIndex);
const char *DynOS_Warp_GetParamName(s32 aLevel, s32 aIndex);
#endif
#endif

38
data/dynos.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef DYNOS_H
#define DYNOS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <limits.h>
#include <dirent.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
#include <new>
#include <utility>
#include <string>
extern "C" {
#endif
#include "config.h"
#include "pc/fs/fs.h"
#include "audio_defines.h"
#undef STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#ifdef __cplusplus
}
#endif
#define DYNOS_VERSION "1.0"
#define DYNOS_EXE_FOLDER sys_exe_path()
#define DYNOS_USER_FOLDER sys_user_path()
#define DYNOS_RES_FOLDER "dynos"
#define DYNOS_PACKS_FOLDER DYNOS_RES_FOLDER "/packs"
#define DYNOS_CONFIG_FILENAME "DynOS." DYNOS_VERSION ".config.txt"
#define DYNOS_AT_STARTUP __attribute__((constructor))
#define DYNOS_AT_EXIT __attribute__((destructor))
#endif

24
data/dynos_c.cpp Normal file
View file

@ -0,0 +1,24 @@
#include "dynos.cpp.h"
extern "C" {
void *dynos_update_cmd(void *cmd) {
return DynOS_UpdateCmd(cmd);
}
void dynos_update_gfx() {
return DynOS_UpdateGfx();
}
void dynos_update_opt(void *pad) {
return DynOS_UpdateOpt(pad);
}
s32 dynos_gfx_import_texture(void **output, void *ptr, s32 tile, void *grapi, void **hashmap, void *pool, s32 *poolpos, s32 poolsize) {
return DynOS_Gfx_ImportTexture(output, ptr, tile, grapi, hashmap, pool, (u32 *) poolpos, (u32) poolsize);
}
void dynos_gfx_swap_animations(void *ptr) {
return DynOS_Gfx_SwapAnimations(ptr);
}
}

15
data/dynos_coop.c.h Normal file
View file

@ -0,0 +1,15 @@
#ifdef COOP
#ifndef DYNOS_COOP_C_H
#define DYNOS_COOP_C_H
#ifndef __cplusplus
bool dynos_warp_to_level(s32 aLevel, s32 aArea, s32 aAct);
int dynos_packs_get_count(void);
const char* dynos_packs_get(s32 index);
bool dynos_packs_get_enabled(s32 index);
void dynos_packs_set_enabled(s32 index, bool value);
#endif
#endif
#endif

32
data/dynos_coop_c.cpp Normal file
View file

@ -0,0 +1,32 @@
#ifdef COOP
#include "dynos.cpp.h"
extern "C" {
bool dynos_warp_to_level(s32 aLevel, s32 aArea, s32 aAct) {
return DynOS_Warp_ToLevel(aLevel, aArea, aAct);
}
// -- dynos packs -- //
#define DYNOS_PACK_PATH_SPLIT_LEN 12
int dynos_packs_get_count(void) {
return DynOS_Gfx_GetPacks().Count();
}
const char* dynos_packs_get(s32 index) {
std::string path = DynOS_Gfx_GetPacks()[index]->mPath;
return path.substr(path.find(DYNOS_PACKS_FOLDER) + DYNOS_PACK_PATH_SPLIT_LEN).c_str();
}
bool dynos_packs_get_enabled(s32 index) {
return DynOS_Gfx_GetPacksEnabled()[index];
}
void dynos_packs_set_enabled(s32 index, bool value) {
DynOS_Gfx_GetPacksEnabled()[index] = value;
}
}
#endif

70
data/dynos_gfx_init.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "dynos.cpp.h"
Array<ActorGfx> &DynOS_Gfx_GetActorList() {
static Array<ActorGfx> sActorGfxList;
return sActorGfxList;
}
Array<PackData *> &DynOS_Gfx_GetPacks() {
static Array<PackData *> sPacks;
return sPacks;
}
#ifdef COOP
Array<bool> &DynOS_Gfx_GetPacksEnabled() {
static Array<bool> sPacksEnabled;
return sPacksEnabled;
}
#endif
Array<String> DynOS_Gfx_Init() {
// Alloc and init the actors gfx list
Array<ActorGfx> &pActorGfxList = DynOS_Gfx_GetActorList();
pActorGfxList.Resize(DynOS_Geo_GetActorCount());
for (s32 i = 0; i != DynOS_Geo_GetActorCount(); ++i) {
pActorGfxList[i].mPackIndex = -1;
pActorGfxList[i].mGfxData = NULL;
pActorGfxList[i].mGraphNode = (GraphNode *) DynOS_Geo_GetGraphNode(DynOS_Geo_GetActorLayout(i), false);
}
// Scan the DynOS packs folder
Array<PackData *> &pDynosPacks = DynOS_Gfx_GetPacks();
SysPath _DynosPacksFolder = fstring("%s/%s", DYNOS_EXE_FOLDER, DYNOS_PACKS_FOLDER);
DIR *_DynosPacksDir = opendir(_DynosPacksFolder.c_str());
if (_DynosPacksDir) {
struct dirent *_DynosPacksEnt = NULL;
while ((_DynosPacksEnt = readdir(_DynosPacksDir)) != NULL) {
// Skip . and ..
if (SysPath(_DynosPacksEnt->d_name) == ".") continue;
if (SysPath(_DynosPacksEnt->d_name) == "..") continue;
// If pack folder exists, add it to the pack list
SysPath _PackFolder = fstring("%s/%s", _DynosPacksFolder.c_str(), _DynosPacksEnt->d_name);
if (fs_sys_dir_exists(_PackFolder.c_str())) {
PackData *_Pack = New<PackData>();
// Scan folder for subfolders to convert into .bin files
_Pack->mPath = _PackFolder;
DynOS_Gfx_GeneratePack(_PackFolder);
// Add pack to pack list
pDynosPacks.Add(_Pack);
}
}
closedir(_DynosPacksDir);
}
// Return a list of pack names
Array<String> _PackNames;
for (const auto& _Pack : pDynosPacks) {
u64 _DirSep1 = _Pack->mPath.find_last_of('\\');
u64 _DirSep2 = _Pack->mPath.find_last_of('/');
if (_DirSep1++ == SysPath::npos) _DirSep1 = 0;
if (_DirSep2++ == SysPath::npos) _DirSep2 = 0;
SysPath _DirName = _Pack->mPath.substr(MAX(_DirSep1, _DirSep2));
_PackNames.Add(_DirName.c_str());
}
return _PackNames;
}

313
data/dynos_gfx_load.cpp Normal file
View file

@ -0,0 +1,313 @@
#include "dynos.cpp.h"
//
// Pointers
//
static void *GetPointerFromData(GfxData *aGfxData, const String &aPtrName, u32 aPtrData) {
// Lights
for (auto& _Node : aGfxData->mLights) {
if (_Node->mName == aPtrName) {
if (aPtrData == 1) {
return (void *) &_Node->mData->l[0];
}
if (aPtrData == 2) {
return (void *) &_Node->mData->a;
}
sys_fatal("Unknown Light type: %u", aPtrData);
}
}
// Textures
for (auto& _Node : aGfxData->mTextures) {
if (_Node->mName == aPtrName) {
return (void *) _Node;
}
}
// Display lists
for (auto &_Node : aGfxData->mDisplayLists) {
if (_Node->mName == aPtrName) {
return (void *) _Node->mData;
}
}
// Geo layouts
for (auto &_Node : aGfxData->mGeoLayouts) {
if (_Node->mName == aPtrName) {
return (void *) _Node->mData;
}
}
// Vertices
for (auto &_Node : aGfxData->mVertices) {
if (_Node->mName == aPtrName) {
return (void *) (_Node->mData + aPtrData);
}
}
// Error
sys_fatal("Pointer not found: %s", aPtrName.begin());
return NULL;
}
static void *ReadPointer(FILE *aFile, GfxData *aGfxData, u32 aValue) {
// FUNC
if (aValue == FUNCTION_CODE) {
s32 _GeoFunctionIndex = ReadBytes<s32>(aFile);
return DynOS_Geo_GetFunctionPointerFromIndex(_GeoFunctionIndex);
}
// PNTR
if (aValue == POINTER_CODE) {
String _PtrName; _PtrName.Read(aFile);
u32 _PtrData = ReadBytes<u32>(aFile);
return GetPointerFromData(aGfxData, _PtrName, _PtrData);
}
// Not a pointer
return NULL;
}
//
// Read binary
//
static void LoadLightData(FILE *aFile, GfxData *aGfxData) {
DataNode<Lights1> *_Node = New<DataNode<Lights1>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mData = New<Lights1>();
*_Node->mData = ReadBytes<Lights1>(aFile);
// Append
aGfxData->mLights.Add(_Node);
}
static void LoadTextureData(FILE *aFile, GfxData *aGfxData) {
DataNode<TexData> *_Node = New<DataNode<TexData>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mData = New<TexData>();
_Node->mData->mUploaded = false;
_Node->mData->mPngData.Read(aFile);
if (!_Node->mData->mPngData.Empty()) {
u8 *_RawData = stbi_load_from_memory(_Node->mData->mPngData.begin(), _Node->mData->mPngData.Count(), &_Node->mData->mRawWidth, &_Node->mData->mRawHeight, NULL, 4);
_Node->mData->mRawFormat = G_IM_FMT_RGBA;
_Node->mData->mRawSize = G_IM_SIZ_32b;
_Node->mData->mRawData = Array<u8>(_RawData, _RawData + (_Node->mData->mRawWidth * _Node->mData->mRawHeight * 4));
free(_RawData);
} else { // Probably a palette
_Node->mData->mRawData = Array<u8>();
_Node->mData->mRawWidth = 0;
_Node->mData->mRawHeight = 0;
_Node->mData->mRawFormat = 0;
_Node->mData->mRawSize = 0;
}
// Append
aGfxData->mTextures.Add(_Node);
}
static void LoadVertexData(FILE *aFile, GfxData *aGfxData) {
DataNode<Vtx> *_Node = New<DataNode<Vtx>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mSize = ReadBytes<u32>(aFile);
_Node->mData = New<Vtx>(_Node->mSize);
for (u32 i = 0; i != _Node->mSize; ++i) {
_Node->mData[i].n.ob[0] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[1] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[2] = ReadBytes<s16>(aFile);
_Node->mData[i].n.flag = ReadBytes<s16>(aFile);
_Node->mData[i].n.tc[0] = ReadBytes<s16>(aFile);
_Node->mData[i].n.tc[1] = ReadBytes<s16>(aFile);
_Node->mData[i].n.n[0] = ReadBytes<s8> (aFile);
_Node->mData[i].n.n[1] = ReadBytes<s8> (aFile);
_Node->mData[i].n.n[2] = ReadBytes<s8> (aFile);
_Node->mData[i].n.a = ReadBytes<u8>(aFile);
}
// Append
aGfxData->mVertices.Add(_Node);
}
static void LoadDisplayListData(FILE *aFile, GfxData *aGfxData) {
DataNode<Gfx> *_Node = New<DataNode<Gfx>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mSize = ReadBytes<u32>(aFile);
_Node->mData = New<Gfx>(_Node->mSize);
for (u32 i = 0; i != _Node->mSize; ++i) {
u32 _WordsW0 = ReadBytes<u32>(aFile);
u32 _WordsW1 = ReadBytes<u32>(aFile);
void *_Ptr = ReadPointer(aFile, aGfxData, _WordsW1);
if (_Ptr) {
_Node->mData[i].words.w0 = (uintptr_t) _WordsW0;
_Node->mData[i].words.w1 = (uintptr_t) _Ptr;
} else {
_Node->mData[i].words.w0 = (uintptr_t) _WordsW0;
_Node->mData[i].words.w1 = (uintptr_t) _WordsW1;
}
}
// Append
aGfxData->mDisplayLists.Add(_Node);
}
static void LoadGeoLayoutData(FILE *aFile, GfxData *aGfxData) {
DataNode<GeoLayout> *_Node = New<DataNode<GeoLayout>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mSize = ReadBytes<u32>(aFile);
_Node->mData = New<GeoLayout>(_Node->mSize);
for (u32 i = 0; i != _Node->mSize; ++i) {
u32 _Value = ReadBytes<u32>(aFile);
void *_Ptr = ReadPointer(aFile, aGfxData, _Value);
if (_Ptr) {
_Node->mData[i] = (uintptr_t) _Ptr;
} else {
_Node->mData[i] = (uintptr_t) _Value;
}
}
// Append
aGfxData->mGeoLayouts.Add(_Node);
}
// For retro-compatibility
static void LoadGfxDynCmd(FILE *aFile, GfxData *aGfxData) {
Gfx *_Data = NULL;
String _DisplayListName; _DisplayListName.Read(aFile);
for (auto& _DisplayList : aGfxData->mDisplayLists) {
if (_DisplayList->mName == _DisplayListName) {
_Data = _DisplayList->mData;
break;
}
}
if (!_Data) {
sys_fatal("Display list not found: %s", _DisplayListName.begin());
}
ReadBytes<u32>(aFile);
ReadBytes<u8>(aFile);
}
static void LoadAnimationData(FILE *aFile, GfxData *aGfxData) {
DataNode<AnimData> *_Node = New<DataNode<AnimData>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mData = New<AnimData>();
_Node->mData->mFlags = ReadBytes<s16>(aFile);
_Node->mData->mUnk02 = ReadBytes<s16>(aFile);
_Node->mData->mUnk04 = ReadBytes<s16>(aFile);
_Node->mData->mUnk06 = ReadBytes<s16>(aFile);
_Node->mData->mUnk08 = ReadBytes<s16>(aFile);
_Node->mData->mUnk0A.second = ReadBytes<s16>(aFile);
_Node->mData->mLength = ReadBytes<u32>(aFile);
_Node->mData->mValues.second.Read(aFile);
_Node->mData->mIndex.second.Read(aFile);
// Append
aGfxData->mAnimations.Add(_Node);
}
static void LoadAnimationTable(FILE *aFile, GfxData *aGfxData) {
void *_AnimationPtr = NULL;
// Data
String _AnimationName; _AnimationName.Read(aFile);
if (_AnimationName != "NULL") {
for (auto &_AnimData : aGfxData->mAnimations) {
if (_AnimData->mName == _AnimationName) {
_AnimationPtr = (void *) _AnimData->mData;
break;
}
}
if (!_AnimationPtr) {
sys_fatal("Animation not found: %s", _AnimationName.begin());
}
}
// Append
aGfxData->mAnimationTable.Add({ "", _AnimationPtr });
}
//
// Load from binary
//
GfxData *DynOS_Gfx_LoadFromBinary(const SysPath &aPackFolder, const char *aActorName) {
struct DynosGfxDataCache { SysPath mPackFolder; Array<Pair<const char *, GfxData *>> mGfxData; };
static Array<DynosGfxDataCache *> sDynosGfxDataCache;
// Look for pack in cache
DynosGfxDataCache *_Pack = NULL;
for (s32 i = 0; i != sDynosGfxDataCache.Count(); ++i) {
if (sDynosGfxDataCache[i]->mPackFolder == aPackFolder) {
_Pack = sDynosGfxDataCache[i];
break;
}
}
// Look for actor in pack
if (_Pack) {
for (s32 i = 0; i != _Pack->mGfxData.Count(); ++i) {
if (_Pack->mGfxData[i].first == aActorName) { // Perfectly valid, aActorName comes from static RO data, so its address never changes during execution
return _Pack->mGfxData[i].second;
}
}
}
// Load data from binary file
GfxData *_GfxData = NULL;
SysPath _Filename = fstring("%s/%s.bin", aPackFolder.begin(), aActorName);
FILE *_File = fopen(_Filename.c_str(), "rb");
if (_File) {
_GfxData = New<GfxData>();
for (bool _Done = false; !_Done;) {
switch (ReadBytes<u8>(_File)) {
case DATA_TYPE_LIGHT: LoadLightData (_File, _GfxData); break;
case DATA_TYPE_TEXTURE: LoadTextureData (_File, _GfxData); break;
case DATA_TYPE_VERTEX: LoadVertexData (_File, _GfxData); break;
case DATA_TYPE_DISPLAY_LIST: LoadDisplayListData(_File, _GfxData); break;
case DATA_TYPE_GEO_LAYOUT: LoadGeoLayoutData (_File, _GfxData); break;
case DATA_TYPE_ANIMATION: LoadAnimationData (_File, _GfxData); break;
case DATA_TYPE_ANIMATION_TABLE: LoadAnimationTable (_File, _GfxData); break;
case DATA_TYPE_GFXDYNCMD: LoadGfxDynCmd (_File, _GfxData); break;
default: _Done = true; break;
}
}
fclose(_File);
}
// Add data to cache, even if not loaded
if (_Pack) {
_Pack->mGfxData.Add({ aActorName, _GfxData });
} else {
_Pack = New<DynosGfxDataCache>();
_Pack->mPackFolder = aPackFolder;
_Pack->mGfxData.Add({ aActorName, _GfxData });
sDynosGfxDataCache.Add(_Pack);
}
return _GfxData;
}

1887
data/dynos_gfx_read.cpp Normal file

File diff suppressed because it is too large Load diff

285
data/dynos_gfx_texture.cpp Normal file
View file

@ -0,0 +1,285 @@
#include "dynos.cpp.h"
extern "C" {
#include "pc/gfx/gfx_rendering_api.h"
}
//
// Conversion
//
#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F)
#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF)
#define SCALE_4_8(VAL_) ((VAL_) * 0x11)
#define SCALE_8_4(VAL_) ((VAL_) / 0x11)
#define SCALE_3_8(VAL_) ((VAL_) * 0x24)
#define SCALE_8_3(VAL_) ((VAL_) / 0x24)
static u8 *RGBA16_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 2);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; i += 2) {
u16 _Col = (aData[i + 0] << 8) | aData[i + 1];
u8 _Red = (_Col >> 11) & 0x1F;
u8 _Grn = (_Col >> 6) & 0x1F;
u8 _Blu = (_Col >> 1) & 0x1F;
u8 _Alp = (_Col >> 0) & 0x01;
*(pBuffer++) = (SCALE_5_8(_Red));
*(pBuffer++) = (SCALE_5_8(_Grn));
*(pBuffer++) = (SCALE_5_8(_Blu));
*(pBuffer++) = (0xFF * (_Alp));
}
return _Buffer;
}
static u8 *RGBA32_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 1);
memcpy(_Buffer, aData, aLength);
return _Buffer;
}
static u8 *IA4_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 8);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
u8 _Half0 = (aData[i] >> 4) & 0xF;
*(pBuffer++) = (SCALE_3_8(_Half0 >> 1));
*(pBuffer++) = (SCALE_3_8(_Half0 >> 1));
*(pBuffer++) = (SCALE_3_8(_Half0 >> 1));
*(pBuffer++) = (0xFF * (_Half0 & 1));
u8 _Half1 = (aData[i] >> 0) & 0xF;
*(pBuffer++) = (SCALE_3_8(_Half1 >> 1));
*(pBuffer++) = (SCALE_3_8(_Half1 >> 1));
*(pBuffer++) = (SCALE_3_8(_Half1 >> 1));
*(pBuffer++) = (0xFF * (_Half1 & 1));
}
return _Buffer;
}
static u8 *IA8_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 4);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
u8 _Col = (aData[i] >> 4) & 0xF;
u8 _Alp = (aData[i] >> 0) & 0xF;
*(pBuffer++) = (SCALE_4_8(_Col));
*(pBuffer++) = (SCALE_4_8(_Col));
*(pBuffer++) = (SCALE_4_8(_Col));
*(pBuffer++) = (SCALE_4_8(_Alp));
}
return _Buffer;
}
static u8 *IA16_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 2);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; i += 2) {
u8 _Col = aData[i + 0];
u8 _Alp = aData[i + 1];
*(pBuffer++) = (_Col);
*(pBuffer++) = (_Col);
*(pBuffer++) = (_Col);
*(pBuffer++) = (_Alp);
}
return _Buffer;
}
static u8 *CI4_RGBA32(const u8 *aData, u64 aLength, const u8 *aPalette) {
u8 *_Buffer = New<u8>(aLength * 8);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
u8 _Idx0 = (aData[i] >> 4) & 0xF;
u16 _Col0 = (aPalette[_Idx0 * 2 + 0] << 8) | aPalette[_Idx0 * 2 + 1];
u8 _Red0 = (_Col0 >> 11) & 0x1F;
u8 _Grn0 = (_Col0 >> 6) & 0x1F;
u8 _Blu0 = (_Col0 >> 1) & 0x1F;
u8 _Alp0 = (_Col0 >> 0) & 0x01;
*(pBuffer++) = (SCALE_5_8(_Red0));
*(pBuffer++) = (SCALE_5_8(_Grn0));
*(pBuffer++) = (SCALE_5_8(_Blu0));
*(pBuffer++) = (0xFF * (_Alp0));
u8 _Idx1 = (aData[i] >> 0) & 0xF;
u16 _Col1 = (aPalette[_Idx1 * 2 + 0] << 8) | aPalette[_Idx1 * 2 + 1];
u8 _Red1 = (_Col1 >> 11) & 0x1F;
u8 _Grn1 = (_Col1 >> 6) & 0x1F;
u8 _Blu1 = (_Col1 >> 1) & 0x1F;
u8 _Alp1 = (_Col1 >> 0) & 0x01;
*(pBuffer++) = (SCALE_5_8(_Red1));
*(pBuffer++) = (SCALE_5_8(_Grn1));
*(pBuffer++) = (SCALE_5_8(_Blu1));
*(pBuffer++) = (0xFF * (_Alp1));
}
return _Buffer;
}
static u8 *CI8_RGBA32(const u8 *aData, u64 aLength, const u8 *aPalette) {
u8 *_Buffer = New<u8>(aLength * 4);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
u8 _Idx = aData[i];
u16 _Col = (aPalette[_Idx * 2 + 0] << 8) | aPalette[_Idx * 2 + 1];
u8 _Red = (_Col >> 11) & 0x1F;
u8 _Grn = (_Col >> 6) & 0x1F;
u8 _Blu = (_Col >> 1) & 0x1F;
u8 _Alp = (_Col >> 0) & 0x01;
*(pBuffer++) = (SCALE_5_8(_Red));
*(pBuffer++) = (SCALE_5_8(_Grn));
*(pBuffer++) = (SCALE_5_8(_Blu));
*(pBuffer++) = (0xFF * (_Alp));
}
return _Buffer;
}
static u8 *I4_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 8);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
u8 _Half0 = (aData[i] >> 4) & 0xF;
*(pBuffer++) = (SCALE_4_8(_Half0));
*(pBuffer++) = (SCALE_4_8(_Half0));
*(pBuffer++) = (SCALE_4_8(_Half0));
*(pBuffer++) = (255);
u8 _Half1 = (aData[i] >> 0) & 0xF;
*(pBuffer++) = (SCALE_4_8(_Half1));
*(pBuffer++) = (SCALE_4_8(_Half1));
*(pBuffer++) = (SCALE_4_8(_Half1));
*(pBuffer++) = (255);
}
return _Buffer;
}
static u8 *I8_RGBA32(const u8 *aData, u64 aLength) {
u8 *_Buffer = New<u8>(aLength * 4);
u8 *pBuffer = _Buffer;
for (u64 i = 0; i != aLength; ++i) {
*(pBuffer++) = (aData[i]);
*(pBuffer++) = (aData[i]);
*(pBuffer++) = (aData[i]);
*(pBuffer++) = (255);
}
return _Buffer;
}
u8 *DynOS_Gfx_TextureConvertToRGBA32(const u8 *aData, u64 aLength, s32 aFormat, s32 aSize, const u8 *aPalette) {
switch ((aFormat << 8) | aSize ) {
case ((G_IM_FMT_RGBA << 8) | G_IM_SIZ_16b): return RGBA16_RGBA32(aData, aLength);
case ((G_IM_FMT_RGBA << 8) | G_IM_SIZ_32b): return RGBA32_RGBA32(aData, aLength);
case ((G_IM_FMT_IA << 8) | G_IM_SIZ_4b ): return IA4_RGBA32 (aData, aLength);
case ((G_IM_FMT_IA << 8) | G_IM_SIZ_8b ): return IA8_RGBA32 (aData, aLength);
case ((G_IM_FMT_IA << 8) | G_IM_SIZ_16b): return IA16_RGBA32 (aData, aLength);
case ((G_IM_FMT_CI << 8) | G_IM_SIZ_4b ): return CI4_RGBA32 (aData, aLength, aPalette);
case ((G_IM_FMT_CI << 8) | G_IM_SIZ_8b ): return CI8_RGBA32 (aData, aLength, aPalette);
case ((G_IM_FMT_I << 8) | G_IM_SIZ_4b ): return I4_RGBA32 (aData, aLength);
case ((G_IM_FMT_I << 8) | G_IM_SIZ_8b ): return I8_RGBA32 (aData, aLength);
}
return NULL;
}
//
// Upload
//
typedef struct GfxRenderingAPI GRAPI;
static void DynOS_Gfx_UploadTexture(DataNode<TexData> *aNode, GRAPI *aGfxRApi, s32 aTile, s32 aTexId) {
aGfxRApi->select_texture(aTile, aTexId);
aGfxRApi->upload_texture(aNode->mData->mRawData.begin(), aNode->mData->mRawWidth, aNode->mData->mRawHeight);
aNode->mData->mUploaded = true;
}
//
// Cache
//
struct THN {
struct THN *mNext;
const void *mAddr; // Contains the pointer to the DataNode<TexData> struct, NOT the actual texture data
u8 mFmt, mSiz;
s32 mTexId;
u8 mCms, mCmt;
bool mLInf;
};
static bool DynOS_Gfx_CacheTexture(THN **aOutput, DataNode<TexData> *aNode, s32 aTile, GRAPI *aGfxRApi, THN **aHashMap, THN *aPool, u32 *aPoolPos, u32 aPoolSize) {
// Find texture in cache
uintptr_t _Hash = ((uintptr_t) aNode) & ((aPoolSize * 2) - 1);
THN **_Node = &(aHashMap[_Hash]);
while ((*_Node) != NULL && ((*_Node) - aPool) < (*aPoolPos)) {
if ((*_Node)->mAddr == (const void *) aNode) {
aGfxRApi->select_texture(aTile, (*_Node)->mTexId);
if (!aNode->mData->mUploaded) {
DynOS_Gfx_UploadTexture(aNode, aGfxRApi, aTile, (*_Node)->mTexId);
}
(*aOutput) = (*_Node);
return true;
}
_Node = &(*_Node)->mNext;
}
// If cache is full, clear cache
if ((*aPoolPos) == aPoolSize) {
(*aPoolPos) = 0;
_Node = &aHashMap[_Hash];
}
// Add new texture to cache
(*_Node) = &aPool[(*aPoolPos)++];
if (!(*_Node)->mAddr) {
(*_Node)->mTexId = aGfxRApi->new_texture();
}
aGfxRApi->select_texture(aTile, (*_Node)->mTexId);
aGfxRApi->set_sampler_parameters(aTile, false, 0, 0);
(*_Node)->mCms = 0;
(*_Node)->mCmt = 0;
(*_Node)->mLInf = false;
(*_Node)->mNext = NULL;
(*_Node)->mAddr = aNode;
(*_Node)->mFmt = G_IM_FMT_RGBA;
(*_Node)->mSiz = G_IM_SIZ_32b;
(*aOutput) = (*_Node);
return false;
}
//
// Import
//
static DataNode<TexData> *DynOS_Gfx_RetrieveNode(void *aPtr) {
Array<ActorGfx> &pActorGfxList = DynOS_Gfx_GetActorList();
for (auto& _ActorGfx : pActorGfxList) {
if (_ActorGfx.mGfxData) {
for (auto &_Node : _ActorGfx.mGfxData->mTextures) {
if ((void*) _Node == aPtr) {
return _Node;
}
}
}
}
return NULL;
}
static bool DynOS_Gfx_ImportTexture_Typed(THN **aOutput, void *aPtr, s32 aTile, GRAPI *aGfxRApi, THN **aHashMap, THN *aPool, u32 *aPoolPos, u32 aPoolSize) {
DataNode<TexData> *_Node = DynOS_Gfx_RetrieveNode(aPtr);
if (_Node) {
if (!DynOS_Gfx_CacheTexture(aOutput, _Node, aTile, aGfxRApi, aHashMap, aPool, aPoolPos, aPoolSize)) {
DynOS_Gfx_UploadTexture(_Node, aGfxRApi, aTile, (*aOutput)->mTexId);
}
return true;
}
return false;
}
bool DynOS_Gfx_ImportTexture(void **aOutput, void *aPtr, s32 aTile, void *aGfxRApi, void **aHashMap, void *aPool, u32 *aPoolPos, u32 aPoolSize) {
return DynOS_Gfx_ImportTexture_Typed(
(THN **) aOutput,
(void *) aPtr,
(s32) aTile,
(GRAPI *) aGfxRApi,
(THN **) aHashMap,
(THN *) aPool,
(u32 *) aPoolPos,
(u32) aPoolSize
);
}

165
data/dynos_gfx_update.cpp Normal file
View file

@ -0,0 +1,165 @@
#include "dynos.cpp.h"
extern "C" {
#include "object_fields.h"
#include "game/level_update.h"
#include "game/object_list_processor.h"
}
//
// Update animations
//
// Retrieve the current Mario's animation index
static s32 RetrieveCurrentMarioAnimationIndex() {
struct MarioAnimDmaRelatedThing *_AnimDmaTable = gMarioState->animation->animDmaTable;
for (s32 i = 0; i != (s32) _AnimDmaTable->count; ++i) {
void *_AnimAddr = _AnimDmaTable->srcAddr + _AnimDmaTable->anim[i].offset;
if (_AnimAddr == gMarioState->animation->currentAnimAddr) {
return i;
}
}
return -1;
}
// Retrieve the current animation index
// As we don't know the length of the table, let's hope that we'll always find the animation...
static s32 RetrieveCurrentAnimationIndex(struct Object *aObject) {
if (!aObject->oAnimations || !aObject->header.gfx.animInfo.curAnim) {
return -1;
}
for (s32 i = 0; aObject->oAnimations[i] != NULL; ++i) {
if (aObject->oAnimations[i] == aObject->header.gfx.animInfo.curAnim) {
return i;
}
}
return -1;
}
// Must be called twice, before and after geo_set_animation_globals
void DynOS_Gfx_SwapAnimations(void *aPtr) {
static Animation *pDefaultAnimation = NULL;
static Animation sGfxDataAnimation;
// Does the object has a model?
struct Object *_Object = (struct Object *) aPtr;
if (!_Object->header.gfx.sharedChild) {
return;
}
// Swap the current animation with the one from the Gfx data
if (!pDefaultAnimation) {
pDefaultAnimation = _Object->header.gfx.animInfo.curAnim;
// Actor index
s32 _ActorIndex = DynOS_Geo_GetActorIndex(_Object->header.gfx.sharedChild->georef);
if (_ActorIndex == -1) {
return;
}
// Gfx data
GfxData *_GfxData = DynOS_Gfx_GetActorList()[_ActorIndex].mGfxData;
if (!_GfxData) {
return;
}
// Animation table
if (_GfxData->mAnimationTable.Empty()) {
return;
}
// Animation index
s32 _AnimIndex = (_Object == gMarioObject ? RetrieveCurrentMarioAnimationIndex() : RetrieveCurrentAnimationIndex(_Object));
if (_AnimIndex == -1) {
return;
}
// Animation data
const AnimData *_AnimData = (const AnimData *) _GfxData->mAnimationTable[_AnimIndex].second;
if (_AnimData) {
sGfxDataAnimation.flags = _AnimData->mFlags;
sGfxDataAnimation.animYTransDivisor = _AnimData->mUnk02;
sGfxDataAnimation.startFrame = _AnimData->mUnk04;
sGfxDataAnimation.loopStart = _AnimData->mUnk06;
sGfxDataAnimation.loopEnd = _AnimData->mUnk08;
sGfxDataAnimation.unusedBoneCount = _AnimData->mUnk0A.second;
sGfxDataAnimation.values = _AnimData->mValues.second.begin();
sGfxDataAnimation.index = _AnimData->mIndex.second.begin();
sGfxDataAnimation.length = _AnimData->mLength;
_Object->header.gfx.animInfo.curAnim = &sGfxDataAnimation;
}
// Restore the default animation
} else {
_Object->header.gfx.animInfo.curAnim = pDefaultAnimation;
pDefaultAnimation = NULL;
}
}
//
// Update models
//
void DynOS_Gfx_Update() {
if (gObjectLists) {
// Check packs
#ifdef COOP
Array<bool> &_Enabled = DynOS_Gfx_GetPacksEnabled();
const Array<PackData *> &pDynosPacks = DynOS_Gfx_GetPacks();
while (_Enabled.Count() < pDynosPacks.Count()) {
_Enabled.Add(true);
}
#else
Array<bool> _Enabled;
const Array<PackData *> &pDynosPacks = DynOS_Gfx_GetPacks();
for (s32 i = 0; i != pDynosPacks.Count(); ++i) {
_Enabled.Add(DynOS_Opt_GetValue(String("dynos_pack_%d", i)));
}
#endif
// Loop through all object lists
for (s32 list : { OBJ_LIST_PLAYER, OBJ_LIST_DESTRUCTIVE, OBJ_LIST_GENACTOR, OBJ_LIST_PUSHABLE, OBJ_LIST_LEVEL, OBJ_LIST_DEFAULT, OBJ_LIST_SURFACE, OBJ_LIST_POLELIKE, OBJ_LIST_UNIMPORTANT }) {
struct Object *_Head = (struct Object *) &gObjectLists[list];
for (struct Object *_Object = (struct Object *) _Head->header.next; _Object != _Head; _Object = (struct Object *) _Object->header.next) {
if (_Object->header.gfx.sharedChild) {
// Actor index
s32 _ActorIndex = DynOS_Geo_GetActorIndex(_Object->header.gfx.sharedChild->georef);
if (_ActorIndex != -1) {
// Replace the object's model and animations
ActorGfx *_ActorGfx = &DynOS_Gfx_GetActorList()[_ActorIndex];
for (s32 i = 0; i != pDynosPacks.Count(); ++i) {
// If enabled and no pack is selected
// load the pack's model and replace the default actor's model
if (_Enabled[i] && _ActorGfx->mPackIndex == -1) {
// Load Gfx data from binary
GfxData *_GfxData = DynOS_Gfx_LoadFromBinary(pDynosPacks[i]->mPath, DynOS_Geo_GetActorName(_ActorIndex));
if (_GfxData) {
_ActorGfx->mPackIndex = i;
_ActorGfx->mGfxData = _GfxData;
_ActorGfx->mGraphNode = (GraphNode *) DynOS_Geo_GetGraphNode((*(_GfxData->mGeoLayouts.end() - 1))->mData, true);
_ActorGfx->mGraphNode->georef = DynOS_Geo_GetActorLayout(_ActorIndex);
break;
}
}
// If disabled and this pack is the one selected
// replace the actor's model by the default one
else if (!_Enabled[i] && _ActorGfx->mPackIndex == i) {
_ActorGfx->mPackIndex = -1;
_ActorGfx->mGfxData = NULL;
_ActorGfx->mGraphNode = (GraphNode *) DynOS_Geo_GetGraphNode(DynOS_Geo_GetActorLayout(_ActorIndex), true);
}
}
// Update object
_Object->header.gfx.sharedChild = _ActorGfx->mGraphNode;
}
}
}
}
}
}

318
data/dynos_gfx_write.cpp Normal file
View file

@ -0,0 +1,318 @@
#include "dynos.cpp.h"
//
// Pointers
//
typedef Pair<String, u32> PointerData;
static PointerData GetDataFromPointer(const void* aPtr, GfxData* aGfxData) {
// Lights
for (auto& _Node : aGfxData->mLights) {
if (&_Node->mData->l[0] == aPtr) { // Light *, not Lights1 *
return { _Node->mName, 1 };
}
if (&_Node->mData->a == aPtr) { // Ambient *, not Lights1 *
return { _Node->mName, 2 };
}
}
// Textures
for (auto& _Node : aGfxData->mTextures) {
if (_Node == aPtr) {
return { _Node->mName, 0 };
}
}
// Display lists
for (auto& _Node : aGfxData->mDisplayLists) {
if (_Node == aPtr) {
return { _Node->mName, 0 };
}
}
// Geo layouts
for (auto& _Node : aGfxData->mGeoLayouts) {
if (_Node->mData == aPtr) {
return { _Node->mName, 0 };
}
}
// Vertices
String _VtxArrayName = "";
uintptr_t _VtxArrayStart = 0;
for (auto& _Node : aGfxData->mVertices) {
if (_Node->mData == aPtr) {
return { _Node->mName, 0 };
}
if ((uintptr_t)_Node->mData <= (uintptr_t)aPtr &&
(uintptr_t)_Node->mData >= _VtxArrayStart) {
_VtxArrayName = _Node->mName;
_VtxArrayStart = (uintptr_t)_Node->mData;
}
}
return { _VtxArrayName, (u32)((const Vtx*)aPtr - (const Vtx*)_VtxArrayStart) };
}
static void WritePointer(FILE* aFile, const void* aPtr, GfxData* aGfxData) {
// NULL
if (!aPtr) {
WriteBytes<u32>(aFile, 0);
return;
}
// Geo function
s32 _GeoFunctionIndex = DynOS_Geo_GetFunctionIndex(aPtr);
if (_GeoFunctionIndex != -1) {
WriteBytes<u32>(aFile, FUNCTION_CODE);
WriteBytes<s32>(aFile, _GeoFunctionIndex);
return;
}
// Pointer
PointerData _PtrData = GetDataFromPointer(aPtr, aGfxData);
WriteBytes<u32>(aFile, POINTER_CODE);
_PtrData.first.Write(aFile);
WriteBytes<u32>(aFile, _PtrData.second);
}
//
// Lights
//
static void WriteLightData(FILE* aFile, GfxData* aGfxData, DataNode<Lights1> *aNode) {
if (!aNode->mData) return;
// Header
WriteBytes<u8>(aFile, DATA_TYPE_LIGHT);
aNode->mName.Write(aFile);
// Data
WriteBytes<Lights1>(aFile, *aNode->mData);
}
//
// Textures
//
static void WriteTextureData(FILE* aFile, GfxData* aGfxData, DataNode<TexData> *aNode) {
if (!aNode->mData) return;
// Header
WriteBytes<u8>(aFile, DATA_TYPE_TEXTURE);
aNode->mName.Write(aFile);
// Data
aNode->mData->mPngData.Write(aFile);
}
//
// Vertices
//
static void WriteVertexData(FILE* aFile, GfxData* aGfxData, DataNode<Vtx> *aNode) {
if (!aNode->mData) return;
// Header
WriteBytes<u8>(aFile, DATA_TYPE_VERTEX);
aNode->mName.Write(aFile);
// Data
WriteBytes<u32>(aFile, aNode->mSize);
for (u32 i = 0; i != aNode->mSize; ++i) {
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[0]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[1]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[2]);
WriteBytes<s16>(aFile, aNode->mData[i].n.flag);
WriteBytes<s16>(aFile, aNode->mData[i].n.tc[0]);
WriteBytes<s16>(aFile, aNode->mData[i].n.tc[1]);
WriteBytes<s8> (aFile, aNode->mData[i].n.n[0]);
WriteBytes<s8> (aFile, aNode->mData[i].n.n[1]);
WriteBytes<s8> (aFile, aNode->mData[i].n.n[2]);
WriteBytes<u8> (aFile, aNode->mData[i].n.a);
}
}
//
// Display lists
//
static void WriteDisplayListData(FILE *aFile, GfxData *aGfxData, DataNode<Gfx> *aNode) {
if (!aNode->mData) return;
// Header
WriteBytes<u8>(aFile, DATA_TYPE_DISPLAY_LIST);
aNode->mName.Write(aFile);
// Data
WriteBytes<u32>(aFile, aNode->mSize);
for (u32 i = 0; i != aNode->mSize; ++i) {
Gfx *_Head = &aNode->mData[i];
if (aGfxData->mPointerList.Find((void *) _Head) != -1) {
WriteBytes<u32>(aFile, _Head->words.w0);
WritePointer(aFile, (const void *) _Head->words.w1, aGfxData);
} else {
WriteBytes<u32>(aFile, _Head->words.w0);
WriteBytes<u32>(aFile, _Head->words.w1);
}
}
}
//
// Geo layouts
//
static void WriteGeoLayoutData(FILE *aFile, GfxData *aGfxData, DataNode<GeoLayout> *aNode) {
if (!aNode->mData) return;
// Header
WriteBytes<u8>(aFile, DATA_TYPE_GEO_LAYOUT);
aNode->mName.Write(aFile);
// Data
WriteBytes<u32>(aFile, aNode->mSize);
for (u32 i = 0; i != aNode->mSize; ++i) {
GeoLayout *_Head = &aNode->mData[i];
if (aGfxData->mPointerList.Find((void *) _Head) != -1) {
WritePointer(aFile, (const void *) (*_Head), aGfxData);
} else {
WriteBytes<u32>(aFile, *((u32 *) _Head));
}
}
}
//
// Animation data
//
static void WriteAnimationData(FILE* aFile, GfxData* aGfxData) {
for (auto& _Node : aGfxData->mAnimations) {
// Value buffer
s32 _ValueBufferIdx = aGfxData->mAnimValues.FindIf([&_Node](const AnimBuffer<s16> *aAnimBuffer) { return aAnimBuffer->first == _Node->mData->mValues.first; });
if (_ValueBufferIdx == -1) {
continue;
}
// Index buffer
s32 _IndexBufferIdx = aGfxData->mAnimIndices.FindIf([&_Node](const AnimBuffer<u16> *aAnimBuffer) { return aAnimBuffer->first == _Node->mData->mIndex.first; });
if (_IndexBufferIdx == -1) {
continue;
}
// Unk0A buffer
s32 _Unk0ABufferIdx = aGfxData->mAnimIndices.FindIf([&_Node](const AnimBuffer<u16> *aAnimBuffer) { return aAnimBuffer->first == _Node->mData->mUnk0A.first; });
if (_Unk0ABufferIdx == -1) {
continue;
}
// Header
WriteBytes<u8>(aFile, DATA_TYPE_ANIMATION);
_Node->mName.Write(aFile);
// Data
WriteBytes<s16>(aFile, _Node->mData->mFlags);
WriteBytes<s16>(aFile, _Node->mData->mUnk02);
WriteBytes<s16>(aFile, _Node->mData->mUnk04);
WriteBytes<s16>(aFile, _Node->mData->mUnk06);
WriteBytes<s16>(aFile, _Node->mData->mUnk08);
WriteBytes<s16>(aFile, (aGfxData->mAnimIndices[_Unk0ABufferIdx]->second.Count() / 6) - 1);
WriteBytes<u32>(aFile, _Node->mData->mLength);
aGfxData->mAnimValues[_ValueBufferIdx]->second.Write(aFile);
aGfxData->mAnimIndices[_IndexBufferIdx]->second.Write(aFile);
}
}
//
// Animation table
//
static void WriteAnimationTable(FILE* aFile, GfxData* aGfxData) {
for (auto& _AnimName : aGfxData->mAnimationTable) {
// Header
WriteBytes<u8>(aFile, DATA_TYPE_ANIMATION_TABLE);
// Data
_AnimName.first.Write(aFile);
}
}
//
// Write
//
bool DynOS_Gfx_WriteBinary(const SysPath &aOutputFilename, GfxData *aGfxData) {
FILE *_File = fopen(aOutputFilename.c_str(), "wb");
if (!_File) {
PrintError(" ERROR: Unable to create file \"%s\"", aOutputFilename.c_str());
return false;
}
for (u64 i = 0; i != aGfxData->mLoadIndex; ++i) {
for (auto &_Node : aGfxData->mLights) {
if (_Node->mLoadIndex == i) {
WriteLightData(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mTextures) {
if (_Node->mLoadIndex == i) {
WriteTextureData(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mVertices) {
if (_Node->mLoadIndex == i) {
WriteVertexData(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mDisplayLists) {
if (_Node->mLoadIndex == i) {
WriteDisplayListData(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mGeoLayouts) {
if (_Node->mLoadIndex == i) {
WriteGeoLayoutData(_File, aGfxData, _Node);
}
}
}
WriteAnimationData(_File, aGfxData);
WriteAnimationTable(_File, aGfxData);
fclose(_File);
return true;
}
//
// Free
//
void DynOS_Gfx_Free(GfxData* aGfxData) {
if (aGfxData) {
for (auto& _Node : aGfxData->mLights) {
Delete(_Node->mData);
Delete(_Node);
}
for (auto& _Node : aGfxData->mTextures) {
Delete(_Node->mData);
Delete(_Node);
}
for (auto& _Node : aGfxData->mVertices) {
Delete(_Node->mData);
Delete(_Node);
}
for (auto& _Node : aGfxData->mDisplayLists) {
Delete(_Node->mData);
Delete(_Node);
}
for (auto& _Node : aGfxData->mGeoLayouts) {
Delete(_Node->mData);
Delete(_Node);
}
for (auto& _Node : aGfxData->mAnimations) {
Delete(_Node->mData);
Delete(_Node);
}
Delete(aGfxData);
}
}

825
data/dynos_level.cpp Normal file
View file

@ -0,0 +1,825 @@
#include "dynos.cpp.h"
extern "C" {
#include "game/segment2.h"
#include "game/save_file.h"
#include "levels/scripts.h"
}
//
// Const
//
extern "C" {
extern const BehaviorScript *sWarpBhvSpawnTable[];
}
#define DYNOS_LEVEL_TEXT_EMPTY ""
#define DYNOS_LEVEL_TEXT_CASTLE "CASTLE"
#define DYNOS_LEVEL_TEXT_BOWSER_1 "BOWSER 1"
#define DYNOS_LEVEL_TEXT_BOWSER_2 "BOWSER 2"
#define DYNOS_LEVEL_TEXT_BOWSER_3 "BOWSER 3"
#define DYNOS_LEVEL_TEXT_100_COINS_STAR "100 COINS STAR"
#define DYNOS_LEVEL_TEXT_RED_COINS_STAR "RED COINS STAR"
#define DYNOS_LEVEL_TEXT_ONE_SECRET_STAR "ONE OF THE CASTLE'S SECRET STARS!"
static void SetConvertedTextToBuffer(u8 *aBuffer, const char *aText) {
u8 *_ConvertedText = DynOS_String_Convert(aText, false);
memcpy(aBuffer, _ConvertedText, DynOS_String_Length(_ConvertedText) + 1);
}
//
// Data
//
struct DynosWarp {
/* 0 */ s16 mArea = 0;
/* 1 */ s16 mId = 0;
/* 2 */ s16 mType = -1;
/* 3 */ s16 mPosX = 0;
/* 4 */ s16 mPosY = 0;
/* 5 */ s16 mPosZ = 0;
/* 6 */ s16 mAngle = 0;
/* 7 */ s16 mDestLevel = 0;
/* 8 */ s16 mDestArea = 0;
/* 9 */ s16 mDestId = 0;
};
static void *sDynosLevelScripts[LEVEL_COUNT] = { NULL };
static Array<DynosWarp> sDynosLevelWarps[LEVEL_COUNT] = { Array<DynosWarp>() };
static Array<s32> sDynosLevelList = Array<s32>(); // Ordered by Course Id, COURSE_NONE excluded
static u64 DynOS_Level_CmdGet(void *aCmd, u64 aOffset) {
u64 _Offset = (((aOffset) & 3llu) | (((aOffset) & ~3llu) << (sizeof(void *) >> 3llu)));
return *((u64 *) (u64(aCmd) + _Offset));
}
static void *DynOS_Level_CmdNext(void *aCmd, u64 aCmdSize) {
u64 _Offset = (((aCmdSize) & 3llu) | (((aCmdSize) & ~3llu) << (sizeof(void *) >> 3llu)));
return (void *) (u64(aCmd) + _Offset);
}
static void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *));
//
// Init
//
static s32 DynOS_Level_PreprocessMasterScript(u8 aType, void *aCmd) {
static bool sDynosScriptExecLevelTable = false;
static s32 sDynosLevelNum = -1;
if (!sDynosScriptExecLevelTable) {
// JUMP_LINK
if (aType == 0x06) {
sDynosScriptExecLevelTable = true;
return 0;
}
} else {
// JUMP_IF
if (aType == 0x0C) {
sDynosLevelNum = (s32) DynOS_Level_CmdGet(aCmd, 0x04);
return 0;
}
// EXECUTE
if (aType == 0x00) {
void *_Script = (void *) DynOS_Level_CmdGet(aCmd, 0x0C);
if (sDynosLevelNum >= 0 && sDynosLevelNum < LEVEL_COUNT && !sDynosLevelScripts[sDynosLevelNum]) {
sDynosLevelScripts[sDynosLevelNum] = _Script;
}
sDynosLevelNum = -1;
return 2;
}
// EXIT
if (aType == 0x02) {
return 3;
}
// SLEEP
if (aType == 0x03) {
return 3;
}
}
return 0;
}
static s32 sDynosCurrentLevelNum;
static s32 DynOS_Level_PreprocessScript(u8 aType, void *aCmd) {
static u8 sDynosAreaIndex = 0;
static auto _GetWarpStruct = [](u8 aArea, u8 aId) -> DynosWarp * {
for (s32 i = 0; i != sDynosLevelWarps[sDynosCurrentLevelNum].Count(); ++i) {
if (sDynosLevelWarps[sDynosCurrentLevelNum][i].mArea == aArea &&
sDynosLevelWarps[sDynosCurrentLevelNum][i].mId == aId) {
return &sDynosLevelWarps[sDynosCurrentLevelNum][i];
}
}
DynosWarp _Warp;
_Warp.mArea = aArea;
_Warp.mId = aId;
sDynosLevelWarps[sDynosCurrentLevelNum].Add(_Warp);
return &sDynosLevelWarps[sDynosCurrentLevelNum][sDynosLevelWarps[sDynosCurrentLevelNum].Count() - 1];
};
// AREA
if (aType == 0x1F) {
sDynosAreaIndex = (u8) DynOS_Level_CmdGet(aCmd, 2);
}
// OBJECT
else if (aType == 0x24) {
const BehaviorScript *bhv = (const BehaviorScript *) DynOS_Level_CmdGet(aCmd, 20);
for (s32 i = 0; i < 20; ++i) {
if (sWarpBhvSpawnTable[i] == bhv) {
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, ((((u32) DynOS_Level_CmdGet(aCmd, 16)) >> 16) & 0xFF));
if (_Warp->mType == -1) {
_Warp->mType = i;
_Warp->mPosX = (s16) DynOS_Level_CmdGet(aCmd, 4);
_Warp->mPosY = (s16) DynOS_Level_CmdGet(aCmd, 6);
_Warp->mPosZ = (s16) DynOS_Level_CmdGet(aCmd, 8);
_Warp->mAngle = (s16)((((s32)((s16) DynOS_Level_CmdGet(aCmd, 12))) * 0x8000) / 180);
}
break;
}
}
}
// WARP_NODE
else if (aType == 0x26) {
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2));
if (_Warp->mDestLevel == 0) {
_Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3);
_Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4);
_Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5);
}
}
// PAINTING_WARP_NODE
else if (aType == 0x27) {
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2));
if (_Warp->mDestLevel == 0) {
_Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3);
_Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4);
_Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5);
}
}
// SLEEP
// SLEEP_BEFORE_EXIT
else if (aType == 0x03 || aType == 0x04) {
return 3;
}
return 0;
}
// Runs only once
static void DynOS_Level_Init() {
static bool sInited = false;
if (!sInited) {
// Level scripts
DynOS_Level_ParseScript(level_main_scripts_entry, DynOS_Level_PreprocessMasterScript);
// Level warps
for (sDynosCurrentLevelNum = 0; sDynosCurrentLevelNum != LEVEL_COUNT; ++sDynosCurrentLevelNum) {
if (sDynosLevelScripts[sDynosCurrentLevelNum]) {
DynOS_Level_ParseScript(sDynosLevelScripts[sDynosCurrentLevelNum], DynOS_Level_PreprocessScript);
}
}
// Level list ordered by course id
for (s32 i = COURSE_MIN; i <= COURSE_MAX; ++i) {
if (i == COURSE_CAKE_END) continue;
for (s32 j = 1; j != LEVEL_COUNT; ++j) {
if (gLevelToCourseNumTable[j - 1] == i) {
sDynosLevelList.Add(j);
}
}
}
// Done
sInited = true;
}
}
//
// Common
//
s32 DynOS_Level_GetCount() {
DynOS_Level_Init();
return sDynosLevelList.Count();
}
const s32 *DynOS_Level_GetList() {
DynOS_Level_Init();
return sDynosLevelList.begin();
}
s32 DynOS_Level_GetCourse(s32 aLevel) {
return (s32) gLevelToCourseNumTable[aLevel - 1];
}
const void *DynOS_Level_GetScript(s32 aLevel) {
DynOS_Level_Init();
return sDynosLevelScripts[aLevel];
}
//
// Course name
//
const u8 *DynOS_Level_GetName(s32 aLevel, bool aDecaps, bool aAddCourseNumber) {
DynOS_Level_Init();
static u8 sBuffer[256];
memset(sBuffer, 0xFF, 256);
s32 _Course = DynOS_Level_GetCourse(aLevel);
// Level name
if (aLevel == LEVEL_BOWSER_1) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_1);
} else if (aLevel == LEVEL_BOWSER_2) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_2);
} else if (aLevel == LEVEL_BOWSER_3) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_3);
} else if (_Course < COURSE_BOB) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_CASTLE);
} else if (_Course >= COURSE_CAKE_END) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_CASTLE);
} else {
const u8 *_CourseName = ((const u8 **) seg2_course_name_table)[_Course - COURSE_BOB] + 3;
memcpy(sBuffer, _CourseName, DynOS_String_Length(_CourseName));
}
// Decaps
if (aDecaps) {
DynOS_String_Decapitalize(sBuffer);
}
// Course number
if (aAddCourseNumber && (_Course >= COURSE_BOB) && (_Course <= COURSE_STAGES_MAX)) {
memmove(sBuffer + 5, sBuffer, DynOS_String_Length(sBuffer));
sBuffer[0] = ((_Course / 10) == 0 ? 158 : (_Course / 10));
sBuffer[1] = (_Course % 10);
sBuffer[2] = 158;
sBuffer[3] = 159;
sBuffer[4] = 158;
}
return sBuffer;
}
//
// Act/Star name
//
const u8 *DynOS_Level_GetActName(s32 aLevel, s32 aAct, bool aDecaps, bool aAddStarNumber) {
DynOS_Level_Init();
static u8 sBuffer[256];
memset(sBuffer, 0xFF, 256);
s32 _Course = DynOS_Level_GetCourse(aLevel);
// Star name
if (_Course < COURSE_BOB) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_ONE_SECRET_STAR);
} else if (aLevel == LEVEL_BITDW) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR);
} else if (aLevel == LEVEL_BITFS) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR);
} else if (aLevel == LEVEL_BITS) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR);
} else if (_Course > COURSE_STAGES_MAX) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_EMPTY);
} else if (aAct >= 7) {
SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_100_COINS_STAR);
} else {
const u8 *_ActName = ((const u8 **) seg2_act_name_table)[(_Course - COURSE_BOB) * 6 + (aAct - 1)];
memcpy(sBuffer, _ActName, DynOS_String_Length(_ActName));
}
// Decaps
if (aDecaps) {
DynOS_String_Decapitalize(sBuffer);
}
// Star number
if (aAddStarNumber && (_Course >= COURSE_BOB) && (_Course <= COURSE_STAGES_MAX)) {
memmove(sBuffer + 5, sBuffer, DynOS_String_Length(sBuffer));
sBuffer[0] = ((aAct / 10) == 0 ? 158 : (aAct / 10));
sBuffer[1] = (aAct % 10);
sBuffer[2] = 158;
sBuffer[3] = 159;
sBuffer[4] = 158;
}
return sBuffer;
}
const u8 *DynOS_Level_GetAreaName(s32 aLevel, s32 aArea, bool aDecaps) {
DynOS_Level_Init();
static const char *sAreaNamesPerLevel[][4] = {
{ "", "", "", "" },
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BoB */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WF */
{ "MAIN AREA", "SUNKEN SHIP", "NOT AVAILABLE", "NOT AVAILABLE" }, /* JRB */
{ "MAIN AREA", "COTTAGE SLIDE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* CCM */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BBH */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* HMC */
{ "MAIN AREA", "VOLCANO", "NOT AVAILABLE", "NOT AVAILABLE" }, /* LLL */
{ "MAIN AREA", "PYRAMID", "EYEROCK'S ROOM", "NOT AVAILABLE" }, /* SSL */
{ "MAIN AREA", "DOCKS", "NOT AVAILABLE", "NOT AVAILABLE" }, /* DDD */
{ "MAIN AREA", "IGLOO", "NOT AVAILABLE", "NOT AVAILABLE" }, /* SL */
{ "MAIN AREA", "DOWNTOWN", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WDW */
{ "MAIN AREA", "SECRET SLIDE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TTM */
{ "HUGE ISLAND", "TINY ISLAND", "WIGGLER'S ROOM", "NOT AVAILABLE" }, /* THI */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TTC */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* RR */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITDW */
{ "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 1 */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITFS */
{ "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 2 */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITS */
{ "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 3 */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* PSS */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TOTWC */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* COTMC */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* VCUTM */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WMOTR */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* SA */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Castle grounds */
{ "FIRST FLOOR", "SECOND FLOOR", "BASEMENT", "NOT AVAILABLE" }, /* Castle inside */
{ "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Castle courtyard */
};
static u8 sBuffer[256];
memset(sBuffer, 0xFF, 256);
// Area name
switch (aLevel) {
case LEVEL_BOB: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[1][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_WF: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[2][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_JRB: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[3][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_CCM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[4][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BBH: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[5][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_HMC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[6][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_LLL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[7][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_SSL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[8][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_DDD: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[9][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_SL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[10][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_WDW: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[11][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_TTM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[12][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_THI: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[13][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_TTC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[14][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_RR: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[15][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BITDW: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[16][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BOWSER_1: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[17][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BITFS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[18][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BOWSER_2: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[19][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BITS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[20][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_BOWSER_3: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[21][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_PSS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[22][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_TOTWC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[23][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_COTMC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[24][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_VCUTM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[25][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_WMOTR: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[26][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_SA: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[27][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_CASTLE_GROUNDS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[28][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_CASTLE: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[29][MIN(MAX(aArea - 1, 0), 3)]); break;
case LEVEL_CASTLE_COURTYARD: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[30][MIN(MAX(aArea - 1, 0), 3)]); break;
default: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[0][MIN(MAX(aArea - 1, 0), 3)]); break;
}
// Decaps
if (aDecaps) {
DynOS_String_Decapitalize(sBuffer);
}
return sBuffer;
}
//
// Level Script Preprocessing
// By default,
// - Ifs are always true
// - Skips are always false
// - Loops break after the first loop
//
struct LvlCmd {
u8 mType;
u8 mSize;
};
struct Stack {
u64 mData[32];
s32 mBaseIndex;
s32 mTopIndex;
};
template <typename T>
static void StackPush(Stack& aStack, const T &aValue) {
if (aStack.mTopIndex >= 0) {
aStack.mData[aStack.mTopIndex] = u64(aValue);
aStack.mTopIndex++;
}
}
template <typename T>
static T StackPop(Stack& aStack) {
if (aStack.mTopIndex <= 0) {
return (T) 0;
}
aStack.mTopIndex--;
return (T) aStack.mData[aStack.mTopIndex];
}
static LvlCmd *DynOS_Level_CmdExecute(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize));
StackPush(aStack, aStack.mBaseIndex);
aStack.mBaseIndex = aStack.mTopIndex;
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12);
}
static LvlCmd *DynOS_Level_CmdExitAndExecute(Stack &aStack, LvlCmd *aCmd) {
aStack.mTopIndex = aStack.mBaseIndex;
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12);
}
static LvlCmd *DynOS_Level_CmdExit(Stack &aStack, LvlCmd *aCmd) {
aStack.mTopIndex = aStack.mBaseIndex;
aStack.mBaseIndex = StackPop<s32>(aStack);
return StackPop<LvlCmd *>(aStack);
}
static LvlCmd *DynOS_Level_CmdSleep(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSleepBeforeExit(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdJump(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4);
}
static LvlCmd *DynOS_Level_CmdJumpLink(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize));
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4);
}
static LvlCmd *DynOS_Level_CmdReturn(Stack &aStack, UNUSED LvlCmd *aCmd) {
return StackPop<LvlCmd *>(aStack);
}
static LvlCmd *DynOS_Level_CmdJumpLinkPushArg(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize));
StackPush(aStack, DynOS_Level_CmdGet(aCmd, 2));
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdJumpRepeat(Stack &aStack, LvlCmd *aCmd) {
aStack.mTopIndex -= 2;
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoopBegin(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize));
StackPush(aStack, 0);
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoopUntil(Stack &aStack, LvlCmd *aCmd) {
aStack.mTopIndex -= 2;
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdJumpIf(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); /* Not an error, that's intentional */
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8);
}
static LvlCmd *DynOS_Level_CmdJumpLinkIf(Stack &aStack, LvlCmd *aCmd) {
StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize));
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8);
}
static LvlCmd *DynOS_Level_CmdSkipIf(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSkip(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSkipNop(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdCall(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdCallLoop(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetRegister(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdPushPool(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdPopPool(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadFixed(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadRaw(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadMIO0(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadMarioHead(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadMIO0Texture(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdInitLevel(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdClearLevel(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdAllocLevelPool(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdFreeLevelPool(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdBeginArea(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdEndArea(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadModelFromDL(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadModelFromGeo(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_Cmd23(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdMario(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdObject(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdWarpNode(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdInstantWarp(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetTerrainType(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdPaintingWarpNode(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_Cmd3A(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetWhirlpool(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetBlackout(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetGamma(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetTerrain(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetRooms(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdMacroObjects(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdLoadArea(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdUnloadArea(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetMarioStartPos(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_Cmd2C(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_Cmd2D(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetTransition(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdNop(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdShowDialog(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetBackgroundMusic(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdSetMenuMusic(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdStopMusic(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdGetOrSet(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdAdvanceDemo(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdClearDemoPointer(Stack &aStack, LvlCmd *aCmd) {
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static LvlCmd *DynOS_Level_CmdJumpArea(Stack &aStack, LvlCmd *aCmd, s32 (*aPreprocessFunction)(u8, void *)) {
DynOS_Level_ParseScript((const void *) DynOS_Level_CmdGet(aCmd, 8), aPreprocessFunction);
return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize);
}
static void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *)) {
Stack _Stack;
_Stack.mBaseIndex = -1;
_Stack.mTopIndex = 0;
for (LvlCmd *_Cmd = (LvlCmd *) aScript; _Cmd != NULL;) {
u8 _CmdType = (_Cmd->mType & 0x3F);
s32 _Action = aPreprocessFunction(_CmdType, (void *) _Cmd);
switch (_Action) {
case 0:
switch (_CmdType) {
case 0x00: _Cmd = DynOS_Level_CmdExecute(_Stack, _Cmd); break;
case 0x01: _Cmd = DynOS_Level_CmdExitAndExecute(_Stack, _Cmd); break;
case 0x02: _Cmd = DynOS_Level_CmdExit(_Stack, _Cmd); break;
case 0x03: _Cmd = DynOS_Level_CmdSleep(_Stack, _Cmd); break;
case 0x04: _Cmd = DynOS_Level_CmdSleepBeforeExit(_Stack, _Cmd); break;
case 0x05: _Cmd = DynOS_Level_CmdJump(_Stack, _Cmd); break;
case 0x06: _Cmd = DynOS_Level_CmdJumpLink(_Stack, _Cmd); break;
case 0x07: _Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd); break;
case 0x08: _Cmd = DynOS_Level_CmdJumpLinkPushArg(_Stack, _Cmd); break;
case 0x09: _Cmd = DynOS_Level_CmdJumpRepeat(_Stack, _Cmd); break;
case 0x0A: _Cmd = DynOS_Level_CmdLoopBegin(_Stack, _Cmd); break;
case 0x0B: _Cmd = DynOS_Level_CmdLoopUntil(_Stack, _Cmd); break;
case 0x0C: _Cmd = DynOS_Level_CmdJumpIf(_Stack, _Cmd); break;
case 0x0D: _Cmd = DynOS_Level_CmdJumpLinkIf(_Stack, _Cmd); break;
case 0x0E: _Cmd = DynOS_Level_CmdSkipIf(_Stack, _Cmd); break;
case 0x0F: _Cmd = DynOS_Level_CmdSkip(_Stack, _Cmd); break;
case 0x10: _Cmd = DynOS_Level_CmdSkipNop(_Stack, _Cmd); break;
case 0x11: _Cmd = DynOS_Level_CmdCall(_Stack, _Cmd); break;
case 0x12: _Cmd = DynOS_Level_CmdCallLoop(_Stack, _Cmd); break;
case 0x13: _Cmd = DynOS_Level_CmdSetRegister(_Stack, _Cmd); break;
case 0x14: _Cmd = DynOS_Level_CmdPushPool(_Stack, _Cmd); break;
case 0x15: _Cmd = DynOS_Level_CmdPopPool(_Stack, _Cmd); break;
case 0x16: _Cmd = DynOS_Level_CmdLoadFixed(_Stack, _Cmd); break;
case 0x17: _Cmd = DynOS_Level_CmdLoadRaw(_Stack, _Cmd); break;
case 0x18: _Cmd = DynOS_Level_CmdLoadMIO0(_Stack, _Cmd); break;
case 0x19: _Cmd = DynOS_Level_CmdLoadMarioHead(_Stack, _Cmd); break;
case 0x1A: _Cmd = DynOS_Level_CmdLoadMIO0Texture(_Stack, _Cmd); break;
case 0x1B: _Cmd = DynOS_Level_CmdInitLevel(_Stack, _Cmd); break;
case 0x1C: _Cmd = DynOS_Level_CmdClearLevel(_Stack, _Cmd); break;
case 0x1D: _Cmd = DynOS_Level_CmdAllocLevelPool(_Stack, _Cmd); break;
case 0x1E: _Cmd = DynOS_Level_CmdFreeLevelPool(_Stack, _Cmd); break;
case 0x1F: _Cmd = DynOS_Level_CmdBeginArea(_Stack, _Cmd); break;
case 0x20: _Cmd = DynOS_Level_CmdEndArea(_Stack, _Cmd); break;
case 0x21: _Cmd = DynOS_Level_CmdLoadModelFromDL(_Stack, _Cmd); break;
case 0x22: _Cmd = DynOS_Level_CmdLoadModelFromGeo(_Stack, _Cmd); break;
case 0x23: _Cmd = DynOS_Level_Cmd23(_Stack, _Cmd); break;
case 0x24: _Cmd = DynOS_Level_CmdObject(_Stack, _Cmd); break;
case 0x25: _Cmd = DynOS_Level_CmdMario(_Stack, _Cmd); break;
case 0x26: _Cmd = DynOS_Level_CmdWarpNode(_Stack, _Cmd); break;
case 0x27: _Cmd = DynOS_Level_CmdPaintingWarpNode(_Stack, _Cmd); break;
case 0x28: _Cmd = DynOS_Level_CmdInstantWarp(_Stack, _Cmd); break;
case 0x29: _Cmd = DynOS_Level_CmdLoadArea(_Stack, _Cmd); break;
case 0x2A: _Cmd = DynOS_Level_CmdUnloadArea(_Stack, _Cmd); break;
case 0x2B: _Cmd = DynOS_Level_CmdSetMarioStartPos(_Stack, _Cmd); break;
case 0x2C: _Cmd = DynOS_Level_Cmd2C(_Stack, _Cmd); break;
case 0x2D: _Cmd = DynOS_Level_Cmd2D(_Stack, _Cmd); break;
case 0x2E: _Cmd = DynOS_Level_CmdSetTerrain(_Stack, _Cmd); break;
case 0x2F: _Cmd = DynOS_Level_CmdSetRooms(_Stack, _Cmd); break;
case 0x30: _Cmd = DynOS_Level_CmdShowDialog(_Stack, _Cmd); break;
case 0x31: _Cmd = DynOS_Level_CmdSetTerrainType(_Stack, _Cmd); break;
case 0x32: _Cmd = DynOS_Level_CmdNop(_Stack, _Cmd); break;
case 0x33: _Cmd = DynOS_Level_CmdSetTransition(_Stack, _Cmd); break;
case 0x34: _Cmd = DynOS_Level_CmdSetBlackout(_Stack, _Cmd); break;
case 0x35: _Cmd = DynOS_Level_CmdSetGamma(_Stack, _Cmd); break;
case 0x36: _Cmd = DynOS_Level_CmdSetBackgroundMusic(_Stack, _Cmd); break;
case 0x37: _Cmd = DynOS_Level_CmdSetMenuMusic(_Stack, _Cmd); break;
case 0x38: _Cmd = DynOS_Level_CmdStopMusic(_Stack, _Cmd); break;
case 0x39: _Cmd = DynOS_Level_CmdMacroObjects(_Stack, _Cmd); break;
case 0x3A: _Cmd = DynOS_Level_Cmd3A(_Stack, _Cmd); break;
case 0x3B: _Cmd = DynOS_Level_CmdSetWhirlpool(_Stack, _Cmd); break;
case 0x3C: _Cmd = DynOS_Level_CmdGetOrSet(_Stack, _Cmd); break;
case 0x3D: _Cmd = DynOS_Level_CmdAdvanceDemo(_Stack, _Cmd); break;
case 0x3E: _Cmd = DynOS_Level_CmdClearDemoPointer(_Stack, _Cmd); break;
case 0x3F: _Cmd = DynOS_Level_CmdJumpArea(_Stack, _Cmd, aPreprocessFunction); break;
} break;
case 1:
_Cmd = (LvlCmd *) DynOS_Level_CmdNext(_Cmd, _Cmd->mSize);
break;
case 2:
_Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd);
break;
case 3:
return;
}
}
}
//
// Level Script Utilities
//
s16 *DynOS_Level_GetWarp(s32 aLevel, s32 aArea, u8 aWarpId) {
DynOS_Level_Init();
for (const auto &_Warp : sDynosLevelWarps[aLevel]) {
if (_Warp.mArea == aArea && _Warp.mId == aWarpId) {
return (s16 *) &_Warp;
}
}
return NULL;
}
s16 *DynOS_Level_GetWarpEntry(s32 aLevel, s32 aArea) {
DynOS_Level_Init();
if (aLevel == LEVEL_TTM && aArea > 2) return NULL;
return DynOS_Level_GetWarp(aLevel, aArea, 0x0A);
}
s16 *DynOS_Level_GetWarpDeath(s32 aLevel, s32 aArea) {
DynOS_Level_Init();
s16 *_Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF1);
if (!_Warp) _Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF3);
return _Warp;
}

64
data/dynos_main.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "dynos.cpp.h"
extern "C" {
#include "sm64.h"
#include "level_commands.h"
#include "game/level_update.h"
#include "game/options_menu.h"
#include "game/object_list_processor.h"
extern s16 gMenuMode;
extern s8 gDialogBoxState;
#ifdef OMM_DEFINES_H
extern void omm_opt_init();
#endif
}
//
// Main Menu
//
void DynOS_ReturnToMainMenu() {
optmenu_toggle();
level_set_transition(0, NULL);
gDialogBoxState = 0;
gMenuMode = -1;
fade_into_special_warp(-2, 0);
}
//
// Init
//
DYNOS_AT_STARTUP void DynOS_Init() {
#ifdef OMM_DEFINES_H
omm_opt_init();
#endif
DynOS_Opt_Init();
}
//
// Update
//
static bool sDynosIsLevelEntry = false;
void DynOS_UpdateOpt(void *aPad) {
if (sDynosIsLevelEntry) {
DynOS_Warp_SetParam(gCurrLevelNum, -1);
sDynosIsLevelEntry = false;
}
DynOS_Opt_Update((OSContPad *) aPad);
gPrevFrameObjectCount = 0;
}
void *DynOS_UpdateCmd(void *aCmd) {
static const uintptr_t sCmdLevelEntry[] = { CALL(0, lvl_init_or_update) };
sDynosIsLevelEntry |= (memcmp(aCmd, sCmdLevelEntry, sizeof(sCmdLevelEntry)) == 0);
return DynOS_Warp_Update(aCmd, sDynosIsLevelEntry);
}
void DynOS_UpdateGfx() {
DynOS_Gfx_Update();
}
bool DynOS_IsTransitionActive() {
return gWarpTransition.isActive;
}

502
data/dynos_misc.cpp Normal file
View file

@ -0,0 +1,502 @@
#include "dynos.cpp.h"
extern "C" {
#include "object_fields.h"
#include "game/object_helpers.h"
#include "game/segment2.h"
#include "game/level_geo.h"
#include "game/level_update.h"
#include "game/moving_texture.h"
#include "game/paintings.h"
#include "game/geo_misc.h"
#include "game/mario_misc.h"
#include "game/mario_actions_cutscene.h"
#include "game/screen_transition.h"
#include "game/object_list_processor.h"
#include "game/behavior_actions.h"
#include "game/rendering_graph_node.h"
#include "actors/common0.h"
#include "actors/common1.h"
#include "actors/group0.h"
#include "actors/group1.h"
#include "actors/group2.h"
#include "actors/group3.h"
#include "actors/group4.h"
#include "actors/group5.h"
#include "actors/group6.h"
#include "actors/group7.h"
#include "actors/group8.h"
#include "actors/group9.h"
#include "actors/group10.h"
#include "actors/group11.h"
#include "actors/group12.h"
#include "actors/group13.h"
#include "actors/group14.h"
#include "actors/group15.h"
#include "actors/group16.h"
#include "actors/group17.h"
#ifdef COOP
#include "actors/custom0.h"
#include "actors/zcustom0.h"
#endif
}
//
// String
//
static const struct { const char *mStr; u8 mChar64; s32 mWidth; } sSm64CharMap[] = {
{ "0", 0x00, 7 }, { "1", 0x01, 7 }, { "2", 0x02, 7 }, { "3", 0x03, 7 }, { "4", 0x04, 7 }, { "5", 0x05, 7 },
{ "6", 0x06, 7 }, { "7", 0x07, 7 }, { "8", 0x08, 7 }, { "9", 0x09, 7 }, { "A", 0x0A, 6 }, { "B", 0x0B, 6 },
{ "C", 0x0C, 6 }, { "D", 0x0D, 6 }, { "E", 0x0E, 6 }, { "F", 0x0F, 6 }, { "G", 0x10, 6 }, { "H", 0x11, 6 },
{ "I", 0x12, 5 }, { "J", 0x13, 6 }, { "K", 0x14, 6 }, { "L", 0x15, 5 }, { "M", 0x16, 8 }, { "N", 0x17, 8 },
{ "O", 0x18, 6 }, { "P", 0x19, 6 }, { "Q", 0x1A, 6 }, { "R", 0x1B, 6 }, { "S", 0x1C, 6 }, { "T", 0x1D, 5 },
{ "U", 0x1E, 6 }, { "V", 0x1F, 6 }, { "W", 0x20, 8 }, { "X", 0x21, 7 }, { "Y", 0x22, 6 }, { "Z", 0x23, 6 },
{ "a", 0x24, 6 }, { "b", 0x25, 5 }, { "c", 0x26, 5 }, { "d", 0x27, 6 }, { "e", 0x28, 5 }, { "f", 0x29, 5 },
{ "g", 0x2A, 6 }, { "h", 0x2B, 5 }, { "i", 0x2C, 4 }, { "j", 0x2D, 5 }, { "k", 0x2E, 5 }, { "l", 0x2F, 3 },
{ "m", 0x30, 7 }, { "n", 0x31, 5 }, { "o", 0x32, 5 }, { "p", 0x33, 5 }, { "q", 0x34, 6 }, { "r", 0x35, 5 },
{ "s", 0x36, 5 }, { "t", 0x37, 5 }, { "u", 0x38, 5 }, { "v", 0x39, 5 }, { "w", 0x3A, 7 }, { "x", 0x3B, 7 },
{ "y", 0x3C, 5 }, { "z", 0x3D, 5 }, { "\'", 0x3E, 4 }, { ".", 0x3F, 4 }, { "^", 0x50, 8 }, { "|", 0x51, 8 },
{ "<", 0x52, 8 }, { ">", 0x53, 8 }, { "[A]", 0x54, 7 }, { "[B]", 0x55, 7 }, { "[C]", 0x56, 6 }, { "[Z]", 0x57, 7 },
{ "[R]", 0x58, 7 }, { ",", 0x6F, 4 }, { " ", 0x9E, 5 }, { "-", 0x9F, 6 }, { "/", 0xD0, 10 }, { "[%]", 0xE0, 7 },
{ "(", 0xE1, 5 }, { ")(", 0xE2, 10 }, { ")", 0xE3, 5 }, { "+", 0xE4, 9 }, { "&", 0xE5, 8 }, { ":", 0xE6, 4 },
{ "!", 0xF2, 5 }, { "%", 0xF3, 7 }, { "?", 0xF4, 7 }, { "~", 0xF7, 8 }, { "$", 0xF9, 8 }, { "@", 0xFA, 10 },
{ "*", 0xFB, 6 }, { "=", 0xFD, 10 }, { "\n", 0xFE, 0 },
};
static const char *DynOS_String_AddChar64(u8 *aStr64, const char *pStr, s32 &aIndex) {
for (const auto &c : sSm64CharMap) {
if (strstr(pStr, c.mStr) == pStr) {
aStr64[aIndex++] = c.mChar64;
return pStr + strlen(c.mStr);
}
}
// Put a space by default
aStr64[aIndex++] = 0x9E;
return pStr + 1;
}
u8 *DynOS_String_Convert(const char *aString, bool aHeapAlloc) {
// Allocation
static u8 sStringBuffer[8][2048];
static u32 sStringBufferIndex = 0;
u8 *_Str64;
if (aHeapAlloc) {
_Str64 = New<u8>(2048);
} else {
_Str64 = sStringBuffer[sStringBufferIndex];
sStringBufferIndex = (sStringBufferIndex + 1) % 8;
}
// Conversion
memset(_Str64, 0xFF, 2048);
const char *pStr = aString;
for (s32 i = 0; *pStr != 0 && i < 2047;) {
pStr = DynOS_String_AddChar64(_Str64, pStr, i);
}
return _Str64;
}
u8 *DynOS_String_Decapitalize(u8 *aStr64) {
bool _WasSpace = true;
for (u8 *pStr64 = aStr64; *pStr64 != 0xFF; pStr64++) {
if (*pStr64 >= 10 && *pStr64 <= 35) {
if (_WasSpace) _WasSpace = false;
else *pStr64 += 26;
} else if (*pStr64 >= 63) {
_WasSpace = true;
}
}
return aStr64;
}
s32 DynOS_String_Length(const u8 *aStr64) {
s32 _Length = 0;
for (; aStr64 && *aStr64 != 255; aStr64++, _Length++);
return _Length;
}
s32 DynOS_String_WidthChar64(u8 aChar64) {
for (const auto &c : sSm64CharMap) {
if (c.mChar64 == aChar64) {
return c.mWidth;
}
}
return 0;
}
s32 DynOS_String_Width(const u8 *aStr64) {
s32 _Width = 0;
for (; *aStr64 != 0xFF; aStr64++) {
_Width += DynOS_String_WidthChar64(*aStr64);
}
return _Width;
}
//
// Geo
//
static void *geo_rotate_3d_coin(s32 callContext, void *node, UNUSED void *c) {
if (callContext == GEO_CONTEXT_RENDER) {
struct Object *obj = (struct Object *) gCurGraphNodeObject;
struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) ((struct GraphNode *) node)->next;
rotNode->rotation[0] = 0;
rotNode->rotation[1] = obj->oAnimState;
rotNode->rotation[2] = 0;
obj->oAnimState += 0x0800;
}
return NULL;
}
//
// Actors
//
// &__Actors()
#define define_actor(geo) (const void *) #geo, (const void *) geo
static const void *sDynosActors[] = {
define_actor(amp_geo),
define_actor(birds_geo),
define_actor(blargg_geo),
define_actor(blue_coin_switch_geo),
define_actor(black_bobomb_geo),
define_actor(bobomb_buddy_geo),
define_actor(boo_geo),
define_actor(boo_castle_geo),
define_actor(bookend_geo),
define_actor(bookend_part_geo),
define_actor(bowling_ball_geo),
define_actor(bowling_ball_track_geo),
define_actor(bowser_geo),
define_actor(bowser2_geo),
define_actor(bowser_bomb_geo),
define_actor(bowser_flames_geo),
define_actor(bowser_impact_smoke_geo),
define_actor(bowser_1_yellow_sphere_geo),
define_actor(invisible_bowser_accessory_geo),
define_actor(bowser_key_geo),
define_actor(bowser_key_cutscene_geo),
define_actor(breakable_box_geo),
define_actor(breakable_box_small_geo),
define_actor(bub_geo),
define_actor(bubba_geo),
define_actor(bubble_geo),
define_actor(bullet_bill_geo),
define_actor(bully_geo),
define_actor(bully_boss_geo),
define_actor(burn_smoke_geo),
define_actor(butterfly_geo),
define_actor(cannon_barrel_geo),
define_actor(cannon_base_geo),
define_actor(cap_switch_geo),
define_actor(cartoon_star_geo),
define_actor(chain_chomp_geo),
define_actor(checkerboard_platform_geo),
define_actor(chilly_chief_geo),
define_actor(chilly_chief_big_geo),
define_actor(chuckya_geo),
define_actor(clam_shell_geo),
define_actor(yellow_coin_geo),
define_actor(yellow_coin_no_shadow_geo),
define_actor(blue_coin_geo),
define_actor(blue_coin_no_shadow_geo),
define_actor(red_coin_geo),
define_actor(red_coin_no_shadow_geo),
define_actor(dirt_animation_geo),
define_actor(dorrie_geo),
define_actor(cabin_door_geo),
define_actor(castle_door_geo),
define_actor(castle_door_0_star_geo),
define_actor(castle_door_1_star_geo),
define_actor(castle_door_3_stars_geo),
define_actor(haunted_door_geo),
define_actor(hazy_maze_door_geo),
define_actor(metal_door_geo),
define_actor(key_door_geo),
define_actor(wooden_door_geo),
define_actor(enemy_lakitu_geo),
define_actor(exclamation_box_geo),
define_actor(exclamation_box_outline_geo),
define_actor(explosion_geo),
define_actor(eyerok_left_hand_geo),
define_actor(eyerok_right_hand_geo),
define_actor(fish_geo),
define_actor(cyan_fish_geo),
define_actor(flyguy_geo),
define_actor(red_flame_geo),
define_actor(red_flame_shadow_geo),
define_actor(blue_flame_geo),
define_actor(fwoosh_geo),
define_actor(goomba_geo),
define_actor(haunted_cage_geo),
define_actor(haunted_chair_geo),
define_actor(heart_geo),
define_actor(heave_ho_geo),
define_actor(hoot_geo),
define_actor(king_bobomb_geo),
define_actor(klepto_geo),
define_actor(koopa_with_shell_geo),
define_actor(koopa_without_shell_geo),
define_actor(koopa_flag_geo),
define_actor(koopa_shell_geo),
define_actor(lakitu_geo),
define_actor(mad_piano_geo),
define_actor(manta_seg5_geo_05008D14),
define_actor(mario_geo),
define_actor(marios_cap_geo),
define_actor(marios_metal_cap_geo),
define_actor(marios_wing_cap_geo),
define_actor(marios_winged_metal_cap_geo),
define_actor(metal_box_geo),
define_actor(metallic_ball_geo),
define_actor(mips_geo),
define_actor(mist_geo),
define_actor(moneybag_geo),
define_actor(monty_mole_geo),
define_actor(mr_blizzard_geo),
define_actor(mr_blizzard_hidden_geo),
define_actor(mr_i_geo),
define_actor(mr_i_iris_geo),
define_actor(mushroom_1up_geo),
define_actor(number_geo),
define_actor(peach_geo),
define_actor(penguin_geo),
define_actor(piranha_plant_geo),
define_actor(pokey_head_geo),
define_actor(pokey_body_part_geo),
define_actor(purple_marble_geo),
define_actor(purple_switch_geo),
define_actor(scuttlebug_geo),
define_actor(seaweed_geo),
define_actor(skeeter_geo),
define_actor(small_key_geo),
define_actor(small_water_splash_geo),
define_actor(smoke_geo),
define_actor(snufit_geo),
define_actor(sparkles_geo),
define_actor(sparkles_animation_geo),
define_actor(spindrift_geo),
define_actor(spiny_geo),
define_actor(spiny_ball_geo),
define_actor(star_geo),
define_actor(transparent_star_geo),
define_actor(sushi_geo),
define_actor(swoop_geo),
define_actor(thwomp_geo),
define_actor(toad_geo),
define_actor(treasure_chest_base_geo),
define_actor(treasure_chest_lid_geo),
define_actor(bubbly_tree_geo),
define_actor(spiky_tree_geo),
define_actor(snow_tree_geo),
define_actor(palm_tree_geo),
define_actor(leaves_geo),
define_actor(tweester_geo),
define_actor(ukiki_geo),
define_actor(unagi_geo),
define_actor(warp_pipe_geo),
define_actor(water_bomb_geo),
define_actor(water_bomb_shadow_geo),
define_actor(water_ring_geo),
define_actor(water_splash_geo),
define_actor(idle_water_wave_geo),
define_actor(wave_trail_geo),
define_actor(white_particle_geo),
define_actor(white_puff_geo),
define_actor(whomp_geo),
define_actor(wiggler_head_geo),
define_actor(wiggler_body_geo),
define_actor(wooden_post_geo),
define_actor(wooden_signpost_geo),
define_actor(yellow_sphere_geo),
define_actor(yoshi_geo),
define_actor(yoshi_egg_geo),
#ifdef COOP
define_actor(luigi_geo),
define_actor(luigis_cap_geo),
define_actor(luigis_metal_cap_geo),
define_actor(luigis_wing_cap_geo),
define_actor(luigis_winged_metal_cap_geo),
define_actor(toad_player_geo),
define_actor(toads_cap_geo),
define_actor(toads_metal_cap_geo),
define_actor(toads_wing_cap_geo),
define_actor(waluigi_geo),
define_actor(waluigis_cap_geo),
define_actor(waluigis_metal_cap_geo),
define_actor(waluigis_wing_cap_geo),
define_actor(waluigis_winged_metal_cap_geo),
define_actor(wario_geo),
define_actor(warios_cap_geo),
define_actor(warios_metal_cap_geo),
define_actor(warios_wing_cap_geo),
define_actor(warios_winged_metal_cap_geo),
#endif
};
s32 DynOS_Geo_GetActorCount() {
return (s32) (sizeof(sDynosActors) / (2 * sizeof(sDynosActors[0])));
}
const char *DynOS_Geo_GetActorName(s32 aIndex) {
return (const char *) sDynosActors[2 * aIndex];
}
const void *DynOS_Geo_GetActorLayout(s32 aIndex) {
return (const void *) sDynosActors[2 * aIndex + 1];
}
s32 DynOS_Geo_GetActorIndex(const void *aGeoLayout) {
for (s32 i = 0; i != DynOS_Geo_GetActorCount(); ++i) {
if (sDynosActors[2 * i + 1] == aGeoLayout) {
return i;
}
}
return -1;
}
//
// Geo Functions
//
static const Array<Pair<const char *, void *>> &__GeoFunctions() {
#define define_geo_function(name) { #name, (void *) name }
static const Array<Pair<const char *, void *>> sGeoFunctions = {
define_geo_function(geo_mirror_mario_set_alpha),
define_geo_function(geo_switch_mario_stand_run),
define_geo_function(geo_switch_mario_eyes),
define_geo_function(geo_mario_tilt_torso),
define_geo_function(geo_mario_head_rotation),
define_geo_function(geo_switch_mario_hand),
define_geo_function(geo_mario_hand_foot_scaler),
define_geo_function(geo_switch_mario_cap_effect),
define_geo_function(geo_switch_mario_cap_on_off),
define_geo_function(geo_mario_rotate_wing_cap_wings),
define_geo_function(geo_switch_mario_hand_grab_pos),
define_geo_function(geo_render_mirror_mario),
define_geo_function(geo_mirror_mario_backface_culling),
define_geo_function(geo_update_projectile_pos_from_parent),
define_geo_function(geo_update_layer_transparency),
define_geo_function(geo_switch_anim_state),
define_geo_function(geo_switch_area),
define_geo_function(geo_camera_main),
define_geo_function(geo_camera_fov),
define_geo_function(geo_envfx_main),
define_geo_function(geo_skybox_main),
define_geo_function(geo_wdw_set_initial_water_level),
define_geo_function(geo_movtex_pause_control),
define_geo_function(geo_movtex_draw_water_regions),
define_geo_function(geo_movtex_draw_nocolor),
define_geo_function(geo_movtex_draw_colored),
define_geo_function(geo_movtex_draw_colored_no_update),
define_geo_function(geo_movtex_draw_colored_2_no_update),
define_geo_function(geo_movtex_update_horizontal),
define_geo_function(geo_movtex_draw_colored_no_update),
define_geo_function(geo_painting_draw),
define_geo_function(geo_painting_update),
define_geo_function(geo_exec_inside_castle_light),
define_geo_function(geo_exec_flying_carpet_timer_update),
define_geo_function(geo_exec_flying_carpet_create),
define_geo_function(geo_exec_cake_end_screen),
define_geo_function(geo_cannon_circle_base),
define_geo_function(geo_move_mario_part_from_parent),
define_geo_function(geo_bits_bowser_coloring),
define_geo_function(geo_update_body_rot_from_parent),
define_geo_function(geo_switch_bowser_eyes),
define_geo_function(geo_switch_tuxie_mother_eyes),
define_geo_function(geo_update_held_mario_pos),
define_geo_function(geo_snufit_move_mask),
define_geo_function(geo_snufit_scale_body),
define_geo_function(geo_scale_bowser_key),
{ "geo_rotate_coin", (void *) geo_rotate_3d_coin },
define_geo_function(geo_offset_klepto_held_object),
define_geo_function(geo_switch_peach_eyes),
#ifdef COOP
define_geo_function(geo_mario_set_player_colors),
#endif
};
#undef define_geo_function
return sGeoFunctions;
}
#define sGeoFunctions __GeoFunctions()
void *DynOS_Geo_GetFunctionPointerFromName(const String &aName) {
for (const auto &_GeoFunction : sGeoFunctions) {
if (aName == _GeoFunction.first) {
return _GeoFunction.second;
}
};
return NULL;
}
void *DynOS_Geo_GetFunctionPointerFromIndex(s32 aIndex) {
return sGeoFunctions[aIndex].second;
}
s32 DynOS_Geo_GetFunctionIndex(const void *aPtr) {
for (const auto &_GeoFunction : sGeoFunctions) {
if (_GeoFunction.second == aPtr) {
return (s32) (&_GeoFunction - sGeoFunctions.begin());
}
}
return -1;
}
static void _RelocateGraphNodePointers(struct GraphNode *aHead, u64 aOffset) {
struct GraphNode *_Node = aHead;
do {
if (_Node->prev) {
_Node->prev = (struct GraphNode *) ((u64) _Node->prev + aOffset);
}
if (_Node->next) {
_Node->next = (struct GraphNode *) ((u64) _Node->next + aOffset);
}
if (_Node->parent) {
_Node->parent = (struct GraphNode *) ((u64) _Node->parent + aOffset);
}
if (_Node->children) {
_Node->children = (struct GraphNode *) ((u64) _Node->children + aOffset);
_RelocateGraphNodePointers(_Node->children, aOffset);
}
_Node = _Node->next;
} while (_Node != aHead);
}
void *DynOS_Geo_GetGraphNode(const void *aGeoLayout, bool aKeepInMemory) {
static Array<Pair<void *, void *>> sLoadedGraphNodes;
if (aKeepInMemory) {
s32 _LoadedGraphNodeIndex = sLoadedGraphNodes.FindIf([&aGeoLayout](const Pair<void *, void *> &aLoadedGraphNode) { return aLoadedGraphNode.first == aGeoLayout; });
if (_LoadedGraphNodeIndex != -1) {
return sLoadedGraphNodes[_LoadedGraphNodeIndex].second;
}
}
// Process the geo layout on a large pool of memory (16 MB)
struct AllocOnlyPool *_Pool = (struct AllocOnlyPool *) calloc(1, 0x1000000);
_Pool->totalSpace = 0x1000000 - sizeof(struct AllocOnlyPool);
_Pool->usedSpace = 0;
_Pool->startPtr = (u8 *) _Pool + sizeof(struct AllocOnlyPool);
_Pool->freePtr = (u8 *) _Pool + sizeof(struct AllocOnlyPool);
void *_Processed = process_geo_layout(_Pool, (void *) aGeoLayout);
// Copy the graph node data to the minimum amount of memory needed
if (_Processed && _Pool->usedSpace != 0) {
struct GraphNode *_Node = (struct GraphNode *) calloc(1, _Pool->usedSpace);
memcpy(_Node, _Pool->startPtr, _Pool->usedSpace);
// Relocate all graph pointers
u64 _Offset = (u64) _Node - (u64) _Pool->startPtr;
_RelocateGraphNodePointers(_Node, _Offset);
// Add it to loaded graph nodes
if (aKeepInMemory) {
sLoadedGraphNodes.Add({ (void *) aGeoLayout, (void *) _Node });
}
free(_Pool);
return _Node;
}
free(_Pool);
return NULL;
}

748
data/dynos_opt.cpp Normal file
View file

@ -0,0 +1,748 @@
#include "dynos.cpp.h"
extern "C" {
#include "pc/configfile.h"
#include "audio/external.h"
#include "game/game_init.h"
#include "pc/controller/controller_keyboard.h"
#ifdef BETTERCAMERA
#include "game/bettercamera.h"
#endif
}
//
// Data
//
static DynosOption *sPrevOpt = NULL;
static DynosOption *sDynosMenu = NULL;
static DynosOption *sOptionsMenu = NULL;
static DynosOption *sCurrentMenu = NULL;
static DynosOption *sCurrentOpt = NULL;
extern s32 sBindingState;
//
// Action list
//
typedef bool (*DynosActionFunction)(const char *);
struct DynosAction : NoCopy {
String mFuncName;
DynosActionFunction mAction;
};
STATIC_STORAGE(Array<DynosAction *>, DynosActions);
#define sDynosActions __DynosActions()
static DynosActionFunction DynOS_Opt_GetAction(const String& aFuncName) {
for (auto &_DynosAction : sDynosActions) {
if (_DynosAction->mFuncName == aFuncName) {
return _DynosAction->mAction;
}
}
return NULL;
}
void DynOS_Opt_AddAction(const String& aFuncName, bool (*aFuncPtr)(const char *), bool aOverwrite) {
for (auto &_DynosAction : sDynosActions) {
if (_DynosAction->mFuncName == aFuncName) {
if (aOverwrite) {
_DynosAction->mAction = aFuncPtr;
}
return;
}
}
DynosAction *_DynosAction = New<DynosAction>();
_DynosAction->mFuncName = aFuncName;
_DynosAction->mAction = aFuncPtr;
sDynosActions.Add(_DynosAction);
}
//
// Constructors
//
static DynosOption *DynOS_Opt_GetExistingOption(DynosOption *aOpt, const String &aName) {
while (aOpt) {
if (aOpt->mName == aName) {
return aOpt;
}
if (aOpt->mType == DOPT_SUBMENU) {
DynosOption *_Opt = DynOS_Opt_GetExistingOption(aOpt->mSubMenu.mChild, aName);
if (_Opt) {
return _Opt;
}
}
aOpt = aOpt->mNext;
}
return NULL;
}
static DynosOption *DynOS_Opt_NewOption(const String &aName, const String &aConfigName, const String &aLabel, const String &aTitle) {
// Check if the option already exists
static DynosOption sDummyOpt;
if (DynOS_Opt_GetExistingOption(sDynosMenu, aName)) {
return &sDummyOpt;
}
// Create a new option
DynosOption *_Opt = New<DynosOption>();
_Opt->mName = aName;
_Opt->mConfigName = aConfigName;
_Opt->mLabel = { aLabel, NULL };
_Opt->mTitle = { aTitle, NULL };
_Opt->mDynos = true;
if (sPrevOpt == NULL) { // The very first option
_Opt->mPrev = NULL;
_Opt->mNext = NULL;
_Opt->mParent = NULL;
sDynosMenu = _Opt;
} else {
if (sPrevOpt->mType == DOPT_SUBMENU && sPrevOpt->mSubMenu.mEmpty) { // First option of a sub-menu
_Opt->mPrev = NULL;
_Opt->mNext = NULL;
_Opt->mParent = sPrevOpt;
sPrevOpt->mSubMenu.mChild = _Opt;
sPrevOpt->mSubMenu.mEmpty = false;
} else {
_Opt->mPrev = sPrevOpt;
_Opt->mNext = NULL;
_Opt->mParent = sPrevOpt->mParent;
sPrevOpt->mNext = _Opt;
}
}
sPrevOpt = _Opt;
return _Opt;
}
static void DynOS_Opt_EndSubMenu() {
if (sPrevOpt && sPrevOpt->mParent) {
if (sPrevOpt->mType == DOPT_SUBMENU && sPrevOpt->mSubMenu.mEmpty) { // ENDMENU command following a SUBMENU command
sPrevOpt->mSubMenu.mEmpty = false;
} else {
sPrevOpt = sPrevOpt->mParent;
}
}
}
static void DynOS_Opt_CreateSubMenu(const String &aName, const String &aLabel, const String &aTitle) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, "", aLabel, aTitle);
_Opt->mType = DOPT_SUBMENU;
_Opt->mSubMenu.mChild = NULL;
_Opt->mSubMenu.mEmpty = true;
}
static void DynOS_Opt_CreateToggle(const String &aName, const String &aConfigName, const String &aLabel, s32 aValue) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, aConfigName, aLabel, aLabel);
_Opt->mType = DOPT_TOGGLE;
_Opt->mToggle.mTog = New<bool>();
*_Opt->mToggle.mTog = (bool) aValue;
}
static void DynOS_Opt_CreateScroll(const String &aName, const String &aConfigName, const String &aLabel, s32 aMin, s32 aMax, s32 aStep, s32 aValue) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, aConfigName, aLabel, aLabel);
_Opt->mType = DOPT_SCROLL;
_Opt->mScroll.mMin = aMin;
_Opt->mScroll.mMax = aMax;
_Opt->mScroll.mStep = aStep;
_Opt->mScroll.mValue = New<s32>();
*_Opt->mScroll.mValue = aValue;
}
static void DynOS_Opt_CreateChoice(const String &aName, const String &aConfigName, const String &aLabel, const Array<String>& aChoices, s32 aValue) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, aConfigName, aLabel, aLabel);
_Opt->mType = DOPT_CHOICE;
_Opt->mChoice.mIndex = New<s32>();
*_Opt->mChoice.mIndex = aValue;
for (const auto &_Choice : aChoices) {
_Opt->mChoice.mChoices.Add({ _Choice, NULL });
}
}
static void DynOS_Opt_CreateButton(const String &aName, const String &aLabel, const String& aFuncName) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, "", aLabel, aLabel);
_Opt->mType = DOPT_BUTTON;
_Opt->mButton.mFuncName = aFuncName;
}
static void DynOS_Opt_CreateBind(const String &aName, const String &aConfigName, const String &aLabel, u32 aMask, u32 aBind0, u32 aBind1, u32 aBind2) {
DynosOption *_Opt = DynOS_Opt_NewOption(aName, aConfigName, aLabel, aLabel);
_Opt->mType = DOPT_BIND;
_Opt->mBind.mMask = aMask;
_Opt->mBind.mBinds = New<u32>(3);
_Opt->mBind.mBinds[0] = aBind0;
_Opt->mBind.mBinds[1] = aBind1;
_Opt->mBind.mBinds[2] = aBind2;
_Opt->mBind.mIndex = 0;
}
//
// Loop through DynosOptions
//
DynosOption *DynOS_Opt_Loop(DynosOption *aOpt, DynosLoopFunc aFunc, void *aData) {
while (aOpt) {
if (aFunc(aOpt, aData)) {
return aOpt;
} else if (aOpt->mType == DOPT_SUBMENU) {
DynosOption *_Opt = DynOS_Opt_Loop(aOpt->mSubMenu.mChild, aFunc, aData);
if (_Opt) {
return _Opt;
}
}
aOpt = aOpt->mNext;
}
return NULL;
}
//
// Get/Set values
//
static bool DynOS_Opt_Get(DynosOption *aOpt, void *aData) {
return aOpt->mName == (const char *) aData;
}
s32 DynOS_Opt_GetValue(const String &aName) {
DynosOption *_Opt = DynOS_Opt_Loop(sDynosMenu, DynOS_Opt_Get, (void *) aName.begin());
if (_Opt) {
switch (_Opt->mType) {
case DOPT_TOGGLE: return *_Opt->mToggle.mTog;
case DOPT_CHOICE: return *_Opt->mChoice.mIndex;
case DOPT_CHOICELEVEL: return *_Opt->mChoice.mIndex;
case DOPT_CHOICEAREA: return *_Opt->mChoice.mIndex;
case DOPT_CHOICESTAR: return *_Opt->mChoice.mIndex;
case DOPT_CHOICEPARAM: return *_Opt->mChoice.mIndex;
case DOPT_SCROLL: return *_Opt->mScroll.mValue;
default: break;
}
}
return 0;
}
void DynOS_Opt_SetValue(const String &aName, s32 aValue) {
DynosOption *_Opt = DynOS_Opt_Loop(sDynosMenu, DynOS_Opt_Get, (void *) aName.begin());
if (_Opt) {
switch (_Opt->mType) {
case DOPT_TOGGLE: *_Opt->mToggle.mTog = aValue; break;
case DOPT_CHOICE: *_Opt->mChoice.mIndex = aValue; break;
case DOPT_CHOICELEVEL: *_Opt->mChoice.mIndex = aValue; break;
case DOPT_CHOICEAREA: *_Opt->mChoice.mIndex = aValue; break;
case DOPT_CHOICESTAR: *_Opt->mChoice.mIndex = aValue; break;
case DOPT_CHOICEPARAM: *_Opt->mChoice.mIndex = aValue; break;
case DOPT_SCROLL: *_Opt->mScroll.mValue = aValue; break;
default: break;
}
}
}
//
// Processing
//
#define SOUND_DYNOS_SAVED (SOUND_MENU_MARIO_CASTLE_WARP2 | (0xFF << 8))
#define SOUND_DYNOS_SELECT (SOUND_MENU_CHANGE_SELECT | (0xF8 << 8))
#define SOUND_DYNOS_OK (SOUND_MENU_CHANGE_SELECT | (0xF8 << 8))
#define SOUND_DYNOS_CANCEL (SOUND_MENU_CAMERA_BUZZ | (0xFC << 8))
enum {
INPUT_LEFT,
INPUT_RIGHT,
INPUT_A,
INPUT_Z
};
enum {
RESULT_NONE,
RESULT_OK,
RESULT_CANCEL
};
static s32 DynOS_Opt_ProcessInput(DynosOption *aOpt, s32 input) {
switch (aOpt->mType) {
case DOPT_TOGGLE:
if (input == INPUT_LEFT) {
*aOpt->mToggle.mTog = false;
return RESULT_OK;
}
if (input == INPUT_RIGHT) {
*aOpt->mToggle.mTog = true;
return RESULT_OK;
}
if (input == INPUT_A) {
*aOpt->mToggle.mTog = !(*aOpt->mToggle.mTog);
return RESULT_OK;
}
break;
case DOPT_CHOICE:
if (input == INPUT_LEFT) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + aOpt->mChoice.mChoices.Count() - 1) % (aOpt->mChoice.mChoices.Count());
return RESULT_OK;
}
if (input == INPUT_RIGHT || input == INPUT_A) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 1) % (aOpt->mChoice.mChoices.Count());
return RESULT_OK;
}
break;
case DOPT_CHOICELEVEL:
if (input == INPUT_LEFT) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + DynOS_Level_GetCount() - 1) % (DynOS_Level_GetCount());
return RESULT_OK;
}
if (input == INPUT_RIGHT || input == INPUT_A) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 1) % (DynOS_Level_GetCount());
return RESULT_OK;
}
break;
case DOPT_CHOICEAREA:
if (input == INPUT_LEFT) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 3) % (4);
return RESULT_OK;
}
if (input == INPUT_RIGHT || input == INPUT_A) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 1) % (4);
return RESULT_OK;
}
break;
case DOPT_CHOICESTAR:
if (input == INPUT_LEFT) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 5) % (6);
return RESULT_OK;
}
if (input == INPUT_RIGHT || input == INPUT_A) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 1) % (6);
return RESULT_OK;
}
break;
case DOPT_CHOICEPARAM:
if (input == INPUT_LEFT) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 4) % (5);
return RESULT_OK;
}
if (input == INPUT_RIGHT || input == INPUT_A) {
*aOpt->mChoice.mIndex = (*aOpt->mChoice.mIndex + 1) % (5);
return RESULT_OK;
}
break;
case DOPT_SCROLL:
if (input == INPUT_LEFT) {
*aOpt->mScroll.mValue = MAX(aOpt->mScroll.mMin, *aOpt->mScroll.mValue - aOpt->mScroll.mStep * (gPlayer1Controller->buttonDown & A_BUTTON ? 5 : 1));
return RESULT_OK;
}
if (input == INPUT_RIGHT) {
*aOpt->mScroll.mValue = MIN(aOpt->mScroll.mMax, *aOpt->mScroll.mValue + aOpt->mScroll.mStep * (gPlayer1Controller->buttonDown & A_BUTTON ? 5 : 1));
return RESULT_OK;
}
break;
case DOPT_BIND:
if (input == INPUT_LEFT) {
aOpt->mBind.mIndex = MAX(0, aOpt->mBind.mIndex - 1);
return RESULT_OK;
}
if (input == INPUT_RIGHT) {
aOpt->mBind.mIndex = MIN(2, aOpt->mBind.mIndex + 1);
return RESULT_OK;
}
if (input == INPUT_Z) {
aOpt->mBind.mBinds[aOpt->mBind.mIndex] = VK_INVALID;
return RESULT_OK;
}
if (input == INPUT_A) {
aOpt->mBind.mBinds[aOpt->mBind.mIndex] = VK_INVALID;
sBindingState = 1;
controller_get_raw_key();
return RESULT_OK;
}
break;
case DOPT_BUTTON:
if (input == INPUT_A) {
DynosActionFunction _Action = DynOS_Opt_GetAction(aOpt->mButton.mFuncName);
if (_Action != NULL && _Action(aOpt->mName.begin())) {
return RESULT_OK;
}
return RESULT_CANCEL;
}
break;
case DOPT_SUBMENU:
if (input == INPUT_A) {
if (aOpt->mSubMenu.mChild != NULL) {
sCurrentOpt = aOpt->mSubMenu.mChild;
return RESULT_OK;
}
return RESULT_CANCEL;
}
break;
}
return RESULT_NONE;
}
static void DynOS_Opt_Open(DynosOption *aMenu) {
play_sound(SOUND_DYNOS_SELECT, gDefaultSoundArgs);
sCurrentMenu = aMenu;
sCurrentOpt = aMenu;
}
static void DynOS_Opt_Close(bool aPlaySavedSfx) {
if (sCurrentMenu != NULL) {
if (aPlaySavedSfx) {
play_sound(SOUND_DYNOS_SAVED, gDefaultSoundArgs);
}
#ifdef BETTERCAMERA
newcam_init_settings();
#endif
controller_reconfigure();
configfile_save(configfile_name());
DynOS_Opt_SaveConfig(sDynosMenu);
sCurrentMenu = NULL;
}
}
static void DynOS_Opt_ProcessInputs() {
static s32 sStickTimer = 0;
static bool sPrevStick = 0;
// Stick values
f32 _StickX = gPlayer1Controller->stickX;
f32 _StickY = gPlayer1Controller->stickY;
if (absx(_StickX) > 60 || absx(_StickY) > 60) {
if (sStickTimer == 0) {
sStickTimer = (sPrevStick ? 2 : 9);
} else {
_StickX = 0;
_StickY = 0;
sStickTimer--;
}
sPrevStick = true;
} else {
sStickTimer = 0;
sPrevStick = false;
}
// Key binding
if (sBindingState != 0) {
u32 _Key = (sCurrentOpt->mDynos ? (u32) DynOS_Opt_ControllerGetKeyPressed() : controller_get_raw_key());
if (_Key != VK_INVALID) {
play_sound(SOUND_DYNOS_SELECT, gDefaultSoundArgs);
sCurrentOpt->mBind.mBinds[sCurrentOpt->mBind.mIndex] = _Key;
sBindingState = false;
}
return;
}
if (sCurrentMenu != NULL) {
// Up
if (_StickY > +60) {
if (sCurrentOpt->mPrev != NULL) {
sCurrentOpt = sCurrentOpt->mPrev;
} else {
while (sCurrentOpt->mNext) sCurrentOpt = sCurrentOpt->mNext;
}
play_sound(SOUND_DYNOS_SELECT, gDefaultSoundArgs);
return;
}
// Down
if (_StickY < -60) {
if (sCurrentOpt->mNext != NULL) {
sCurrentOpt = sCurrentOpt->mNext;
} else {
while (sCurrentOpt->mPrev) sCurrentOpt = sCurrentOpt->mPrev;
}
play_sound(SOUND_DYNOS_SELECT, gDefaultSoundArgs);
return;
}
// Left
if (_StickX < -60) {
switch (DynOS_Opt_ProcessInput(sCurrentOpt, INPUT_LEFT)) {
case RESULT_OK: play_sound(SOUND_DYNOS_OK, gDefaultSoundArgs); break;
case RESULT_CANCEL: play_sound(SOUND_DYNOS_CANCEL, gDefaultSoundArgs); break;
case RESULT_NONE: break;
}
return;
}
// Right
if (_StickX > +60) {
switch (DynOS_Opt_ProcessInput(sCurrentOpt, INPUT_RIGHT)) {
case RESULT_OK: play_sound(SOUND_DYNOS_OK, gDefaultSoundArgs); break;
case RESULT_CANCEL: play_sound(SOUND_DYNOS_CANCEL, gDefaultSoundArgs); break;
case RESULT_NONE: break;
}
return;
}
// A
if (gPlayer1Controller->buttonPressed & A_BUTTON) {
switch (DynOS_Opt_ProcessInput(sCurrentOpt, INPUT_A)) {
case RESULT_OK: play_sound(SOUND_DYNOS_OK, gDefaultSoundArgs); break;
case RESULT_CANCEL: play_sound(SOUND_DYNOS_CANCEL, gDefaultSoundArgs); break;
case RESULT_NONE: break;
}
return;
}
// B
if (gPlayer1Controller->buttonPressed & B_BUTTON) {
if (sCurrentOpt->mParent != NULL) {
sCurrentOpt = sCurrentOpt->mParent;
play_sound(SOUND_DYNOS_SELECT, gDefaultSoundArgs);
} else {
DynOS_Opt_Close(true);
}
return;
}
// Z
if (gPlayer1Controller->buttonPressed & Z_TRIG) {
switch (DynOS_Opt_ProcessInput(sCurrentOpt, INPUT_Z)) {
case RESULT_OK: play_sound(SOUND_DYNOS_OK, gDefaultSoundArgs); break;
case RESULT_CANCEL: play_sound(SOUND_DYNOS_CANCEL, gDefaultSoundArgs); break;
case RESULT_NONE:
if (sCurrentMenu == sDynosMenu) {
DynOS_Opt_Close(true);
} else {
DynOS_Opt_Open(sDynosMenu);
} break;
}
return;
}
// R
if (gPlayer1Controller->buttonPressed & R_TRIG) {
if (sCurrentMenu == sOptionsMenu) {
DynOS_Opt_Close(true);
} else {
DynOS_Opt_Open(sOptionsMenu);
}
return;
}
// Start
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
DynOS_Opt_Close(true);
return;
}
} else if (gPlayer1Controller->buttonPressed & R_TRIG) {
DynOS_Opt_Open(sOptionsMenu);
} else if (gPlayer1Controller->buttonPressed & Z_TRIG) {
DynOS_Opt_Open(sDynosMenu);
}
}
//
// Init
//
static void DynOS_Opt_CreateWarpToLevelSubMenu() {
DynOS_Opt_CreateSubMenu("dynos_warp_to_level_submenu", "Warp to Level", "WARP TO LEUEL");
// Level select
{
DynosOption *aOpt = DynOS_Opt_NewOption("dynos_warp_level", "", "Level Select", "");
aOpt->mType = DOPT_CHOICELEVEL;
aOpt->mChoice.mIndex = New<s32>();
*aOpt->mChoice.mIndex = 0;
}
// Area select
{
DynosOption *aOpt = DynOS_Opt_NewOption("dynos_warp_area", "", "Area Select", "");
aOpt->mType = DOPT_CHOICEAREA;
aOpt->mChoice.mIndex = New<s32>();
*aOpt->mChoice.mIndex = 0;
}
// Star select
{
DynosOption *aOpt = DynOS_Opt_NewOption("dynos_warp_act", "", "Star Select", "");
aOpt->mType = DOPT_CHOICESTAR;
aOpt->mChoice.mIndex = New<s32>();
*aOpt->mChoice.mIndex = 0;
}
// Param select
{
DynosOption *aOpt = DynOS_Opt_NewOption("dynos_warp_param", "", "Param Select", "");
aOpt->mType = DOPT_CHOICEPARAM;
aOpt->mChoice.mIndex = New<s32>();
*aOpt->mChoice.mIndex = 0;
}
DynOS_Opt_CreateButton("dynos_warp_to_level", "Warp", "DynOS_Opt_WarpToLevel");
DynOS_Opt_EndSubMenu();
}
static void DynOS_Opt_CreateWarpToCastleSubMenu() {
DynOS_Opt_CreateSubMenu("dynos_warp_to_castle_submenu", "Warp to Castle", "WARP TO CASTLE");
// Level select
{
DynosOption *aOpt = DynOS_Opt_NewOption("dynos_warp_castle", "", "Level Exit", "");
aOpt->mType = DOPT_CHOICELEVEL;
aOpt->mChoice.mIndex = New<s32>();
*aOpt->mChoice.mIndex = 0;
}
DynOS_Opt_CreateButton("dynos_warp_to_castle", "Warp", "DynOS_Opt_WarpToCastle");
DynOS_Opt_EndSubMenu();
}
static u32 DynOS_Opt_GetHash(const String& aStr) {
u32 _Hash = 5381u;
for (char c : aStr) { _Hash += c + (_Hash << 5); }
return _Hash;
}
static void DynOS_Opt_CreateModelPacksSubMenu() {
Array<String> _Packs = DynOS_Gfx_Init();
if (_Packs.Count() == 0) {
return;
}
DynOS_Opt_CreateSubMenu("dynos_model_loader_submenu", "Model Packs", "MODEL PACKS");
for (s32 i = 0; i != _Packs.Count(); ++i) {
DynOS_Opt_CreateToggle(String("dynos_pack_%d", i), String("dynos_pack_%08X", DynOS_Opt_GetHash(_Packs[i])), _Packs[i], false);
}
DynOS_Opt_CreateButton("dynos_packs_disable_all", "Disable all packs", "DynOS_Opt_DisableAllPacks");
DynOS_Opt_EndSubMenu();
}
void DynOS_Opt_Init() {
#ifdef COOP
DynOS_Gfx_Init();
#else
// Convert options menu
DynOS_Opt_InitVanilla(sOptionsMenu);
// Warp to level
DynOS_Opt_CreateWarpToLevelSubMenu();
// Warp to castle
DynOS_Opt_CreateWarpToCastleSubMenu();
// Restart level
DynOS_Opt_CreateButton("dynos_restart_level", "Restart Level", "DynOS_Opt_RestartLevel");
// Exit level
DynOS_Opt_CreateButton("dynos_exit_level", "Exit Level", "DynOS_Opt_ExitLevel");
// Return to main menu
DynOS_Opt_CreateButton("dynos_return_to_main_menu", "Return to Main Menu", "DynOS_Opt_ReturnToMainMenu");
// Model loader
DynOS_Opt_CreateModelPacksSubMenu();
// Init config
DynOS_Opt_LoadConfig(sDynosMenu);
#endif
}
//
// Update
//
void DynOS_Opt_Update(OSContPad *aPad) {
DynOS_Opt_Loop(sDynosMenu, DynOS_Opt_ControllerUpdate, (void *) aPad);
if (DynOS_IsTransitionActive()) {
aPad->button = 0;
aPad->stick_x = 0;
aPad->stick_y = 0;
aPad->ext_stick_x = 0;
aPad->ext_stick_y = 0;
}
}
//
// Hijack
// This is C code
//
extern "C" {
u8 optmenu_open = 0;
void optmenu_toggle(void) {
DynOS_Opt_Close(false);
optmenu_open = 0;
}
void optmenu_draw(void) {
DynOS_Opt_DrawMenu(sCurrentOpt, sCurrentMenu, sOptionsMenu, sDynosMenu);
}
void optmenu_draw_prompt(void) {
DynOS_Opt_DrawPrompt(sCurrentMenu, sOptionsMenu, sDynosMenu);
DynOS_Opt_ProcessInputs();
optmenu_open = (sCurrentMenu != NULL);
}
void optmenu_check_buttons(void) {
}
}
//
// Built-in options
//
#define DYNOS_DEFINE_ACTION(func) \
DYNOS_AT_STARTUP static void DynOS_Opt_AddAction_##func() { \
DynOS_Opt_AddAction(#func, func, false); \
}
#ifndef COOP
static bool DynOS_Opt_ReturnToMainMenu(UNUSED const char *optName) {
DynOS_ReturnToMainMenu();
return true;
}
DYNOS_DEFINE_ACTION(DynOS_Opt_ReturnToMainMenu);
static bool DynOS_Opt_WarpToLevel(UNUSED const char *optName) {
s32 _Level = DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")];
s32 _Area = DynOS_Opt_GetValue("dynos_warp_area") + 1;
s32 _Act = DynOS_Opt_GetValue("dynos_warp_act") + 1;
return DynOS_Warp_ToLevel(_Level, _Area, _Act);
}
DYNOS_DEFINE_ACTION(DynOS_Opt_WarpToLevel);
static bool DynOS_Opt_WarpToCastle(UNUSED const char *optName) {
s32 _Level = DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_castle")];
return DynOS_Warp_ToCastle(_Level);
}
DYNOS_DEFINE_ACTION(DynOS_Opt_WarpToCastle);
static bool DynOS_Opt_RestartLevel(UNUSED const char *optName) {
return DynOS_Warp_RestartLevel();
}
DYNOS_DEFINE_ACTION(DynOS_Opt_RestartLevel);
static bool DynOS_Opt_ExitLevel(UNUSED const char *optName) {
return DynOS_Warp_ExitLevel(30);
}
DYNOS_DEFINE_ACTION(DynOS_Opt_ExitLevel);
static bool DynOS_Opt_DisableAllPacks(UNUSED const char *optName) {
const Array<PackData *> &pDynosPacks = DynOS_Gfx_GetPacks();
for (s32 i = 0; i != pDynosPacks.Count(); ++i) {
DynOS_Opt_SetValue(String("dynos_pack_%d", i), false);
}
return true;
}
DYNOS_DEFINE_ACTION(DynOS_Opt_DisableAllPacks);
#endif
#undef DYNOS_DEFINE_ACTION

62
data/dynos_opt_config.cpp Normal file
View file

@ -0,0 +1,62 @@
#include "dynos.cpp.h"
extern DynosOption *DynOS_Opt_Loop(DynosOption *aOpt, DynosLoopFunc aFunc, void *aData);
static bool DynOS_Opt_ReadConfig(DynosOption *aOpt, void *aData) {
return (aOpt->mConfigName == (const char *) aData);
}
void DynOS_Opt_LoadConfig(DynosOption *aMenu) {
SysPath _Filename = fstring("%s/%s", DYNOS_USER_FOLDER, DYNOS_CONFIG_FILENAME);
FILE *_File = fopen(_Filename.c_str(), "r");
if (_File) {
char _Buffer[1024];
while (fgets(_Buffer, 1024, _File)) {
// Option strings
char *_NameBegin = _Buffer;
char *_DataBegin = strchr(_NameBegin, '=');
if (_NameBegin && _DataBegin) {
*(_DataBegin++) = 0;
// Option name
String _OptName = String(_NameBegin);
DynosOption *_Opt = DynOS_Opt_Loop(aMenu, DynOS_Opt_ReadConfig, (void *) _OptName.begin());
if (_Opt) {
// Option values
switch (_Opt->mType) {
case DOPT_TOGGLE: sscanf(_DataBegin, "%hhu\n", &_Opt->mToggle.mTog[0]); break;
case DOPT_CHOICE: sscanf(_DataBegin, "%d\n", &_Opt->mChoice.mIndex[0]); break;
case DOPT_SCROLL: sscanf(_DataBegin, "%d\n", &_Opt->mScroll.mValue[0]); break;
case DOPT_BIND: sscanf(_DataBegin, "%04X;%04X;%04X\n", &_Opt->mBind.mBinds[0], &_Opt->mBind.mBinds[1], &_Opt->mBind.mBinds[2]); break;
}
}
}
}
fclose(_File);
}
}
static bool DynOS_Opt_WriteConfig(DynosOption *aOpt, void *aData) {
if (aOpt->mConfigName.Length() != 0 &&
aOpt->mConfigName != "null" &&
aOpt->mConfigName != "NULL") {
switch (aOpt->mType) {
case DOPT_TOGGLE: fprintf((FILE *) aData, "%s=%hhu\n", aOpt->mConfigName.begin(), aOpt->mToggle.mTog[0]); break;
case DOPT_CHOICE: fprintf((FILE *) aData, "%s=%d\n", aOpt->mConfigName.begin(), aOpt->mChoice.mIndex[0]); break;
case DOPT_SCROLL: fprintf((FILE *) aData, "%s=%d\n", aOpt->mConfigName.begin(), aOpt->mScroll.mValue[0]); break;
case DOPT_BIND: fprintf((FILE *) aData, "%s=%04X;%04X;%04X\n", aOpt->mConfigName.begin(), aOpt->mBind.mBinds[0], aOpt->mBind.mBinds[1], aOpt->mBind.mBinds[2]); break;
}
}
return false;
}
void DynOS_Opt_SaveConfig(DynosOption *aMenu) {
SysPath _Filename = fstring("%s/%s", DYNOS_USER_FOLDER, DYNOS_CONFIG_FILENAME);
FILE *_File = fopen(_Filename.c_str(), "w");
if (_File) {
DynOS_Opt_Loop(aMenu, DynOS_Opt_WriteConfig, (void *) _File);
fclose(_File);
}
}

70
data/dynos_opt_cont.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "dynos.cpp.h"
extern "C" {
#include "pc/controller/controller_api.h"
}
static bool DynOS_Opt_ControllerIsKeyDown(s32 aCont, s32 aKey) {
// Keyboard
if (aCont == 0 && aKey >= 0 && aKey < SDL_NUM_SCANCODES) {
return SDL_GetKeyboardState(NULL)[aKey];
}
// Game Controller
else if (aKey >= 0x1000) {
// Button
s32 _Button = (aKey - 0x1000);
if (_Button < SDL_CONTROLLER_BUTTON_MAX) {
return SDL_GameControllerGetButton(SDL_GameControllerOpen(aCont - 1), SDL_GameControllerButton(_Button));
}
// Axis
s32 _Axis = (aKey - 0x1000 - SDL_CONTROLLER_BUTTON_MAX);
if (_Axis < SDL_CONTROLLER_AXIS_MAX * 2) {
s32 _AxisValue = SDL_GameControllerGetAxis(SDL_GameControllerOpen(aCont - 1), SDL_GameControllerAxis(_Axis / 2));
if (_Axis & 1) return (_AxisValue < (SHRT_MIN / 2));
else return (_AxisValue > (SHRT_MAX / 2));
}
}
// Invalid
return false;
}
#define MAX_CONTS 8
bool DynOS_Opt_ControllerUpdate(DynosOption *aOpt, void *aData) {
if (aOpt->mType == DOPT_BIND) {
OSContPad *pad = (OSContPad *) aData;
for (s32 _Cont = 0; _Cont < MAX_CONTS; ++_Cont)
for (s32 _Bind = 0; _Bind < 3; ++_Bind) {
pad->button |= aOpt->mBind.mMask * DynOS_Opt_ControllerIsKeyDown(_Cont, aOpt->mBind.mBinds[_Bind]);
}
}
return false;
}
#define MAX_GKEYS (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_MAX * 2)
s32 sBindingState = 0; // 0 = No bind, 1 = Wait for all keys released, 2 = Return first pressed key
s32 DynOS_Opt_ControllerGetKeyPressed() {
// Keyboard
for (s32 _Key = 0; _Key < SDL_NUM_SCANCODES; ++_Key) {
if (DynOS_Opt_ControllerIsKeyDown(0, _Key)) {
if (sBindingState == 1) return VK_INVALID;
return _Key;
}
}
// Game Controller
for (s32 _Cont = 1; _Cont < MAX_CONTS; ++_Cont)
for (s32 _Key = 0; _Key < MAX_GKEYS; ++_Key) {
if (DynOS_Opt_ControllerIsKeyDown(_Cont, _Key + 0x1000)) {
if (sBindingState == 1) return VK_INVALID;
return _Key + 0x1000;
}
}
// No key
sBindingState = 2;
return VK_INVALID;
}

309
data/dynos_opt_render.cpp Normal file
View file

@ -0,0 +1,309 @@
#include "dynos.cpp.h"
extern "C" {
#include "course_table.h"
#include "game/game_init.h"
#include "game/ingame_menu.h"
#include "game/segment2.h"
#include "pc/controller/controller_api.h"
#include "gfx_dimensions.h"
}
extern s32 sBindingState;
#define DYNOS_TEXT_DYNOS_MENU { "DYNOS MENU", NULL }
#define DYNOS_TEXT_A { "([A]) >", NULL }
#define DYNOS_TEXT_OPEN_LEFT { "[Z] DynOS", NULL }
#define DYNOS_TEXT_CLOSE_LEFT { "[Z] Return", NULL }
#define DYNOS_TEXT_OPTIONS_MENU { "OPTIONS", NULL }
#define DYNOS_TEXT_DISABLED { "Disabled", NULL }
#define DYNOS_TEXT_ENABLED { "Enabled", NULL }
#define DYNOS_TEXT_NONE { "NONE", NULL }
#define DYNOS_TEXT_DOT_DOT_DOT { "...", NULL }
#define DYNOS_TEXT_OPEN_RIGHT { "[R] Options", NULL }
#define DYNOS_TEXT_CLOSE_RIGHT { "[R] Return", NULL }
static void RenderString(const u8 *aStr64, s32 aX, s32 aY) {
create_dl_translation_matrix(MENU_MTX_PUSH, aX, aY, 0);
for (; *aStr64 != DIALOG_CHAR_TERMINATOR; ++aStr64) {
if (*aStr64 != DIALOG_CHAR_SPACE) {
void **fontLUT = (void **) segmented_to_virtual(main_font_lut);
void *packedTexture = segmented_to_virtual(fontLUT[*aStr64]);
gDPPipeSync(gDisplayListHead++);
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture));
gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
}
create_dl_translation_matrix(MENU_MTX_NOPUSH, DynOS_String_WidthChar64(*aStr64), 0, 0);
}
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
}
static void PrintString(const Label& aLabel, s32 aX, s32 aY, u32 aFrontColorRGBA, u32 aBackColorRGBA, bool aAlignLeft) {
const u8 *_Str64 = (aLabel.second ? aLabel.second : DynOS_String_Convert(aLabel.first.begin(), false));
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
if ((aBackColorRGBA & 0xFF) != 0) {
gDPSetEnvColor(gDisplayListHead++, ((aBackColorRGBA >> 24) & 0xFF), ((aBackColorRGBA >> 16) & 0xFF), ((aBackColorRGBA >> 8) & 0xFF), ((aBackColorRGBA >> 0) & 0xFF));
if (aAlignLeft) {
RenderString(_Str64, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX) + 1, aY - 1);
} else {
RenderString(_Str64, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + DynOS_String_Width(_Str64) - 1), aY - 1);
}
}
if ((aFrontColorRGBA & 0xFF) != 0) {
gDPSetEnvColor(gDisplayListHead++, ((aFrontColorRGBA >> 24) & 0xFF), ((aFrontColorRGBA >> 16) & 0xFF), ((aFrontColorRGBA >> 8) & 0xFF), ((aFrontColorRGBA >> 0) & 0xFF));
if (aAlignLeft) {
RenderString(_Str64, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX), aY);
} else {
RenderString(_Str64, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + DynOS_String_Width(_Str64)), aY);
}
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
static void PrintBox(s32 aX, s32 aY, s32 aWidth, s32 aHeight, u32 aColorRGBA, bool aAlignLeft) {
if ((aColorRGBA && 0xFF) != 0) {
Mtx *_Matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
if (!_Matrix) return;
if (aAlignLeft) {
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(aX), aY + aHeight, 0);
} else {
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_RIGHT_EDGE(aX + aWidth), aY + aHeight, 0);
}
guScale(_Matrix, (f32) aWidth / 130.f, (f32) aHeight / 80.f, 1.f);
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(_Matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
gDPSetEnvColor(gDisplayListHead++, ((aColorRGBA >> 24) & 0xFF), ((aColorRGBA >> 16) & 0xFF), ((aColorRGBA >> 8) & 0xFF), ((aColorRGBA >> 0) & 0xFF));
gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
}
static const char *IntToString(const char *fmt, s32 x) {
static char sBuffer[16];
snprintf(sBuffer, 16, fmt, x);
return sBuffer;
}
#define get_label(opt) (opt->mLabel)
#define get_title(opt) (opt->mTitle)
#define get_choice(opt) (opt->mChoice.mChoices[*opt->mChoice.mIndex])
#define get_dec_number(n) { "", DynOS_String_Convert(IntToString("%d", n), false) }
#define get_hex_number(n) { "", DynOS_String_Convert(IntToString("%04X", n), false) }
#define get_level(opt) { "", DynOS_Level_GetName(DynOS_Level_GetList()[*opt->mChoice.mIndex], true, true) }
#define get_star(opt) { "", DynOS_Level_GetActName(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")], *opt->mChoice.mIndex + 1, true, true) }
#define get_param(opt) { DynOS_Warp_GetParamName(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")], *opt->mChoice.mIndex), NULL }
static s32 GetCurrentOptionCount(DynosOption *aCurrentOpt) {
s32 _Count = 0;
while (aCurrentOpt->mPrev) { aCurrentOpt = aCurrentOpt->mPrev; }
while (aCurrentOpt) { aCurrentOpt = aCurrentOpt->mNext; _Count++; }
return _Count;
}
static s32 GetCurrentOptionIndex(DynosOption *aCurrentOpt) {
s32 _Index = 0;
while (aCurrentOpt->mPrev) { aCurrentOpt = aCurrentOpt->mPrev; _Index++; }
return _Index;
}
#define PREV(opt) (opt == NULL ? NULL : opt->mPrev)
#define NEXT(opt) (opt == NULL ? NULL : opt->mNext)
static DynosOption **GetCurrentOptions(DynosOption *aCurrentOpt) {
static DynosOption *sOptionList[13];
sOptionList[6] = aCurrentOpt;
sOptionList[5] = PREV(sOptionList[6]);
sOptionList[4] = PREV(sOptionList[5]);
sOptionList[3] = PREV(sOptionList[4]);
sOptionList[2] = PREV(sOptionList[3]);
sOptionList[1] = PREV(sOptionList[2]);
sOptionList[0] = PREV(sOptionList[1]);
sOptionList[7] = NEXT(sOptionList[6]);
sOptionList[8] = NEXT(sOptionList[7]);
sOptionList[9] = NEXT(sOptionList[8]);
sOptionList[10] = NEXT(sOptionList[9]);
sOptionList[11] = NEXT(sOptionList[10]);
sOptionList[12] = NEXT(sOptionList[11]);
s32 _StartIndex = 12, _EndIndex = 0;
for (s32 i = 0; i != 13; ++i) {
if (sOptionList[i] != NULL) {
_StartIndex = MIN(_StartIndex, i);
_EndIndex = MAX(_EndIndex, i);
}
}
if (_EndIndex - _StartIndex < 7) {
return &sOptionList[_StartIndex];
}
if (_EndIndex <= 9) {
return &sOptionList[_EndIndex - 6];
}
if (_StartIndex >= 3) {
return &sOptionList[_StartIndex];
}
return &sOptionList[3];
}
#undef PREV
#undef NEXT
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_BLACK 0x000000FF
#define COLOR_GRAY 0xA0A0A0FF
#define COLOR_DARK_GRAY 0x808080FF
#define COLOR_SELECT 0x80E0FFFF
#define COLOR_SELECT_BOX 0x00FFFF20
#define COLOR_ENABLED 0x20E020FF
#define COLOR_DISABLED 0xFF2020FF
#define OFFSET_FROM_LEFT_EDGE (20.f * sqr(GFX_DIMENSIONS_ASPECT_RATIO))
#define OFFSET_FROM_RIGHT_EDGE (20.f * sqr(GFX_DIMENSIONS_ASPECT_RATIO))
#define SCROLL_BAR_SIZE ((s32) (45.f * GFX_DIMENSIONS_ASPECT_RATIO))
static void DynOS_Opt_DrawOption(DynosOption *aOpt, DynosOption *aCurrentOpt, s32 aY) {
if (aOpt == NULL) {
return;
}
// Selected box
if (aOpt == aCurrentOpt) {
u8 _Alpha = (u8) ((coss(gGlobalTimer * 0x800) + 1.f) * 0x20);
PrintBox(OFFSET_FROM_LEFT_EDGE - 4, aY - 2, GFX_DIMENSIONS_FROM_RIGHT_EDGE(OFFSET_FROM_RIGHT_EDGE) - GFX_DIMENSIONS_FROM_LEFT_EDGE(OFFSET_FROM_LEFT_EDGE) + 8, 20, COLOR_SELECT_BOX + _Alpha, 1);
}
// Label
if (aOpt == aCurrentOpt) {
PrintString(get_label(aOpt), OFFSET_FROM_LEFT_EDGE, aY, COLOR_SELECT, COLOR_BLACK, 1);
} else {
PrintString(get_label(aOpt), OFFSET_FROM_LEFT_EDGE, aY, COLOR_WHITE, COLOR_BLACK, 1);
}
// Values
switch (aOpt->mType) {
case DOPT_TOGGLE: {
if (*aOpt->mToggle.mTog) {
PrintString(DYNOS_TEXT_ENABLED, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_ENABLED, COLOR_BLACK, 0);
} else {
PrintString(DYNOS_TEXT_DISABLED, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_DISABLED, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICE: {
PrintString(get_choice(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_CHOICELEVEL: {
PrintString(get_level(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_CHOICEAREA: {
s32 _Level = DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")];
s32 _Area = *aOpt->mChoice.mIndex + 1;
const u8 *_Name = DynOS_Level_GetAreaName(_Level, _Area, true);
if (DynOS_Level_GetWarpEntry(_Level, _Area)) {
PrintString({ "", _Name }, OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} else {
PrintString({ "", _Name }, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_GRAY, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICESTAR: {
s32 _Course = DynOS_Level_GetCourse(DynOS_Level_GetList()[DynOS_Opt_GetValue("dynos_warp_level")]);
if (_Course >= COURSE_MIN && _Course <= COURSE_STAGES_MAX) {
PrintString(get_star(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
}
} break;
case DOPT_CHOICEPARAM: {
PrintString(get_param(aOpt), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
} break;
case DOPT_SCROLL: {
s32 _Width = (s32) (SCROLL_BAR_SIZE * (f32) (*aOpt->mScroll.mValue - aOpt->mScroll.mMin) / (f32) (aOpt->mScroll.mMax - aOpt->mScroll.mMin));
PrintString(get_dec_number(*aOpt->mScroll.mValue), OFFSET_FROM_RIGHT_EDGE, aY, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, COLOR_BLACK, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE + 28, aY + 4, SCROLL_BAR_SIZE + 2, 8, COLOR_DARK_GRAY, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE + 29 + SCROLL_BAR_SIZE - _Width, aY + 5, _Width, 6, aOpt == aCurrentOpt ? COLOR_SELECT : COLOR_WHITE, 0);
} break;
case DOPT_BIND: {
for (s32 i = 0; i != 3; ++i) {
u32 _Bind = aOpt->mBind.mBinds[i];
if (aOpt == aCurrentOpt && i == aOpt->mBind.mIndex) {
if (sBindingState != 0) {
PrintString(DYNOS_TEXT_DOT_DOT_DOT, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
} else if (_Bind == VK_INVALID) {
PrintString(DYNOS_TEXT_NONE, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
} else {
PrintString(get_hex_number(_Bind), OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_SELECT, COLOR_BLACK, 0);
}
} else {
if (_Bind == VK_INVALID) {
PrintString(DYNOS_TEXT_NONE, OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_GRAY, COLOR_BLACK, 0);
} else {
PrintString(get_hex_number(_Bind), OFFSET_FROM_RIGHT_EDGE + (2 - i) * 36, aY, COLOR_WHITE, COLOR_BLACK, 0);
}
}
}
} break;
case DOPT_BUTTON: {
} break;
case DOPT_SUBMENU: {
if (aOpt == aCurrentOpt) {
PrintString(DYNOS_TEXT_A, OFFSET_FROM_RIGHT_EDGE, aY, COLOR_SELECT, COLOR_BLACK, 0);
}
} break;
}
}
void DynOS_Opt_DrawMenu(DynosOption *aCurrentOption, DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu) {
if (aCurrentMenu == NULL) {
return;
}
// Colorful label
Label _Title;
if (aCurrentOption->mParent) {
_Title = get_title(aCurrentOption->mParent);
} else if (aCurrentMenu == aDynosMenu) {
_Title = DYNOS_TEXT_DYNOS_MENU;
} else if (aCurrentMenu == aOptionsMenu) {
_Title = DYNOS_TEXT_OPTIONS_MENU;
}
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
if (!_Title.second) _Title.second = DynOS_String_Convert(_Title.first.begin(), false);
print_hud_lut_string(HUD_LUT_GLOBAL, (SCREEN_WIDTH / 2 - DynOS_String_Length(_Title.second) * 6), 40, _Title.second);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
// Display options
DynosOption **_Options = GetCurrentOptions(aCurrentOption);
for (s32 i = 0; i != 7; ++i) {
DynOS_Opt_DrawOption(_Options[i], aCurrentOption, 156 - 20 * i);
}
// Scroll bar
s32 _OptCount = GetCurrentOptionCount(aCurrentOption);
s32 _OptIndex = GetCurrentOptionIndex(aCurrentOption);
if (_OptCount > 7) {
s32 _Height = (s32) (134.f * sqrtf(1.f / (_OptCount - 6)));
s32 _Y = 37 + (134 - _Height) * (1.f - MAX(0.f, MIN(1.f, (f32)(_OptIndex - 3) / (f32)(_OptCount - 6))));
PrintBox(OFFSET_FROM_RIGHT_EDGE - 16, 36, 8, 136, COLOR_DARK_GRAY, 0);
PrintBox(OFFSET_FROM_RIGHT_EDGE - 15, _Y, 6, _Height, COLOR_WHITE, 0);
}
}
#define PROMPT_OFFSET (56.25f * GFX_DIMENSIONS_ASPECT_RATIO)
void DynOS_Opt_DrawPrompt(DynosOption *aCurrentMenu, DynosOption *aOptionsMenu, DynosOption *aDynosMenu) {
if (aCurrentMenu == aOptionsMenu) {
PrintString(DYNOS_TEXT_OPEN_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_CLOSE_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
} else if (aCurrentMenu == aDynosMenu) {
PrintString(DYNOS_TEXT_CLOSE_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_OPEN_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
} else {
PrintString(DYNOS_TEXT_OPEN_LEFT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 1);
PrintString(DYNOS_TEXT_OPEN_RIGHT, PROMPT_OFFSET, 212, COLOR_WHITE, COLOR_BLACK, 0);
}
}

159
data/dynos_opt_vanilla.cpp Normal file
View file

@ -0,0 +1,159 @@
#include "dynos.cpp.h"
static DynosOption *sPrevOpt = NULL;
static DynosOption *sOptionsMenu = NULL;
//
// Vanilla actions
//
typedef void (*VanillaActionFunction)(struct Option *, s32);
typedef struct VanillaAction {
String mFuncName;
VanillaActionFunction mAction;
} VanillaAction;
STATIC_STORAGE(Array<VanillaAction *>, VanillaActions);
#define sVanillaActions __VanillaActions()
static VanillaActionFunction DynOS_Opt_GetVanillaAction(const String& aFuncName) {
for (auto &_DynosAction : sVanillaActions) {
if (_DynosAction->mFuncName == aFuncName) {
return _DynosAction->mAction;
}
}
return NULL;
}
static void DynOS_Opt_AddVanillaAction(const String& aFuncName, void (*aFuncPtr)(struct Option *, s32)) {
for (auto &_DynosAction : sVanillaActions) {
if (_DynosAction->mFuncName == aFuncName) {
return;
}
}
VanillaAction *_DynosAction = New<VanillaAction>();
_DynosAction->mFuncName = aFuncName;
_DynosAction->mAction = aFuncPtr;
sVanillaActions.Add(_DynosAction);
}
static bool DynOS_Opt_CallVanillaAction(const char *aOptName) {
VanillaActionFunction _Func = DynOS_Opt_GetVanillaAction(aOptName);
if (_Func) {
_Func(NULL, 0);
return true;
}
return false;
}
//
// Convert classic options menu into DynOS menu
//
static DynosOption *DynOS_Opt_ConvertOption(const u8 *aLabel, const u8 *aTitle) {
static u32 sOptIdx = 0;
DynosOption *_Opt = New<DynosOption>();
_Opt->mName = String("vanilla_opt_%08X", sOptIdx++);
_Opt->mConfigName = "";
_Opt->mLabel = { "", aLabel };
_Opt->mTitle = { "", aTitle };
_Opt->mDynos = false;
if (sPrevOpt == NULL) { // The very first option
_Opt->mPrev = NULL;
_Opt->mNext = NULL;
_Opt->mParent = NULL;
sOptionsMenu = _Opt;
} else {
if (sPrevOpt->mType == DOPT_SUBMENU && sPrevOpt->mSubMenu.mEmpty) { // First option of a sub-menu
_Opt->mPrev = NULL;
_Opt->mNext = NULL;
_Opt->mParent = sPrevOpt;
sPrevOpt->mSubMenu.mChild = _Opt;
sPrevOpt->mSubMenu.mEmpty = false;
} else {
_Opt->mPrev = sPrevOpt;
_Opt->mNext = NULL;
_Opt->mParent = sPrevOpt->mParent;
sPrevOpt->mNext = _Opt;
}
}
sPrevOpt = _Opt;
return _Opt;
}
static void DynOS_Opt_EndSubMenu() {
if (sPrevOpt) {
if (sPrevOpt->mType == DOPT_SUBMENU && sPrevOpt->mSubMenu.mEmpty) { // ENDMENU command following a SUBMENU command
sPrevOpt->mSubMenu.mEmpty = false;
} else {
sPrevOpt = sPrevOpt->mParent;
}
}
}
static void DynOS_Opt_ConvertSubMenu(const u8 *aLabel, const u8 *aTitle) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aTitle);
_Opt->mType = DOPT_SUBMENU;
_Opt->mSubMenu.mChild = NULL;
_Opt->mSubMenu.mEmpty = true;
}
static void DynOS_Opt_ConvertToggle(const u8 *aLabel, bool *pValue) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aLabel);
_Opt->mType = DOPT_TOGGLE;
_Opt->mToggle.mTog = (bool *) pValue;
}
static void DynOS_Opt_ConvertScroll(const u8 *aLabel, s32 aMin, s32 aMax, s32 aStep, u32 *pValue) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aLabel);
_Opt->mType = DOPT_SCROLL;
_Opt->mScroll.mMin = aMin;
_Opt->mScroll.mMax = aMax;
_Opt->mScroll.mStep = aStep;
_Opt->mScroll.mValue = (s32 *) pValue;
}
static void DynOS_Opt_ConvertChoice(const u8 *aLabel, const u8 **aChoices, s32 aCount, u32 *pValue) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aLabel);
_Opt->mType = DOPT_CHOICE;
_Opt->mChoice.mIndex = (s32 *) pValue;
for (s32 i = 0; i != aCount; ++i) {
_Opt->mChoice.mChoices.Add({ "", aChoices[i] });
}
}
static void DynOS_Opt_ConvertButton(const u8 *aLabel, VanillaActionFunction aAction) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aLabel);
_Opt->mType = DOPT_BUTTON;
_Opt->mButton.mFuncName = "DynOS_Opt_CallVanillaAction";
DynOS_Opt_AddVanillaAction(_Opt->mName, aAction);
}
static void DynOS_Opt_ConvertBind(const u8 *aLabel, u32 *pBinds) {
DynosOption *_Opt = DynOS_Opt_ConvertOption(aLabel, aLabel);
_Opt->mType = DOPT_BIND;
_Opt->mBind.mMask = 0;
_Opt->mBind.mBinds = pBinds;
_Opt->mBind.mIndex = 0;
}
#ifndef COOP
extern "C" {
extern void dynos_opt_convert_vanilla_main_menu();
void dynos_opt_end_submenu() { return DynOS_Opt_EndSubMenu(); }
void dynos_opt_convert_submenu(const u8 *label, const u8 *title) { return DynOS_Opt_ConvertSubMenu(label, title); }
void dynos_opt_convert_toggle(const u8 *label, bool *bval) { return DynOS_Opt_ConvertToggle(label, bval); }
void dynos_opt_convert_scroll(const u8 *label, s32 min, s32 max, s32 step, u32 *uval) { return DynOS_Opt_ConvertScroll(label, min, max, step, uval); }
void dynos_opt_convert_choice(const u8 *label, const u8 **choices, s32 numChoices, u32 *uval) { return DynOS_Opt_ConvertChoice(label, choices, numChoices, uval); }
void dynos_opt_convert_button(const u8 *label, void *action) { return DynOS_Opt_ConvertButton(label, (VanillaActionFunction) action); }
void dynos_opt_convert_bind(const u8 *label, u32 *uval) { return DynOS_Opt_ConvertBind(label, uval); }
}
#endif
void DynOS_Opt_InitVanilla(DynosOption *&aOptionsMenu) {
sPrevOpt = NULL;
#ifndef COOP
dynos_opt_convert_vanilla_main_menu();
#endif
DynOS_Opt_AddAction("DynOS_Opt_CallVanillaAction", DynOS_Opt_CallVanillaAction, true);
aOptionsMenu = sOptionsMenu;
}

View file

@ -0,0 +1,72 @@
#ifndef COOP
// Not my problem
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsizeof-pointer-div"
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
#pragma GCC diagnostic ignored "-Wpointer-sign"
#pragma GCC diagnostic ignored "-Wsign-compare"
#define optmenu_toggle optmenu_toggle_unused
#define optmenu_draw optmenu_draw_unused
#define optmenu_draw_prompt optmenu_draw_prompt_unused
#define optmenu_check_buttons optmenu_check_buttons_unused
#define optmenu_open optmenu_open_unused
#define DYNOS_INL
#include "game/options_menu.c"
#undef DYNOS_INL
#undef optmenu_toggle
#undef optmenu_draw
#undef optmenu_draw_prompt
#undef optmenu_check_buttons
#undef optmenu_open
#pragma GCC diagnostic pop
// Now, that's my problem
extern void dynos_opt_end_submenu();
extern void dynos_opt_convert_submenu(const u8 *label, const u8 *title);
extern void dynos_opt_convert_toggle(const u8 *label, bool *bval);
extern void dynos_opt_convert_scroll(const u8 *label, s32 min, s32 max, s32 step, u32 *uval);
extern void dynos_opt_convert_choice(const u8 *label, const u8 **choices, s32 numChoices, u32 *uval);
extern void dynos_opt_convert_button(const u8 *label, void *action);
extern void dynos_opt_convert_bind(const u8 *label, u32 *uval);
static void dynos_opt_convert_menu(struct SubMenu *submenu) {
for (s32 i = 0; i != submenu->numOpts; ++i) {
struct Option *opt = &submenu->opts[i];
switch (opt->type) {
case OPT_TOGGLE:
dynos_opt_convert_toggle(opt->label, opt->bval);
break;
case OPT_CHOICE:
dynos_opt_convert_choice(opt->label, opt->choices, opt->numChoices, opt->uval);
break;
case OPT_SCROLL:
dynos_opt_convert_scroll(opt->label, opt->scrMin, opt->scrMax, opt->scrStep, opt->uval);
break;
case OPT_SUBMENU:
dynos_opt_convert_submenu(opt->label, opt->nextMenu->label);
dynos_opt_convert_menu(opt->nextMenu);
dynos_opt_end_submenu();
break;
case OPT_BIND:
dynos_opt_convert_bind(opt->label, opt->uval);
break;
case OPT_BUTTON:
dynos_opt_convert_button(opt->label, opt->actionFn);
break;
default:
break;
}
}
}
void dynos_opt_convert_vanilla_main_menu() {
dynos_opt_convert_menu(&menuMain);
}
#endif

471
data/dynos_warps.cpp Normal file
View file

@ -0,0 +1,471 @@
#include "dynos.cpp.h"
extern "C" {
#include "sm64.h"
#include "seq_ids.h"
#include "course_table.h"
#include "audio/external.h"
#include "engine/surface_collision.h"
#include "game/mario.h"
#include "game/ingame_menu.h"
#include "game/level_update.h"
#include "game/sound_init.h"
#include "game/object_list_processor.h"
#include "game/options_menu.h"
extern s8 gDialogBoxState;
extern s16 gMenuMode;
extern s32 gWdwWaterLevelSet;
extern u8 sSpawnTypeFromWarpBhv[];
extern void set_mario_initial_action(struct MarioState *, u32, u32);
extern void set_play_mode(s16);
}
//
// Data
//
s32 gDDDBowsersSub = -1;
s32 gDDDPoles = -1;
static s32 sDynosWarpLevelNum = -1;
static s32 sDynosWarpAreaNum = -1;
static s32 sDynosWarpActNum = -1;
static s32 sDynosExitLevelNum = -1;
static s32 sDynosExitAreaNum = -1;
//
// Level Entry
//
bool DynOS_Warp_ToLevel(s32 aLevel, s32 aArea, s32 aAct) {
if (DynOS_Level_GetCourse(aLevel) == COURSE_NONE || !DynOS_Level_GetWarpEntry(aLevel, aArea)) {
return false;
}
sDynosWarpLevelNum = aLevel;
sDynosWarpAreaNum = aArea;
sDynosWarpActNum = aAct;
return true;
}
bool DynOS_Warp_RestartLevel() {
return DynOS_Warp_ToLevel(gCurrLevelNum, 1, gCurrActNum);
}
//
// Level Exit
//
bool DynOS_Warp_ExitLevel(s32 aDelay) {
if (DynOS_Level_GetCourse(gCurrLevelNum) == COURSE_NONE) {
return false;
}
// Close the pause menu if it was open
optmenu_toggle();
level_set_transition(0, NULL);
gDialogBoxState = 0;
gMenuMode = -1;
// Cancel out every music/sound/sequence
for (u16 seqid = 0; seqid != SEQ_COUNT; ++seqid) {
stop_background_music(seqid);
}
play_shell_music();
stop_shell_music();
stop_cap_music();
func_80321080(0);
fadeout_music(0);
fadeout_level_music(0);
// Play Mario head transition, and change play mode to avoid getting stuck on the pause menu
aDelay = MAX(1, aDelay);
gMarioState->invincTimer = -1;
play_transition(WARP_TRANSITION_FADE_INTO_MARIO, aDelay, 0x00, 0x00, 0x00);
set_play_mode(0);
sDynosExitLevelNum = gCurrLevelNum;
sDynosExitAreaNum = gCurrAreaIndex;
return true;
}
bool DynOS_Warp_ToCastle(s32 aLevel) {
if (DynOS_Level_GetCourse(aLevel) == COURSE_NONE) {
return false;
}
// Close the pause menu if it was open
optmenu_toggle();
level_set_transition(0, NULL);
gDialogBoxState = 0;
gMenuMode = -1;
// Cancel out every music/sound/sequence
for (u16 seqid = 0; seqid != SEQ_COUNT; ++seqid) {
stop_background_music(seqid);
}
play_shell_music();
stop_shell_music();
stop_cap_music();
func_80321080(0);
fadeout_music(0);
fadeout_level_music(0);
// Change play mode to avoid getting stuck on the pause menu
set_play_mode(0);
sDynosExitLevelNum = aLevel;
sDynosExitAreaNum = 1;
return true;
}
//
// Params
//
const char *DynOS_Warp_GetParamName(s32 aLevel, s32 aIndex) {
static const char *sLevelParams[][5] = {
{ "", "", "", "", "" },
{ "None", "No Submarine, No Poles", "Submarine Only", "Poles Only", "Submarine And Poles" },
{ "None", "Water Level: Lowest", "Water Level: Low", "Water Level: High", "Water Level: Highest" },
{ "None", "Top Flooded", "Top Drained", "Top Flooded", "Top Drained" },
{ "None", "Clock Speed: Stopped", "Clock Speed: Slow", "Clock Speed: Fast", "Clock Speed: Random" },
};
switch (aLevel) {
case LEVEL_DDD: return sLevelParams[1][MIN(4, aIndex)];
case LEVEL_WDW: return sLevelParams[2][MIN(4, aIndex)];
case LEVEL_THI: return sLevelParams[3][MIN(4, aIndex)];
case LEVEL_TTC: return sLevelParams[4][MIN(4, aIndex)];
}
return sLevelParams[0][MIN(4, aIndex)];
}
// Called thrice
// Pass -1 to use the previous value (only once)
void DynOS_Warp_SetParam(s32 aLevel, s32 aIndex) {
static s32 sDynosWarpPrevParamIndex = -1;
if (aIndex == -1) {
aIndex = sDynosWarpPrevParamIndex;
sDynosWarpPrevParamIndex = -1;
} else {
sDynosWarpPrevParamIndex = aIndex;
}
switch (aLevel) {
case LEVEL_DDD:
switch (aIndex) {
case 1: gDDDBowsersSub = 0; gDDDPoles = 0; break;
case 2: gDDDBowsersSub = 1; gDDDPoles = 0; break;
case 3: gDDDBowsersSub = 0; gDDDPoles = 1; break;
case 4: gDDDBowsersSub = 1; gDDDPoles = 1; break;
}
break;
case LEVEL_WDW:
if (gEnvironmentRegions) {
switch (aIndex) {
case 1: gEnvironmentRegions[6] = *gEnvironmentLevels = 31; gWdwWaterLevelSet = 1; break;
case 2: gEnvironmentRegions[6] = *gEnvironmentLevels = 1024; gWdwWaterLevelSet = 1; break;
case 3: gEnvironmentRegions[6] = *gEnvironmentLevels = 1792; gWdwWaterLevelSet = 1; break;
case 4: gEnvironmentRegions[6] = *gEnvironmentLevels = 2816; gWdwWaterLevelSet = 1; break;
}
}
break;
case LEVEL_THI:
switch (aIndex) {
case 1: gTHIWaterDrained = 0; break;
case 2: gTHIWaterDrained = 1; break;
case 3: gTHIWaterDrained = 0; break;
case 4: gTHIWaterDrained = 1; break;
}
break;
case LEVEL_TTC:
switch (aIndex) {
case 1: gTTCSpeedSetting = TTC_SPEED_STOPPED; break;
case 2: gTTCSpeedSetting = TTC_SPEED_SLOW; break;
case 3: gTTCSpeedSetting = TTC_SPEED_FAST; break;
case 4: gTTCSpeedSetting = TTC_SPEED_RANDOM; break;
}
break;
}
}
//
// Update
//
static void *DynOS_Warp_UpdateWarp(void *aCmd, bool aIsLevelInitDone) {
static s32 sDynosWarpTargetArea = -1;
// Phase 1 - Clear the previous level and set up the new level
if (sDynosWarpTargetArea == -1) {
// Close the pause menu if it was open
optmenu_toggle();
level_set_transition(0, NULL);
gDialogBoxState = 0;
gMenuMode = -1;
// Cancel out every music/sound/sequence
for (u16 seqid = 0; seqid != SEQ_COUNT; ++seqid) {
stop_background_music(seqid);
}
play_shell_music();
stop_shell_music();
stop_cap_music();
func_80321080(0);
fadeout_music(0);
fadeout_level_music(0);
// Free everything from the current level
clear_objects();
clear_area_graph_nodes();
clear_areas();
main_pool_pop_state();
// Reset Mario's state
gMarioState->healCounter = 0;
gMarioState->hurtCounter = 0;
gMarioState->numCoins = 0;
gMarioState->input = 0;
gMarioState->controller->buttonPressed = 0;
gHudDisplay.coins = 0;
// Set up new level values
gCurrLevelNum = sDynosWarpLevelNum;
gCurrCourseNum = DynOS_Level_GetCourse(gCurrLevelNum);
gSavedCourseNum = gCurrCourseNum;
gCurrActNum = MAX(1, sDynosWarpActNum * (gCurrCourseNum <= COURSE_STAGES_MAX));
gDialogCourseActNum = gCurrActNum;
gCurrAreaIndex = sDynosWarpAreaNum;
#ifdef COOP
gCurrActStarNum = sDynosWarpActNum;
#else
DynOS_Warp_SetParam(gCurrLevelNum, DynOS_Opt_GetValue("dynos_warp_param"));
#endif
sDynosWarpTargetArea = gCurrAreaIndex;
// Set up new level script
sWarpDest.type = 0;
sWarpDest.levelNum = 0;
sWarpDest.areaIdx = gCurrAreaIndex;
sWarpDest.nodeId = 0;
sWarpDest.arg = 0;
return (void *) DynOS_Level_GetScript(gCurrLevelNum);
} else {
// Phase 2 - Set Mario spawn info after the MARIO_POS command
if (*((u8 *) aCmd) == 0x2B) {
gMarioSpawnInfo->areaIndex = sDynosWarpTargetArea;
gCurrAreaIndex = sDynosWarpTargetArea;
}
// Phase 3 - End level initialization
if (aIsLevelInitDone) {
// Init Mario
s16 *_LevelEntryWarp = DynOS_Level_GetWarpEntry(gCurrLevelNum, gCurrAreaIndex);
s16 sDynosWarpSpawnType = sSpawnTypeFromWarpBhv[_LevelEntryWarp[2]];
gMarioSpawnInfo->startPos[0] = _LevelEntryWarp[3] + (sDynosWarpSpawnType == MARIO_SPAWN_DOOR_WARP) * 300.0f * sins(_LevelEntryWarp[6]);
gMarioSpawnInfo->startPos[1] = _LevelEntryWarp[4];
gMarioSpawnInfo->startPos[2] = _LevelEntryWarp[5] + (sDynosWarpSpawnType == MARIO_SPAWN_DOOR_WARP) * 300.0f * coss(_LevelEntryWarp[6]);
gMarioSpawnInfo->startAngle[0] = 0;
gMarioSpawnInfo->startAngle[1] = _LevelEntryWarp[6];
gMarioSpawnInfo->startAngle[2] = 0;
gMarioSpawnInfo->areaIndex = gCurrAreaIndex;
init_mario();
set_mario_initial_action(gMarioState, sDynosWarpSpawnType, 0);
#ifndef COOP
DynOS_Warp_SetParam(gCurrLevelNum, DynOS_Opt_GetValue("dynos_warp_param"));
#endif
// Init transition
reset_camera(gCurrentArea->camera);
init_camera(gCurrentArea->camera);
sDelayedWarpOp = WARP_OP_NONE;
switch (sDynosWarpSpawnType) {
case MARIO_SPAWN_UNKNOWN_03: play_transition(WARP_TRANSITION_FADE_FROM_STAR, 0x10, 0x00, 0x00, 0x00); break;
case MARIO_SPAWN_DOOR_WARP: play_transition(WARP_TRANSITION_FADE_FROM_CIRCLE, 0x10, 0x00, 0x00, 0x00); break;
case MARIO_SPAWN_TELEPORT: play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x14, 0xFF, 0xFF, 0xFF); break;
case MARIO_SPAWN_SPIN_AIRBORNE: play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x1A, 0xFF, 0xFF, 0xFF); break;
case MARIO_SPAWN_SPIN_AIRBORNE_CIRCLE: play_transition(WARP_TRANSITION_FADE_FROM_CIRCLE, 0x10, 0x00, 0x00, 0x00); break;
case MARIO_SPAWN_UNKNOWN_27: play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x10, 0x00, 0x00, 0x00); break;
default: play_transition(WARP_TRANSITION_FADE_FROM_STAR, 0x10, 0x00, 0x00, 0x00); break;
}
// Set music
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
if (gMarioState->flags & MARIO_METAL_CAP) play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_METAL_CAP));
if (gMarioState->flags & MARIO_VANISH_CAP) play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP));
if (gMarioState->flags & MARIO_WING_CAP) play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP));
if (gCurrLevelNum == LEVEL_BOWSER_1 ||
gCurrLevelNum == LEVEL_BOWSER_2 ||
gCurrLevelNum == LEVEL_BOWSER_3) {
sound_banks_enable(0, 0xFFFF); // Bowser levels sound fix
}
// Reset values
sDynosWarpTargetArea = -1;
sDynosWarpLevelNum = -1;
sDynosWarpAreaNum = -1;
sDynosWarpActNum = -1;
}
}
// Reset DDD settings to default
if (gCurrCourseNum == COURSE_NONE) {
gDDDBowsersSub = -1;
gDDDPoles = -1;
}
return NULL;
}
static void DynOS_Warp_FindExitPosition(s16 &aPosX, s16 &aPosY, s16 &aPosZ, s16 aFYaw, f32 aDist) {
for (f32 _Dist = aDist; _Dist > 0.f; _Dist -= 10.f) {
f32 _PosX = (f32) aPosX + _Dist * sins(aFYaw + 0x8000);
f32 _PosZ = (f32) aPosZ + _Dist * coss(aFYaw + 0x8000);
for (f32 _DeltaY = 0.f; _DeltaY <= 5000.f; _DeltaY += 100.f) {
f32 _PosY = (f32) aPosY + _DeltaY;
struct Surface *_Floor;
f32 _FloorY = find_floor(_PosX, _PosY, _PosZ, &_Floor);
if (_Floor &&
_Floor->type != SURFACE_WARP &&
_Floor->type != SURFACE_BURNING &&
_Floor->type != SURFACE_DEATH_PLANE &&
_Floor->type != SURFACE_VERTICAL_WIND &&
_Floor->type != SURFACE_DEEP_QUICKSAND &&
_Floor->type != SURFACE_INSTANT_QUICKSAND &&
_Floor->type != SURFACE_INSTANT_MOVING_QUICKSAND) {
aPosX = _PosX;
aPosY = _FloorY;
aPosZ = _PosZ;
return;
}
}
}
}
static void *DynOS_Warp_UpdateExit(void *aCmd, bool aIsLevelInitDone) {
static s32 sDynosExitTargetArea = -1;
static s16 *sDynosExitTargetWarp = NULL;
// Phase 0 - Wait for the Mario head transition to end
if (sDynosExitTargetArea == -1 && DynOS_IsTransitionActive()) {
return NULL;
}
// Phase 1 - Clear the previous level and set up the new level
if (sDynosExitTargetArea == -1) {
// Bowser levels
if (sDynosExitLevelNum == LEVEL_BOWSER_1) sDynosExitLevelNum = LEVEL_BITDW;
if (sDynosExitLevelNum == LEVEL_BOWSER_2) sDynosExitLevelNum = LEVEL_BITFS;
if (sDynosExitLevelNum == LEVEL_BOWSER_3) sDynosExitLevelNum = LEVEL_BITS;
// Exit warp to Castle warp
// Uses the death warp, as it's the only warp that exists for every stage in the game
s16 *_ExitWarp = DynOS_Level_GetWarpDeath(sDynosExitLevelNum, sDynosExitAreaNum);
sDynosExitTargetWarp = DynOS_Level_GetWarp(_ExitWarp[7], _ExitWarp[8], _ExitWarp[9]);
// Free everything from the current level
clear_objects();
clear_area_graph_nodes();
clear_areas();
main_pool_pop_state();
// Reset Mario's state
gMarioState->healCounter = 0;
gMarioState->hurtCounter = 0;
gMarioState->numCoins = 0;
gMarioState->input = 0;
gMarioState->controller->buttonPressed = 0;
gHudDisplay.coins = 0;
// Set up new level values
gCurrLevelNum = _ExitWarp[7];
gCurrCourseNum = DynOS_Level_GetCourse(gCurrLevelNum);
gSavedCourseNum = gCurrCourseNum;
gDialogCourseActNum = gCurrActNum;
gCurrAreaIndex = _ExitWarp[8];
sDynosExitTargetArea = _ExitWarp[8];
// Set up new level script
sWarpDest.type = 0;
sWarpDest.levelNum = 0;
sWarpDest.areaIdx = gCurrAreaIndex;
sWarpDest.nodeId = 0;
sWarpDest.arg = 0;
return (void *) DynOS_Level_GetScript(gCurrLevelNum);
} else {
// Phase 2 - Set Mario spawn info after the MARIO_POS command
if (*((u8 *) aCmd) == 0x2B) {
gMarioSpawnInfo->areaIndex = sDynosExitTargetArea;
gCurrAreaIndex = sDynosExitTargetArea;
}
// Phase 3 - End level initialization
if (sDynosExitTargetWarp && aIsLevelInitDone) {
// Find target position
// Because of course, every hack has its own warp distances and orientations...
s16 _TargetPosX = sDynosExitTargetWarp[3];
s16 _TargetPosY = sDynosExitTargetWarp[4];
s16 _TargetPosZ = sDynosExitTargetWarp[5];
s16 _TargetFYaw = sDynosExitTargetWarp[6];
s16 _TargetDYaw = 0;
f32 _TargetDist = 500.f;
DynOS_Warp_FindExitPosition(_TargetPosX, _TargetPosY, _TargetPosZ, _TargetFYaw + _TargetDYaw, _TargetDist);
// Init Mario
gMarioSpawnInfo->startPos[0] = _TargetPosX;
gMarioSpawnInfo->startPos[1] = _TargetPosY;
gMarioSpawnInfo->startPos[2] = _TargetPosZ;
gMarioSpawnInfo->startAngle[0] = 0;
gMarioSpawnInfo->startAngle[1] = _TargetFYaw + _TargetDYaw;
gMarioSpawnInfo->startAngle[2] = 0;
gMarioSpawnInfo->areaIndex = gCurrAreaIndex;
init_mario();
set_mario_initial_action(gMarioState, MARIO_SPAWN_UNKNOWN_02, 0);
// Init transition
reset_camera(gCurrentArea->camera);
init_camera(gCurrentArea->camera);
sDelayedWarpOp = WARP_OP_NONE;
play_transition(WARP_TRANSITION_FADE_FROM_STAR, 15, 0x00, 0x00, 0x00);
play_sound(SOUND_MENU_MARIO_CASTLE_WARP, gDefaultSoundArgs);
// Set music
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
sDynosExitTargetWarp = NULL;
}
// Phase 4 - Unlock Mario as soon as the second transition is ended
if (!sDynosExitTargetWarp && !DynOS_IsTransitionActive()) {
sDynosExitTargetArea = -1;
sDynosExitLevelNum = -1;
sDynosExitAreaNum = -1;
}
}
return NULL;
}
void *DynOS_Warp_Update(void *aCmd, bool aIsLevelInitDone) {
// Level Exit
if (sDynosExitLevelNum != -1 &&
sDynosExitAreaNum != -1) {
return DynOS_Warp_UpdateExit(aCmd, aIsLevelInitDone);
}
// Level Warp
if (sDynosWarpLevelNum != -1 &&
sDynosWarpAreaNum != -1 &&
sDynosWarpActNum != -1) {
return DynOS_Warp_UpdateWarp(aCmd, aIsLevelInitDone);
}
return NULL;
}

15
dynos.mk Normal file
View file

@ -0,0 +1,15 @@
# ----------------------
# Dynamic Options System
# ----------------------
DYNOS_INPUT_DIR := ./dynos
DYNOS_OUTPUT_DIR := $(BUILD_DIR)/dynos
DYNOS_PACKS_DIR := $(BUILD_DIR)/dynos/packs
DYNOS_INIT := \
mkdir -p $(DYNOS_INPUT_DIR); \
mkdir -p $(DYNOS_OUTPUT_DIR); \
mkdir -p $(DYNOS_PACKS_DIR); \
cp -f -r $(DYNOS_INPUT_DIR) $(BUILD_DIR) 2>/dev/null || true ;
DYNOS_DO := $(shell $(call DYNOS_INIT))
INCLUDE_CFLAGS += -DDYNOS

View file

@ -6,6 +6,8 @@
#include <ultra64.h>
#include "macros.h"
#include "data/dynos.c.h"
#include "data/dynos_coop.c.h"
#include "pc/network/version.h"
// Certain functions are marked as having return values, but do not
@ -111,6 +113,7 @@ struct GraphNode
/*0x08*/ struct GraphNode *next;
/*0x0C*/ struct GraphNode *parent;
/*0x10*/ struct GraphNode *children;
/*0x14*/ const void *georef;
};
struct AnimInfo

View file

@ -791,5 +791,8 @@ struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) {
GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
}
if (gCurRootGraphNode) {
gCurRootGraphNode->georef = (const void *) segptr;
}
return gCurRootGraphNode;
}

View file

@ -896,7 +896,10 @@ struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
sCurrentCmd = cmd;
while (sScriptStatus == SCRIPT_RUNNING) {
void *dynosCurrCmd = (void *) sCurrentCmd;
LevelScriptJumpTable[sCurrentCmd->type]();
void *dynosNextCmd = dynos_update_cmd(dynosCurrCmd);
if (dynosNextCmd) sCurrentCmd = dynosNextCmd;
}
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);

View file

@ -405,6 +405,7 @@ void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 b
}
void render_game(void) {
dynos_update_gfx();
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
geo_process_root(gCurrentArea->unk04, D_8032CE74, D_8032CE78, gFBSetColor);

View file

@ -394,9 +394,6 @@ void adjust_analog_stick(struct Controller *controller) {
// if a demo sequence exists, this will run the demo
// input list until it is complete. called every frame.
void run_demo_inputs(void) {
// eliminate the unused bits.
gControllers[0].controllerData->button &= VALID_BUTTONS;
/*
Check if a demo inputs list
exists and if so, run the
@ -465,6 +462,7 @@ void read_controller_inputs(void) {
if (gControllerBits) {
osRecvMesg(&gSIEventMesgQueue, &D_80339BEC, OS_MESG_BLOCK);
osContGetReadData(gInteractableOverridePad ? &gInteractablePad : &gControllerPads[0]);
dynos_update_opt((void *) &gControllerPads[0]);
}
run_demo_inputs();

View file

@ -2821,6 +2821,12 @@ s16 render_pause_courses_and_castle(void) {
if (gPlayer1Controller->buttonPressed & R_TRIG)
djui_panel_pause_create(NULL);
#ifndef COOP
// call into DynOS's menu system
optmenu_draw();
optmenu_draw_prompt();
#endif
return 0;
}

View file

@ -2921,6 +2921,8 @@ s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32
s32 cur_obj_has_model(u16 modelID) {
if (o->header.gfx.sharedChild == gLoadedGraphNodes[modelID]) {
return TRUE;
} else if (o->header.gfx.sharedChild && gLoadedGraphNodes[modelID] && o->header.gfx.sharedChild->georef == gLoadedGraphNodes[modelID]->georef) {
return TRUE;
} else {
return FALSE;
}

8
src/game/options_menu.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef OPTIONS_MENU_H
#define OPTIONS_MENU_H
void optmenu_draw(void);
void optmenu_draw_prompt(void);
void optmenu_toggle(void);
#endif

View file

@ -1183,7 +1183,9 @@ static void geo_process_object(struct Object *node) {
// FIXME: correct types
if (node->header.gfx.animInfo.curAnim != NULL) {
dynos_gfx_swap_animations(node);
geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation);
dynos_gfx_swap_animations(node);
}
if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
Mtx *mtx = alloc_display_list(sizeof(*mtx));
@ -1300,7 +1302,9 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
gCurAnimType = 0;
gCurGraphNodeHeldObject = (void *) node;
if (node->objNode->header.gfx.animInfo.curAnim != NULL) {
dynos_gfx_swap_animations(node->objNode);
geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation);
dynos_gfx_swap_animations(node->objNode);
}
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);

View file

@ -98,6 +98,10 @@ static void debug_warp_level(u8 level) {
}
static void debug_warp_area() {
extern bool dynos_warp_to_level(s32 aLevel, s32 aArea, s32 aAct);
dynos_warp_to_level(LEVEL_CCM, 1, 5);
return;
if (sCurrPlayMode == PLAY_MODE_CHANGE_LEVEL) { return; }
struct ObjectWarpNode* objectNode = gCurrentArea->warpNodes;

View file

@ -53,6 +53,7 @@
#include "djui_panel_controls_n64.h"
#include "djui_panel_controls_extra.h"
#include "djui_panel_display.h"
#include "djui_panel_dynos.h"
#include "djui_panel_sound.h"
#include "djui_panel_confirm.h"
#include "djui_panel_cheats.h"

View file

@ -42,6 +42,7 @@ struct DjuiBase {
bool addChildrenToHead;
bool abandonAfterChildRenderFail;
s32 tag;
bool bTag;
void (*get_cursor_hover_location)(struct DjuiBase*, f32* x, f32* y);
void (*on_child_render)(struct DjuiBase*, struct DjuiBase*);
void (*on_render_pre)(struct DjuiBase*, bool*);

View file

@ -7,7 +7,7 @@ static void djui_panel_display_apply(UNUSED struct DjuiBase* caller) {
}
void djui_panel_display_create(struct DjuiBase* caller) {
f32 bodyHeight = 32 * 7 + 64 * 1 + 16 * 6;
f32 bodyHeight = 32 * 7 + 64 * 2 + 16 * 7;
struct DjuiBase* defaultBase = NULL;
struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\D\\#1be700\\I\\#00b3ff\\S\\#ffef00\\P\\#ff0800\\L\\#1be700\\A\\#00b3ff\\Y");
@ -44,6 +44,11 @@ void djui_panel_display_create(struct DjuiBase* caller) {
djui_base_set_size_type(&selectionbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&selectionbox3->base, 1.0f, 32);
struct DjuiButton* button5 = djui_button_create(&body->base, "DynOS Model Packs");
djui_base_set_size_type(&button5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&button5->base, 1.0f, 64);
djui_interactable_hook_click(&button5->base, djui_panel_dynos_create);
struct DjuiButton* button6 = djui_button_create(&body->base, "Back");
djui_base_set_size_type(&button6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&button6->base, 1.0f, 64);

View file

@ -0,0 +1,42 @@
#include "djui.h"
#include "src/pc/utils/misc.h"
#include "src/pc/configfile.h"
#include "data/dynos_coop.c.h"
static void djui_panel_dynos_apply(struct DjuiBase* caller) {
dynos_packs_set_enabled(caller->tag, caller->bTag);
}
void djui_panel_dynos_create(struct DjuiBase* caller) {
int packCount = dynos_packs_get_count();
f32 bodyHeight = 32 * (packCount) + 64 * 1 + 16 * (packCount + 1);
struct DjuiBase* defaultBase = NULL;
struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\D\\#1be700\\Y\\#00b3ff\\N\\#ffef00\\O\\#ff0800\\S");
struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel);
{
for (int i = 0; i < packCount; i++) {
bool tmp = dynos_packs_get_enabled(i);
const char* pack = dynos_packs_get(i);
struct DjuiCheckbox* checkbox1 = djui_checkbox_create(&body->base, pack, &tmp);
checkbox1->base.tag = i;
checkbox1->base.bTag = tmp;
checkbox1->value = &checkbox1->base.bTag;
djui_base_set_size_type(&checkbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox1->base, 1.0f, 32);
djui_interactable_hook_value_change(&checkbox1->base, djui_panel_dynos_apply);
if (i == 0) { defaultBase = &checkbox1->base; }
}
struct DjuiButton* button6 = djui_button_create(&body->base, "Back");
djui_base_set_size_type(&button6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&button6->base, 1.0f, 64);
djui_button_set_style(button6, 1);
djui_interactable_hook_click(&button6->base, djui_panel_menu_back);
}
djui_panel_add(caller, &panel->base, defaultBase);
}

View file

@ -0,0 +1,4 @@
#pragma once
#include "djui.h"
void djui_panel_dynos_create(struct DjuiBase* caller);

View file

@ -6,10 +6,8 @@
#include <stdbool.h>
#include <assert.h>
#ifdef EXTERNAL_DATA
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#endif
#ifndef _LANGUAGE_C
#define _LANGUAGE_C
@ -52,13 +50,8 @@
#define MAX_LIGHTS 8
#define MAX_VERTICES 64
#ifdef EXTERNAL_DATA
# define MAX_CACHED_TEXTURES 4096 // for preloading purposes
# define HASH_SHIFT 0
#else
# define MAX_CACHED_TEXTURES 512
# define HASH_SHIFT 5
#endif
#define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2)
#define HASH_MASK (HASHMAP_LEN - 1)
@ -606,6 +599,8 @@ static bool preload_texture(void *user, const char *path) {
#endif // EXTERNAL_DATA
static void import_texture(int tile) {
extern s32 dynos_gfx_import_texture(void **output, void *ptr, s32 tile, void *grapi, void **hashmap, void *pool, s32 *poolpos, s32 poolsize);
if (dynos_gfx_import_texture((void **) &rendering_state.textures[tile], (void *) rdp.loaded_texture[tile].addr, tile, gfx_rapi, (void **) gfx_texture_cache.hashmap, (void *) gfx_texture_cache.pool, (int *) &gfx_texture_cache.pool_pos, MAX_CACHED_TEXTURES)) { return; }
uint8_t fmt = rdp.texture_tile.fmt;
uint8_t siz = rdp.texture_tile.siz;