mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-28 15:03:01 +00:00
Merge branch 'dynos' into unstable
This commit is contained in:
commit
e81808c314
40 changed files with 7255 additions and 11 deletions
3
Makefile
3
Makefile
|
@ -543,6 +543,9 @@ DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(
|
||||||
# Segment elf files
|
# Segment elf files
|
||||||
SEG_FILES := $(SEGMENT_ELF_FILES) $(ACTOR_ELF_FILES) $(LEVEL_ELF_FILES)
|
SEG_FILES := $(SEGMENT_ELF_FILES) $(ACTOR_ELF_FILES) $(LEVEL_ELF_FILES)
|
||||||
|
|
||||||
|
# Dynos
|
||||||
|
include dynos.mk
|
||||||
|
|
||||||
##################### Compiler Options #######################
|
##################### Compiler Options #######################
|
||||||
INCLUDE_CFLAGS := -I include -I $(BUILD_DIR) -I $(BUILD_DIR)/include -I src -I . $(EXTRA_INCLUDES)
|
INCLUDE_CFLAGS := -I include -I $(BUILD_DIR) -I $(BUILD_DIR)/include -I src -I . $(EXTRA_INCLUDES)
|
||||||
ENDIAN_BITWIDTH := $(BUILD_DIR)/endian-and-bitwidth
|
ENDIAN_BITWIDTH := $(BUILD_DIR)/endian-and-bitwidth
|
||||||
|
|
15
data/dynos.c.h
Normal file
15
data/dynos.c.h
Normal 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
702
data/dynos.cpp.h
Normal 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
38
data/dynos.h
Normal 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
24
data/dynos_c.cpp
Normal 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
15
data/dynos_coop.c.h
Normal 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
32
data/dynos_coop_c.cpp
Normal 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
70
data/dynos_gfx_init.cpp
Normal 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
313
data/dynos_gfx_load.cpp
Normal 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
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
285
data/dynos_gfx_texture.cpp
Normal 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
165
data/dynos_gfx_update.cpp
Normal 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
318
data/dynos_gfx_write.cpp
Normal 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
825
data/dynos_level.cpp
Normal 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
64
data/dynos_main.cpp
Normal 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
502
data/dynos_misc.cpp
Normal 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
748
data/dynos_opt.cpp
Normal 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
62
data/dynos_opt_config.cpp
Normal 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
70
data/dynos_opt_cont.cpp
Normal 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
309
data/dynos_opt_render.cpp
Normal 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
159
data/dynos_opt_vanilla.cpp
Normal 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;
|
||||||
|
}
|
72
data/dynos_opt_vanilla_c.c
Normal file
72
data/dynos_opt_vanilla_c.c
Normal 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
471
data/dynos_warps.cpp
Normal 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
15
dynos.mk
Normal 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
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <ultra64.h>
|
#include <ultra64.h>
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
#include "data/dynos.c.h"
|
||||||
|
#include "data/dynos_coop.c.h"
|
||||||
#include "pc/network/version.h"
|
#include "pc/network/version.h"
|
||||||
|
|
||||||
// Certain functions are marked as having return values, but do not
|
// Certain functions are marked as having return values, but do not
|
||||||
|
@ -111,6 +113,7 @@ struct GraphNode
|
||||||
/*0x08*/ struct GraphNode *next;
|
/*0x08*/ struct GraphNode *next;
|
||||||
/*0x0C*/ struct GraphNode *parent;
|
/*0x0C*/ struct GraphNode *parent;
|
||||||
/*0x10*/ struct GraphNode *children;
|
/*0x10*/ struct GraphNode *children;
|
||||||
|
/*0x14*/ const void *georef;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimInfo
|
struct AnimInfo
|
||||||
|
|
|
@ -791,5 +791,8 @@ struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) {
|
||||||
GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
|
GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gCurRootGraphNode) {
|
||||||
|
gCurRootGraphNode->georef = (const void *) segptr;
|
||||||
|
}
|
||||||
return gCurRootGraphNode;
|
return gCurRootGraphNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -896,7 +896,10 @@ struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
|
||||||
sCurrentCmd = cmd;
|
sCurrentCmd = cmd;
|
||||||
|
|
||||||
while (sScriptStatus == SCRIPT_RUNNING) {
|
while (sScriptStatus == SCRIPT_RUNNING) {
|
||||||
|
void *dynosCurrCmd = (void *) sCurrentCmd;
|
||||||
LevelScriptJumpTable[sCurrentCmd->type]();
|
LevelScriptJumpTable[sCurrentCmd->type]();
|
||||||
|
void *dynosNextCmd = dynos_update_cmd(dynosCurrCmd);
|
||||||
|
if (dynosNextCmd) sCurrentCmd = dynosNextCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
|
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
|
||||||
|
|
|
@ -405,6 +405,7 @@ void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 b
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_game(void) {
|
void render_game(void) {
|
||||||
|
dynos_update_gfx();
|
||||||
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
|
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
|
||||||
geo_process_root(gCurrentArea->unk04, D_8032CE74, D_8032CE78, gFBSetColor);
|
geo_process_root(gCurrentArea->unk04, D_8032CE74, D_8032CE78, gFBSetColor);
|
||||||
|
|
||||||
|
|
|
@ -394,9 +394,6 @@ void adjust_analog_stick(struct Controller *controller) {
|
||||||
// if a demo sequence exists, this will run the demo
|
// if a demo sequence exists, this will run the demo
|
||||||
// input list until it is complete. called every frame.
|
// input list until it is complete. called every frame.
|
||||||
void run_demo_inputs(void) {
|
void run_demo_inputs(void) {
|
||||||
// eliminate the unused bits.
|
|
||||||
gControllers[0].controllerData->button &= VALID_BUTTONS;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if a demo inputs list
|
Check if a demo inputs list
|
||||||
exists and if so, run the
|
exists and if so, run the
|
||||||
|
@ -465,6 +462,7 @@ void read_controller_inputs(void) {
|
||||||
if (gControllerBits) {
|
if (gControllerBits) {
|
||||||
osRecvMesg(&gSIEventMesgQueue, &D_80339BEC, OS_MESG_BLOCK);
|
osRecvMesg(&gSIEventMesgQueue, &D_80339BEC, OS_MESG_BLOCK);
|
||||||
osContGetReadData(gInteractableOverridePad ? &gInteractablePad : &gControllerPads[0]);
|
osContGetReadData(gInteractableOverridePad ? &gInteractablePad : &gControllerPads[0]);
|
||||||
|
dynos_update_opt((void *) &gControllerPads[0]);
|
||||||
}
|
}
|
||||||
run_demo_inputs();
|
run_demo_inputs();
|
||||||
|
|
||||||
|
|
|
@ -2821,6 +2821,12 @@ s16 render_pause_courses_and_castle(void) {
|
||||||
if (gPlayer1Controller->buttonPressed & R_TRIG)
|
if (gPlayer1Controller->buttonPressed & R_TRIG)
|
||||||
djui_panel_pause_create(NULL);
|
djui_panel_pause_create(NULL);
|
||||||
|
|
||||||
|
#ifndef COOP
|
||||||
|
// call into DynOS's menu system
|
||||||
|
optmenu_draw();
|
||||||
|
optmenu_draw_prompt();
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2921,6 +2921,8 @@ s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32
|
||||||
s32 cur_obj_has_model(u16 modelID) {
|
s32 cur_obj_has_model(u16 modelID) {
|
||||||
if (o->header.gfx.sharedChild == gLoadedGraphNodes[modelID]) {
|
if (o->header.gfx.sharedChild == gLoadedGraphNodes[modelID]) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
} else if (o->header.gfx.sharedChild && gLoadedGraphNodes[modelID] && o->header.gfx.sharedChild->georef == gLoadedGraphNodes[modelID]->georef) {
|
||||||
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
8
src/game/options_menu.h
Normal file
8
src/game/options_menu.h
Normal 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
|
|
@ -1183,7 +1183,9 @@ static void geo_process_object(struct Object *node) {
|
||||||
|
|
||||||
// FIXME: correct types
|
// FIXME: correct types
|
||||||
if (node->header.gfx.animInfo.curAnim != NULL) {
|
if (node->header.gfx.animInfo.curAnim != NULL) {
|
||||||
|
dynos_gfx_swap_animations(node);
|
||||||
geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation);
|
geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation);
|
||||||
|
dynos_gfx_swap_animations(node);
|
||||||
}
|
}
|
||||||
if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
|
if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
|
||||||
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
||||||
|
@ -1300,7 +1302,9 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
|
||||||
gCurAnimType = 0;
|
gCurAnimType = 0;
|
||||||
gCurGraphNodeHeldObject = (void *) node;
|
gCurGraphNodeHeldObject = (void *) node;
|
||||||
if (node->objNode->header.gfx.animInfo.curAnim != NULL) {
|
if (node->objNode->header.gfx.animInfo.curAnim != NULL) {
|
||||||
|
dynos_gfx_swap_animations(node->objNode);
|
||||||
geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation);
|
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);
|
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
|
||||||
|
|
|
@ -98,6 +98,10 @@ static void debug_warp_level(u8 level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void debug_warp_area() {
|
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; }
|
if (sCurrPlayMode == PLAY_MODE_CHANGE_LEVEL) { return; }
|
||||||
|
|
||||||
struct ObjectWarpNode* objectNode = gCurrentArea->warpNodes;
|
struct ObjectWarpNode* objectNode = gCurrentArea->warpNodes;
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "djui_panel_controls_n64.h"
|
#include "djui_panel_controls_n64.h"
|
||||||
#include "djui_panel_controls_extra.h"
|
#include "djui_panel_controls_extra.h"
|
||||||
#include "djui_panel_display.h"
|
#include "djui_panel_display.h"
|
||||||
|
#include "djui_panel_dynos.h"
|
||||||
#include "djui_panel_sound.h"
|
#include "djui_panel_sound.h"
|
||||||
#include "djui_panel_confirm.h"
|
#include "djui_panel_confirm.h"
|
||||||
#include "djui_panel_cheats.h"
|
#include "djui_panel_cheats.h"
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct DjuiBase {
|
||||||
bool addChildrenToHead;
|
bool addChildrenToHead;
|
||||||
bool abandonAfterChildRenderFail;
|
bool abandonAfterChildRenderFail;
|
||||||
s32 tag;
|
s32 tag;
|
||||||
|
bool bTag;
|
||||||
void (*get_cursor_hover_location)(struct DjuiBase*, f32* x, f32* y);
|
void (*get_cursor_hover_location)(struct DjuiBase*, f32* x, f32* y);
|
||||||
void (*on_child_render)(struct DjuiBase*, struct DjuiBase*);
|
void (*on_child_render)(struct DjuiBase*, struct DjuiBase*);
|
||||||
void (*on_render_pre)(struct DjuiBase*, bool*);
|
void (*on_render_pre)(struct DjuiBase*, bool*);
|
||||||
|
|
|
@ -7,7 +7,7 @@ static void djui_panel_display_apply(UNUSED struct DjuiBase* caller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void djui_panel_display_create(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 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");
|
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_type(&selectionbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||||
djui_base_set_size(&selectionbox3->base, 1.0f, 32);
|
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");
|
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_type(&button6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||||
djui_base_set_size(&button6->base, 1.0f, 64);
|
djui_base_set_size(&button6->base, 1.0f, 64);
|
||||||
|
|
42
src/pc/djui/djui_panel_dynos.c
Normal file
42
src/pc/djui/djui_panel_dynos.c
Normal 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);
|
||||||
|
}
|
4
src/pc/djui/djui_panel_dynos.h
Normal file
4
src/pc/djui/djui_panel_dynos.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#pragma once
|
||||||
|
#include "djui.h"
|
||||||
|
|
||||||
|
void djui_panel_dynos_create(struct DjuiBase* caller);
|
|
@ -6,10 +6,8 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef EXTERNAL_DATA
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _LANGUAGE_C
|
#ifndef _LANGUAGE_C
|
||||||
#define _LANGUAGE_C
|
#define _LANGUAGE_C
|
||||||
|
@ -52,13 +50,8 @@
|
||||||
#define MAX_LIGHTS 8
|
#define MAX_LIGHTS 8
|
||||||
#define MAX_VERTICES 64
|
#define MAX_VERTICES 64
|
||||||
|
|
||||||
#ifdef EXTERNAL_DATA
|
|
||||||
# define MAX_CACHED_TEXTURES 4096 // for preloading purposes
|
# define MAX_CACHED_TEXTURES 4096 // for preloading purposes
|
||||||
# define HASH_SHIFT 0
|
# define HASH_SHIFT 0
|
||||||
#else
|
|
||||||
# define MAX_CACHED_TEXTURES 512
|
|
||||||
# define HASH_SHIFT 5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2)
|
#define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2)
|
||||||
#define HASH_MASK (HASHMAP_LEN - 1)
|
#define HASH_MASK (HASHMAP_LEN - 1)
|
||||||
|
@ -606,6 +599,8 @@ static bool preload_texture(void *user, const char *path) {
|
||||||
#endif // EXTERNAL_DATA
|
#endif // EXTERNAL_DATA
|
||||||
|
|
||||||
static void import_texture(int tile) {
|
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 fmt = rdp.texture_tile.fmt;
|
||||||
uint8_t siz = rdp.texture_tile.siz;
|
uint8_t siz = rdp.texture_tile.siz;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue