2 Commits

Author SHA1 Message Date
JuiceyDev 4c4ff349a0 New folder & better copyassetstoclient 2026-04-01 08:49:02 +02:00
JuiceyDev 79ec8fb439 Fix directory casing for music and Sound assets 2026-03-31 14:08:50 +02:00
7838 changed files with 149082 additions and 86596 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
BasedOnStyle: Google
IndentWidth: 4
AccessModifierOffset: -4
# SortIncludes: false # FIXME: https://github.com/4jcraft/4jcraft/issues/225
SortIncludes: false # FIXME: https://github.com/4jcraft/4jcraft/issues/225
-38
View File
@@ -1,38 +0,0 @@
name: "Build Meson"
description: "Builds the project using Meson"
inputs:
dir_name:
description: "The name of the directory to setup meson in"
required: true
type: string
setup_args:
description: "The args to additionally pass to meson"
required: false
type: string
upload:
description: "Upload the executable"
required: false
default: true
type: boolean
runs:
using: "composite"
steps:
- name: Setup build-directory
shell: nix develop --command bash -e {0}
run: meson setup ${{ inputs.dir_name }} --wipe ${{ inputs.setup_args }}
- name: Compile directory
shell: nix develop --command bash -e {0}
run: meson compile -C ${{ inputs.dir_name }} -j $(nproc) -v Minecraft.Client
- name: Upload executable
if: ${{ inputs.upload }}
uses: actions/upload-artifact@v4
with:
name: minecraft-client-linux-${{ inputs.dir_name }}-${{ github.sha }}
path: ${{ inputs.dir_name }}/Minecraft.Client/Minecraft.Client
retention-days: 7
-18
View File
@@ -1,18 +0,0 @@
name: "Enter Nix dev-shell"
description: "Enters a Nix dev-shell"
runs:
using: "composite"
steps:
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Verify flake integrity
shell: bash
run: nix flake check
- name: Enter Nix dev-shell
shell: bash
run: nix develop
-27
View File
@@ -1,27 +0,0 @@
name: "Restore cache"
description: "Restores the cache for ccache and Meson"
inputs:
ccache_dir:
description: "The directory used by ccache"
required: true
type: string
runs:
using: "composite"
steps:
- name: Restore ccache cache
uses: actions/cache@v4
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }}
- name: Create ccache dir
shell: bash
run: mkdir -p {{ inputs.ccache_dir }}
- name: Restore meson cache
uses: actions/cache@v4
with:
path: ~/.cache/meson
key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }}
+2 -2
View File
@@ -4,9 +4,9 @@ on:
push:
branches: ["dev"]
paths:
- "resources/**"
- "Minecraft.Assets/**"
- "Minecraft.Client/**"
- "minecraft/**"
- "Minecraft.World/**"
- "docs/**"
workflow_dispatch:
+47 -72
View File
@@ -1,91 +1,66 @@
name: Build (Linux, x86-64)
# TODO: test with only pull-request
on:
push:
paths: &workflow_paths
- "**.cppm"
- "**.ixx"
- "**.inl"
- "**.mpp"
- "**.ipp"
- "**.tpp"
- "**.txx"
- "**.inc"
- "**.cpp"
- "**.hpp"
- "**.cxx"
- "**.hxx"
- "**.cc"
- "**.hh"
- "**.c"
- "**.h"
- ".github/workflows/build-linux.yml"
- "**/meson.build"
- '**.cpp'
- '**.h'
- '**.c'
- '**.cc'
- '**.cxx'
- '**.hh'
- '**.hpp'
- '**.hxx'
- '**.inl'
- "**meson.build"
- "flake.nix"
- '.github/workflows/build-linux.yml'
pull_request:
paths: *workflow_paths
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !contains(github.ref, 'dev/') }}
workflow_dispatch:
jobs:
build-linux-amalgamate:
build-linux:
runs-on: ubuntu-latest
steps:
- name: Cache .git folder
- name: Checkout repository
uses: actions/checkout@v6
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev libglm-dev
python -m pip install meson
- name: Restore ccache
uses: actions/cache@v5
with:
path: .git
key: ${{ runner.os }}-git-${{ github.sha }}
restore-keys: ${{ runner.os }}-git-
path: ${{ runner.temp }}/ccache
key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }}
restore-keys: ${{ runner.os }}-ccache-
- uses: actions/checkout@v6
- name: Configure Meson
env:
CC: "ccache clang"
CXX: "ccache clang++"
CC_LD: lld
CXX_LD: lld
run: |
meson setup build --wipe
- name: Build with Meson
env:
CCACHE_DIR: ${{ runner.temp }}/ccache
run: |
# Use all available cores for faster parallel builds
meson compile -C build -j $(nproc) -v Minecraft.Client
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
fetch-depth: 1
- name: Enter dev-shell
uses: ./.github/actions/enter-devshell
- name: Restore build cache
uses: ./.github/actions/restore-cache
with:
ccache_dir: ${{ runner.temp }}/ccache
- name: Compile
uses: ./.github/actions/build-meson
with:
dir_name: "build_release"
setup_args: "-Dunity=on -Dunity_size=99999"
build-linux-full:
runs-on: ubuntu-latest
steps:
- name: Cache .git folder
uses: actions/cache@v5
with:
path: .git
key: ${{ runner.os }}-git-${{ github.sha }}
restore-keys: ${{ runner.os }}-git-
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Enter dev-shell
uses: ./.github/actions/enter-devshell
- name: Restore build cache
uses: ./.github/actions/restore-cache
with:
ccache_dir: ${{ runner.temp }}/ccache
- name: Compile
uses: ./.github/actions/build-meson
with:
dir_name: "build_debug"
setup_args: "-Dunity=off"
name: minecraft-client-linux-${{ github.sha }}
path: build/Minecraft.Client/Minecraft.Client
retention-days: 7
-85
View File
@@ -1,85 +0,0 @@
name: Release Nightly (Linux, x86-64)
on:
push:
paths: &workflow_paths
- '**.cpp'
- '**.h'
- '**.c'
- '**.cc'
- '**.cxx'
- '**.hh'
- '**.hpp'
- '**.hxx'
- '**.inl'
- "**meson.build"
- "flake.nix"
- '.github/workflows/release-linux.yml'
workflow_dispatch:
jobs:
release-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev libglm-dev
python -m pip install meson
# Set a reasonable ccache size
ccache -M 5G || true
- name: Install LLVM + libc++
run: |
wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 20
sudo apt-get install -y libc++-20-dev libc++abi-20-dev
- name: Restore ccache
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/ccache
key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }}
restore-keys: ${{ runner.os }}-ccache-
- name: Configure Meson
env:
CC: "ccache clang"
CXX: "ccache clang++"
CC_LD: lld
CXX_LD: lld
run: |
meson setup build --native-file=scripts/llvm_native.txt --wipe
- name: Build with Meson
env:
CCACHE_DIR: ${{ runner.temp }}/ccache
run: |
export CCACHE_DIR="${{ runner.temp }}/ccache"
meson compile -C build -j $(nproc)
- name: Install patchelf
run: sudo apt-get install -y patchelf
- name: Package artifact
run: |
cd build/targets
zip -r ../minecraft-client-linux.zip app --exclude "app/Minecraft.Client.p/*"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: minecraft-client-linux-${{ github.sha }}
path: minecraft-client-linux.zip
retention-days: 7
- name: Create nightly release
uses: softprops/action-gh-release@v2
with:
tag_name: nightly
name: Nightly
prerelease: true
files: minecraft-client-linux.zip
+4 -1
View File
@@ -6,7 +6,10 @@
!/.github/
!/.github-assets/
!/docs/
!/targets/
!/Minecraft.Assets/
!/Minecraft.Client/
!/Minecraft.World/
!/4J.*/
!/scripts/
!/subprojects/
/subprojects/*
@@ -1,74 +1,49 @@
#pragma once
#include <cstdint>
#include <cstdlib>
// Shared value types used by platform interfaces. These are NOT interfaces
// themselves — they are data carriers that cross the platform boundary.
struct ImageFileBuffer {
enum EImageType { e_typePNG, e_typeJPG };
EImageType m_type;
void* m_pBuffer = nullptr;
int m_bufferSize = 0;
[[nodiscard]] int GetType() const { return m_type; }
[[nodiscard]] void* GetBufferPointer() const { return m_pBuffer; }
[[nodiscard]] int GetBufferSize() const { return m_bufferSize; }
[[nodiscard]] bool Allocated() const { return m_pBuffer != nullptr; }
void Release() {
std::free(m_pBuffer);
m_pBuffer = nullptr;
}
};
struct D3DXIMAGE_INFO {
int Width;
int Height;
};
struct XSOCIAL_PREVIEWIMAGE {
std::uint8_t* pBytes;
std::uint32_t Pitch;
std::uint32_t Width;
std::uint32_t Height;
};
using PXSOCIAL_PREVIEWIMAGE = XSOCIAL_PREVIEWIMAGE*;
struct STRING_VERIFY_RESPONSE {
std::uint16_t wNumStrings;
int* pStringResult;
};
enum class EKeyboardResult {
Pending,
Cancelled,
ResultAccept,
ResultDecline,
};
// Profile-related enums at file scope.
enum class EAwardType {
Achievement = 0,
GamerPic,
Theme,
AvatarItem,
};
// The extracted 4J public headers only need the generic Linux/Windows-style
// compatibility surface. Console backends still own their platform-specific
// identity/content definitions elsewhere.
#if !defined(__ORBIS__) && !defined(__PS3__) && !defined(__PSVITA__) && \
!defined(_DURANGO)
#ifndef XUSER_INDEX_ANY
inline constexpr int XUSER_INDEX_ANY = 255;
inline constexpr int XUSER_MAX_COUNT = 4;
inline constexpr int XUSER_NAME_SIZE = 32;
inline constexpr int XUSER_INDEX_FOCUS = 254;
#endif
#ifndef XUSER_MAX_COUNT
inline constexpr int XUSER_MAX_COUNT = 4;
#endif
#ifndef XUSER_NAME_SIZE
inline constexpr int XUSER_NAME_SIZE = 32;
#endif
#ifndef XUSER_INDEX_FOCUS
inline constexpr int XUSER_INDEX_FOCUS = 254;
#endif
#ifndef FOURJ_COMMON_PLAYER_UID_DEFINED
#define FOURJ_COMMON_PLAYER_UID_DEFINED
using PlayerUID = unsigned long long;
using PPlayerUID = PlayerUID*;
inline constexpr PlayerUID INVALID_XUID = 0;
#endif
#endif
class CXuiStringTable;
#if !defined(__ORBIS__) && !defined(__PS3__) && !defined(__PSVITA__) && \
!defined(_DURANGO)
#ifndef XCONTENT_MAX_DISPLAYNAME_LENGTH
inline constexpr int XCONTENT_MAX_DISPLAYNAME_LENGTH = 256;
inline constexpr int XCONTENT_MAX_FILENAME_LENGTH = 256;
#endif
#ifndef XCONTENT_MAX_FILENAME_LENGTH
inline constexpr int XCONTENT_MAX_FILENAME_LENGTH = 256;
#endif
#ifndef FOURJ_COMMON_XCONTENT_DATA_DEFINED
#define FOURJ_COMMON_XCONTENT_DATA_DEFINED
using XCONTENTDEVICEID = int;
struct XCONTENT_DATA {
@@ -78,9 +53,14 @@ struct XCONTENT_DATA {
char szFileName[XCONTENT_MAX_FILENAME_LENGTH];
};
using PXCONTENT_DATA = XCONTENT_DATA*;
#endif
#ifndef XMARKETPLACE_CONTENT_ID_LEN
inline constexpr int XMARKETPLACE_CONTENT_ID_LEN = 4;
#endif
#ifndef FOURJ_COMMON_XMARKETPLACE_DEFINED
#define FOURJ_COMMON_XMARKETPLACE_DEFINED
struct XMARKETPLACE_CONTENTOFFER_INFO {
std::uint64_t qwOfferID;
std::uint64_t qwPreviewOfferID;
@@ -104,13 +84,45 @@ struct XMARKETPLACE_CONTENTOFFER_INFO {
std::uint32_t dwPointsPrice;
};
using PXMARKETPLACE_CONTENTOFFER_INFO = XMARKETPLACE_CONTENTOFFER_INFO*;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_CONTENT
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_CONTENT = 0x00000002;
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_GAME_DEMO = 0x00000020;
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_GAME_TRAILER = 0x00000040;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_GAME_DEMO
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_GAME_DEMO =
0x00000020;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_GAME_TRAILER
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_GAME_TRAILER =
0x00000040;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_THEME
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_THEME = 0x00000080;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_TILE
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_TILE = 0x00000800;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_ARCADE
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_ARCADE = 0x00002000;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_VIDEO
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_VIDEO = 0x00004000;
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_CONSUMABLE = 0x00010000;
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_AVATARITEM = 0x00100000;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_CONSUMABLE
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_CONSUMABLE =
0x00010000;
#endif
#ifndef XMARKETPLACE_OFFERING_TYPE_AVATARITEM
inline constexpr std::uint32_t XMARKETPLACE_OFFERING_TYPE_AVATARITEM =
0x00100000;
#endif
#endif
@@ -1,25 +1,11 @@
#include "Input.h"
#include "4J_Input.h"
#include "../4J.Common/4J_InputActions.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_gamecontroller.h>
#include <SDL2/SDL_joystick.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_mouse.h>
#include <SDL2/SDL_scancode.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_video.h>
#include <SDL2/begin_code.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <functional>
#include <stdio.h>
#include <string>
#include "../InputActions.h"
#include "../PlatformTypes.h"
C_4JInput InputManager;
static const int KEY_COUNT = SDL_NUM_SCANCODES;
@@ -52,7 +38,8 @@ static bool s_scrollSnapTaken = false;
// Text input state (non-blocking keyboard)
static bool s_keyboardActive = false;
static std::string s_textInputBuf;
static std::function<int(bool)> s_keyboardCallback;
static int (*s_keyboardCallback)(void*, const bool) = nullptr;
static void* s_keyboardCallbackParam = nullptr;
// We set all the watched keys
// I don't know if I'll need to change this if we add chat support soon.
@@ -357,8 +344,9 @@ void C_4JInput::Tick() {
s_keysCurrent[SDL_SCANCODE_RETURN] = false;
s_keysCurrent[SDL_SCANCODE_KP_ENTER] = false;
if (s_keyboardCallback) {
s_keyboardCallback(true);
s_keyboardCallback(s_keyboardCallbackParam, true);
s_keyboardCallback = nullptr;
s_keyboardCallbackParam = nullptr;
}
} else if (KPressed(SDL_SCANCODE_ESCAPE)) {
s_keyboardActive = false;
@@ -367,8 +355,9 @@ void C_4JInput::Tick() {
// Consume the key so it doesn't also trigger ACTION_MENU_CANCEL
s_keysCurrent[SDL_SCANCODE_ESCAPE] = false;
if (s_keyboardCallback) {
s_keyboardCallback(false);
s_keyboardCallback(s_keyboardCallbackParam, false);
s_keyboardCallback = nullptr;
s_keyboardCallbackParam = nullptr;
}
}
}
@@ -640,19 +629,21 @@ void C_4JInput::SetJoypadSensitivity(int, float) {}
void C_4JInput::SetJoypadStickAxisMap(int, unsigned int, unsigned int) {}
void C_4JInput::SetJoypadStickTriggerMap(int, unsigned int, unsigned int) {}
void C_4JInput::SetKeyRepeatRate(float, float) {}
void C_4JInput::SetDebugSequence(const char*, std::function<int()>) {}
void C_4JInput::SetDebugSequence(const char*, int (*)(void*), void*) {}
float C_4JInput::GetIdleSeconds(int) { return 0.f; }
bool C_4JInput::IsPadConnected(int iPad) { return iPad == 0; }
EKeyboardResult C_4JInput::RequestKeyboard(const wchar_t*, const wchar_t*, int,
unsigned int,
std::function<int(bool)> callback,
int (*callback)(void*, const bool),
void* scene,
C_4JInput::EKeyboardMode) {
s_keyboardActive = true;
s_textInputBuf.clear();
s_keyboardCallback = std::move(callback);
s_keyboardCallback = callback;
s_keyboardCallbackParam = scene;
SDL_StartTextInput();
return EKeyboardResult::Pending;
return EKeyboard_Pending;
}
bool C_4JInput::GetMenuDisplayed(int iPad) {
if (iPad >= 0 && iPad < 4) return s_menuDisplayed[iPad];
@@ -660,16 +651,10 @@ bool C_4JInput::GetMenuDisplayed(int iPad) {
}
const char* C_4JInput::GetText() { return s_textInputBuf.c_str(); }
bool C_4JInput::VerifyStrings(wchar_t**, int,
std::function<int(STRING_VERIFY_RESPONSE*)>) {
int (*)(void*, STRING_VERIFY_RESPONSE*), void*) {
return true;
}
void C_4JInput::CancelQueuedVerifyStrings(
std::function<int(STRING_VERIFY_RESPONSE*)>) {}
void C_4JInput::CancelQueuedVerifyStrings(int (*)(void*,
STRING_VERIFY_RESPONSE*),
void*) {}
void C_4JInput::CancelAllVerifyInProgress() {}
// Primary pad (moved from Profile)
namespace {
int s_inputPrimaryPad = 0;
}
int C_4JInput::GetPrimaryPad() { return s_inputPrimaryPad; }
void C_4JInput::SetPrimaryPad(int iPad) { s_inputPrimaryPad = iPad; }
@@ -2,8 +2,6 @@
#include <cstdint>
#include "../IPlatformInput.h"
#define MAP_STYLE_0 0
#define MAP_STYLE_1 1
#define MAP_STYLE_2 2
@@ -49,8 +47,31 @@
#define TRIGGER_MAP_0 0
#define TRIGGER_MAP_1 1
class C_4JInput : public IPlatformInput {
enum EKeyboardResult {
EKeyboard_Pending,
EKeyboard_Cancelled,
EKeyboard_ResultAccept,
EKeyboard_ResultDecline,
};
typedef struct _STRING_VERIFY_RESPONSE {
std::uint16_t wNumStrings;
int* pStringResult;
} STRING_VERIFY_RESPONSE;
class C_4JInput {
public:
enum EKeyboardMode {
EKeyboardMode_Default,
EKeyboardMode_Numeric,
EKeyboardMode_Password,
EKeyboardMode_Alphabet,
EKeyboardMode_Full,
EKeyboardMode_Alphabet_Extended,
EKeyboardMode_IP_Address,
EKeyboardMode_Phone
};
void Initialise(int iInputStateC, unsigned char ucMapC,
unsigned char ucActionC, unsigned char ucMenuActionC);
void Tick(void);
@@ -75,8 +96,8 @@ public:
void SetJoypadStickTriggerMap(int iPad, unsigned int uiFrom,
unsigned int uiTo);
void SetKeyRepeatRate(float fRepeatDelaySecs, float fRepeatRateSecs);
void SetDebugSequence(const char* chSequenceA,
std::function<int()> callback);
void SetDebugSequence(const char* chSequenceA, int (*Func)(void*),
void* lpParam);
float GetIdleSeconds(int iPad);
bool IsPadConnected(int iPad);
@@ -97,7 +118,8 @@ public:
// live here. The remaining public API keeps the direct text/callback form.
EKeyboardResult RequestKeyboard(const wchar_t* Title, const wchar_t* Text,
int iPad, unsigned int uiMaxChars,
std::function<int(bool)> callback,
int (*Func)(void*, const bool),
void* lpParam,
C_4JInput::EKeyboardMode eMode);
bool GetMenuDisplayed(int);
const char* GetText();
@@ -126,18 +148,15 @@ public:
//
// Intent Protect players from inappropriate language.
bool VerifyStrings(wchar_t** pwStringA, int iStringC,
std::function<int(STRING_VERIFY_RESPONSE*)> callback);
void CancelQueuedVerifyStrings(
std::function<int(STRING_VERIFY_RESPONSE*)> callback);
int (*Func)(void*, STRING_VERIFY_RESPONSE*),
void* lpParam);
void CancelQueuedVerifyStrings(int (*Func)(void*, STRING_VERIFY_RESPONSE*),
void* lpParam);
void CancelAllVerifyInProgress(void);
int GetMouseX();
int GetMouseY();
// Primary pad (moved from Profile)
int GetPrimaryPad();
void SetPrimaryPad(int iPad);
// bool InputDetected(int userIndex, wchar_t* inputText);
};
View File
View File
View File
+25
View File
@@ -0,0 +1,25 @@
input_sources = files(
'4J_Input.cpp',
'INP_ForceFeedback.cpp',
'INP_Keyboard.cpp',
'INP_Main.cpp',
'INP_StringCheck.cpp',
'stdafx.cpp',
)
lib_input = static_library('4J_Input',
input_sources,
include_directories : include_directories('.'),
cpp_args : global_cpp_args + global_cpp_defs + [
'-include', meson.current_source_dir() / 'stdafx.h',
],
)
# We import SDL2 but not SDL3.. which is a bit sad,
sdl_dep = dependency('sdl2', required : false)
input_dep = declare_dependency(
link_with : lib_input,
dependencies : [sdl_dep],
include_directories : include_directories('.'),
)
+1
View File
@@ -0,0 +1 @@
#include "stdafx.h"
+6
View File
@@ -0,0 +1,6 @@
#ifndef _4J_INPUT_STADAFX_H
#define _4J_INPUT_STADAFX_H
#include "../4J.Common/4J_Compat.h"
#endif //_4J_INPUT_STADAFX_H
+273
View File
@@ -0,0 +1,273 @@
#include "4J_Profile.h"
#include "../4J.Common/4J_ProfileConstants.h"
#include <cstdio>
#include <cstring>
C_4JProfile ProfileManager;
namespace {
constexpr PlayerUID kFakeXuidBase = 0xe000d45248242f2eULL;
struct ProfileGameSettings {
bool bSettingsChanged;
unsigned char ucMusicVolume;
unsigned char ucSoundFXVolume;
unsigned char ucSensitivity;
unsigned char ucGamma;
unsigned char ucPad01;
unsigned short usBitmaskValues;
unsigned int uiDebugBitmask;
union {
struct {
unsigned char ucTutorialCompletion[TUTORIAL_PROFILE_STORAGE_BYTES];
std::uint32_t dwSelectedSkin;
unsigned char ucMenuSensitivity;
unsigned char ucInterfaceOpacity;
unsigned char ucPad02;
unsigned char usPad03;
unsigned int uiBitmaskValues;
unsigned int uiSpecialTutorialBitmask;
std::uint32_t dwSelectedCape;
unsigned int uiFavoriteSkinA[MAX_FAVORITE_SKINS];
unsigned char ucCurrentFavoriteSkinPos;
unsigned int uiMashUpPackWorldsDisplay;
unsigned char ucLanguage;
};
unsigned char ucReservedSpace[192];
};
};
static_assert(sizeof(ProfileGameSettings) == 204,
"ProfileGameSettings must match GAME_SETTINGS profile storage");
void* s_profileData[XUSER_MAX_COUNT] = {};
C_4JProfile::PROFILESETTINGS s_dashboardSettings[XUSER_MAX_COUNT] = {};
char s_gamertags[XUSER_MAX_COUNT][16] = {};
std::wstring s_displayNames[XUSER_MAX_COUNT];
int s_primaryPad = 0;
int s_lockedProfile = 0;
bool s_profileIsFullVersion = true;
int (*s_defaultOptionsCallback)(void*, C_4JProfile::PROFILESETTINGS*,
const int iPad) = nullptr;
void* s_defaultOptionsCallbackParam = nullptr;
bool isValidPad(int iPad) { return iPad >= 0 && iPad < XUSER_MAX_COUNT; }
void ensureFakeIdentity(int iPad) {
if (!isValidPad(iPad) || s_gamertags[iPad][0] != '\0') {
return;
}
std::snprintf(s_gamertags[iPad], sizeof(s_gamertags[iPad]), "Player%d",
iPad + 1);
s_displayNames[iPad] = std::wstring(L"Player") + std::to_wstring(iPad + 1);
}
void initialiseDefaultGameSettings(ProfileGameSettings* gameSettings) {
gameSettings->ucMenuSensitivity = 100;
gameSettings->ucInterfaceOpacity = 80;
gameSettings->usBitmaskValues |= 0x0200;
gameSettings->usBitmaskValues |= 0x0400;
gameSettings->usBitmaskValues |= 0x1000;
gameSettings->usBitmaskValues |= 0x8000;
gameSettings->uiBitmaskValues = 0L;
gameSettings->uiBitmaskValues |= GAMESETTING_CLOUDS;
gameSettings->uiBitmaskValues |= GAMESETTING_ONLINE;
gameSettings->uiBitmaskValues |= GAMESETTING_FRIENDSOFFRIENDS;
gameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG;
gameSettings->uiBitmaskValues &= ~GAMESETTING_BEDROCKFOG;
gameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYHUD;
gameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYHAND;
gameSettings->uiBitmaskValues |= GAMESETTING_CUSTOMSKINANIM;
gameSettings->uiBitmaskValues |= GAMESETTING_DEATHMESSAGES;
gameSettings->uiBitmaskValues |= (GAMESETTING_UISIZE & 0x00000800);
gameSettings->uiBitmaskValues |=
(GAMESETTING_UISIZE_SPLITSCREEN & 0x00004000);
gameSettings->uiBitmaskValues |= GAMESETTING_ANIMATEDCHARACTER;
for (int i = 0; i < MAX_FAVORITE_SKINS; ++i) {
gameSettings->uiFavoriteSkinA[i] = 0xFFFFFFFF;
}
gameSettings->ucCurrentFavoriteSkinPos = 0;
gameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF;
gameSettings->uiBitmaskValues &= ~GAMESETTING_PS3EULAREAD;
gameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT;
gameSettings->uiBitmaskValues &= ~GAMESETTING_PSVITANETWORKMODEADHOC;
gameSettings->ucTutorialCompletion[0] = 0xFF;
gameSettings->ucTutorialCompletion[1] = 0xFF;
gameSettings->ucTutorialCompletion[2] = 0x0F;
gameSettings->ucTutorialCompletion[28] |= 1 << 0;
}
} // namespace
void C_4JProfile::Initialise(std::uint32_t dwTitleID, std::uint32_t dwOfferID,
unsigned short usProfileVersion,
unsigned int uiProfileValuesC,
unsigned int uiProfileSettingsC,
std::uint32_t* pdwProfileSettingsA,
int iGameDefinedDataSizeX4,
unsigned int* puiGameDefinedDataChangedBitmask) {
s_primaryPad = 0;
s_lockedProfile = 0;
std::memset(s_dashboardSettings, 0, sizeof(s_dashboardSettings));
for (int i = 0; i < XUSER_MAX_COUNT; ++i) {
delete[] static_cast<unsigned char*>(s_profileData[i]);
s_profileData[i] = new unsigned char[iGameDefinedDataSizeX4 / 4];
std::memset(s_profileData[i], 0, iGameDefinedDataSizeX4 / 4);
initialiseDefaultGameSettings(
static_cast<ProfileGameSettings*>(s_profileData[i]));
ensureFakeIdentity(i);
}
}
void C_4JProfile::SetTrialTextStringTable(CXuiStringTable* pStringTable,
int iAccept, int iReject) {}
void C_4JProfile::SetTrialAwardText(eAwardType AwardType, int iTitle,
int iText) {}
int C_4JProfile::GetLockedProfile() { return s_lockedProfile; }
void C_4JProfile::SetLockedProfile(int iProf) { s_lockedProfile = iProf; }
bool C_4JProfile::IsSignedIn(int iQuadrant) { return iQuadrant == 0; }
bool C_4JProfile::IsSignedInLive(int iProf) { return IsSignedIn(iProf); }
bool C_4JProfile::IsGuest(int iQuadrant) { return false; }
unsigned int C_4JProfile::RequestSignInUI(
bool bFromInvite, bool bLocalGame, bool bNoGuestsAllowed,
bool bMultiplayerSignIn, bool bAddUser,
int (*Func)(void*, const bool, const int iPad), void* lpParam,
int iQuadrant) {
return 0;
}
unsigned int C_4JProfile::DisplayOfflineProfile(int (*Func)(void*, const bool,
const int iPad),
void* lpParam, int iQuadrant) {
return 0;
}
unsigned int C_4JProfile::RequestConvertOfflineToGuestUI(
int (*Func)(void*, const bool, const int iPad), void* lpParam,
int iQuadrant) {
return 0;
}
void C_4JProfile::SetPrimaryPlayerChanged(bool bVal) {}
bool C_4JProfile::QuerySigninStatus(void) { return true; }
void C_4JProfile::GetXUID(int iPad, PlayerUID* pXuid, bool bOnlineXuid) {
if (pXuid)
*pXuid =
kFakeXuidBase + static_cast<PlayerUID>(isValidPad(iPad) ? iPad : 0);
}
bool C_4JProfile::AreXUIDSEqual(PlayerUID xuid1, PlayerUID xuid2) {
return xuid1 == xuid2;
}
bool C_4JProfile::XUIDIsGuest(PlayerUID xuid) { return false; }
bool C_4JProfile::AllowedToPlayMultiplayer(int iProf) { return true; }
bool C_4JProfile::GetChatAndContentRestrictions(int iPad,
bool* pbChatRestricted,
bool* pbContentRestricted,
int* piAge) {
if (pbChatRestricted) *pbChatRestricted = false;
if (pbContentRestricted) *pbContentRestricted = false;
if (piAge) *piAge = 18;
return true;
}
void C_4JProfile::StartTrialGame() {}
void C_4JProfile::AllowedPlayerCreatedContent(int iPad, bool thisQuadrantOnly,
bool* allAllowed,
bool* friendsAllowed) {
if (allAllowed) *allAllowed = true;
if (friendsAllowed) *friendsAllowed = true;
}
bool C_4JProfile::CanViewPlayerCreatedContent(int iPad, bool thisQuadrantOnly,
PlayerUID* pXuids,
unsigned int xuidCount) {
return true;
}
void C_4JProfile::ShowProfileCard(int iPad, PlayerUID targetUid) {}
bool C_4JProfile::GetProfileAvatar(int iPad,
int (*Func)(void* lpParam,
std::uint8_t* thumbnailData,
unsigned int thumbnailBytes),
void* lpParam) {
return false;
}
void C_4JProfile::CancelProfileAvatarRequest() {}
int C_4JProfile::GetPrimaryPad() { return s_primaryPad; }
void C_4JProfile::SetPrimaryPad(int iPad) { s_primaryPad = iPad; }
char* C_4JProfile::GetGamertag(int iPad) {
const int resolvedPad = isValidPad(iPad) ? iPad : 0;
ensureFakeIdentity(resolvedPad);
return s_gamertags[resolvedPad];
}
std::wstring C_4JProfile::GetDisplayName(int iPad) {
const int resolvedPad = isValidPad(iPad) ? iPad : 0;
ensureFakeIdentity(resolvedPad);
return s_displayNames[resolvedPad];
}
bool C_4JProfile::IsFullVersion() { return s_profileIsFullVersion; }
void C_4JProfile::SetSignInChangeCallback(void (*Func)(void*, bool,
unsigned int),
void* lpParam) {}
void C_4JProfile::SetNotificationsCallback(void (*Func)(void*, std::uint32_t,
unsigned int),
void* lpParam) {}
bool C_4JProfile::RegionIsNorthAmerica(void) { return false; }
bool C_4JProfile::LocaleIsUSorCanada(void) { return false; }
int C_4JProfile::GetLiveConnectionStatus() { return 0; }
bool C_4JProfile::IsSystemUIDisplayed() { return false; }
void C_4JProfile::SetProfileReadErrorCallback(void (*Func)(void*),
void* lpParam) {}
int C_4JProfile::SetDefaultOptionsCallback(int (*Func)(void*, PROFILESETTINGS*,
const int iPad),
void* lpParam) {
s_defaultOptionsCallback = Func;
s_defaultOptionsCallbackParam = lpParam;
return 0;
}
int C_4JProfile::SetOldProfileVersionCallback(int (*Func)(void*, unsigned char*,
const unsigned short,
const int),
void* lpParam) {
return 0;
}
C_4JProfile::PROFILESETTINGS* C_4JProfile::GetDashboardProfileSettings(
int iPad) {
return &s_dashboardSettings[isValidPad(iPad) ? iPad : 0];
}
void C_4JProfile::WriteToProfile(int iQuadrant, bool bGameDefinedDataChanged,
bool bOverride5MinuteLimitOnProfileWrites) {}
void C_4JProfile::ForceQueuedProfileWrites(int iPad) {}
void* C_4JProfile::GetGameDefinedProfileData(int iQuadrant) {
return isValidPad(iQuadrant) ? s_profileData[iQuadrant] : nullptr;
}
void C_4JProfile::ResetProfileProcessState() {}
void C_4JProfile::Tick(void) {}
void C_4JProfile::RegisterAward(int iAwardNumber, int iGamerconfigID,
eAwardType eType, bool bLeaderboardAffected,
CXuiStringTable* pStringTable, int iTitleStr,
int iTextStr, int iAcceptStr,
char* pszThemeName, unsigned int uiThemeSize) {}
int C_4JProfile::GetAwardId(int iAwardNumber) { return 0; }
eAwardType C_4JProfile::GetAwardType(int iAwardNumber) {
return eAwardType_Achievement;
}
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber) {
return false;
}
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce) {}
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward) { return false; }
void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount) {}
void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID) {}
void C_4JProfile::SetRichPresenceContextValue(int iPad, int iContextID,
int iVal) {}
void C_4JProfile::SetCurrentGameActivity(int iPad, int iNewPresence,
bool bSetOthersToIdle) {}
void C_4JProfile::DisplayFullVersionPurchase(bool bRequired, int iQuadrant,
int iUpsellParam) {}
void C_4JProfile::SetUpsellCallback(void (*Func)(void* lpParam,
eUpsellType type,
eUpsellResponse response,
int iUserData),
void* lpParam) {}
void C_4JProfile::SetDebugFullOverride(bool bVal) {
s_profileIsFullVersion = bVal;
}
+164
View File
@@ -0,0 +1,164 @@
#pragma once
#include <cstdint>
#include <string>
#include "../4J.Common/4J_Compat.h"
enum eAwardType {
eAwardType_Achievement = 0,
eAwardType_GamerPic,
eAwardType_Theme,
eAwardType_AvatarItem,
};
enum eUpsellType {
eUpsellType_Custom = 0, // This is the default, and means that the upsell
// dialog was initiated in the app code
eUpsellType_Achievement,
eUpsellType_GamerPic,
eUpsellType_Theme,
eUpsellType_AvatarItem,
};
enum eUpsellResponse {
eUpsellResponse_Declined,
eUpsellResponse_Accepted_NoPurchase,
eUpsellResponse_Accepted_Purchase,
};
class C_4JProfile {
public:
struct PROFILESETTINGS {
int iYAxisInversion;
int iControllerSensitivity;
int iVibration;
bool bSwapSticks;
};
// 4 players have game defined data, puiGameDefinedDataChangedBitmask needs
// to be checked by the game side to see if there's an update needed - it'll
// have the bits set for players to be updated
void Initialise(std::uint32_t dwTitleID, std::uint32_t dwOfferID,
unsigned short usProfileVersion,
unsigned int uiProfileValuesC,
unsigned int uiProfileSettingsC,
std::uint32_t* pdwProfileSettingsA,
int iGameDefinedDataSizeX4,
unsigned int* puiGameDefinedDataChangedBitmask);
void SetTrialTextStringTable(CXuiStringTable* pStringTable, int iAccept,
int iReject);
void SetTrialAwardText(eAwardType AwardType, int iTitle,
int iText); // achievement popup in the trial game
int GetLockedProfile();
void SetLockedProfile(int iProf);
bool IsSignedIn(int iQuadrant);
bool IsSignedInLive(int iProf);
bool IsGuest(int iQuadrant);
unsigned int RequestSignInUI(bool bFromInvite, bool bLocalGame,
bool bNoGuestsAllowed, bool bMultiplayerSignIn,
bool bAddUser,
int (*Func)(void*, const bool, const int iPad),
void* lpParam,
int iQuadrant = XUSER_INDEX_ANY);
unsigned int DisplayOfflineProfile(int (*Func)(void*, const bool,
const int iPad),
void* lpParam,
int iQuadrant = XUSER_INDEX_ANY);
unsigned int RequestConvertOfflineToGuestUI(
int (*Func)(void*, const bool, const int iPad), void* lpParam,
int iQuadrant = XUSER_INDEX_ANY);
void SetPrimaryPlayerChanged(bool bVal);
bool QuerySigninStatus(void);
void GetXUID(int iPad, PlayerUID* pXuid, bool bOnlineXuid);
bool AreXUIDSEqual(PlayerUID xuid1, PlayerUID xuid2);
bool XUIDIsGuest(PlayerUID xuid);
bool AllowedToPlayMultiplayer(int iProf);
bool GetChatAndContentRestrictions(int iPad, bool* pbChatRestricted,
bool* pbContentRestricted, int* piAge);
void StartTrialGame(); // disables saves and leaderboard, and change state
// to readyforgame from pregame
void AllowedPlayerCreatedContent(int iPad, bool thisQuadrantOnly,
bool* allAllowed, bool* friendsAllowed);
bool CanViewPlayerCreatedContent(int iPad, bool thisQuadrantOnly,
PlayerUID* pXuids, unsigned int xuidCount);
void ShowProfileCard(int iPad, PlayerUID targetUid);
bool GetProfileAvatar(int iPad,
int (*Func)(void* lpParam,
std::uint8_t* thumbnailData,
unsigned int thumbnailBytes),
void* lpParam);
void CancelProfileAvatarRequest();
// SYS
int GetPrimaryPad();
void SetPrimaryPad(int iPad);
char* GetGamertag(int iPad);
std::wstring GetDisplayName(int iPad);
bool IsFullVersion();
void SetSignInChangeCallback(void (*Func)(void*, bool, unsigned int),
void* lpParam);
void SetNotificationsCallback(void (*Func)(void*, std::uint32_t,
unsigned int),
void* lpParam);
bool RegionIsNorthAmerica(void);
bool LocaleIsUSorCanada(void);
int GetLiveConnectionStatus();
bool IsSystemUIDisplayed();
void SetProfileReadErrorCallback(void (*Func)(void*), void* lpParam);
// PROFILE DATA
int SetDefaultOptionsCallback(int (*Func)(void*, PROFILESETTINGS*,
const int iPad),
void* lpParam);
int SetOldProfileVersionCallback(int (*Func)(void*, unsigned char*,
const unsigned short,
const int),
void* lpParam);
PROFILESETTINGS* GetDashboardProfileSettings(int iPad);
void WriteToProfile(int iQuadrant, bool bGameDefinedDataChanged = false,
bool bOverride5MinuteLimitOnProfileWrites = false);
void ForceQueuedProfileWrites(int iPad = XUSER_INDEX_ANY);
void* GetGameDefinedProfileData(int iQuadrant);
void ResetProfileProcessState(); // after a sign out from the primary
// player, call this
void Tick(void);
// ACHIEVEMENTS & AWARDS
void RegisterAward(int iAwardNumber, int iGamerconfigID, eAwardType eType,
bool bLeaderboardAffected = false,
CXuiStringTable* pStringTable = nullptr,
int iTitleStr = -1, int iTextStr = -1,
int iAcceptStr = -1, char* pszThemeName = nullptr,
unsigned int uiThemeSize = 0L);
int GetAwardId(int iAwardNumber);
eAwardType GetAwardType(int iAwardNumber);
bool CanBeAwarded(int iQuadrant, int iAwardNumber);
void Award(int iQuadrant, int iAwardNumber, bool bForce = false);
bool IsAwardsFlagSet(int iQuadrant, int iAward);
// RICH PRESENCE
void RichPresenceInit(int iPresenceCount, int iContextCount);
void RegisterRichPresenceContext(int iGameConfigContextID);
void SetRichPresenceContextValue(int iPad, int iContextID, int iVal);
void SetCurrentGameActivity(int iPad, int iNewPresence,
bool bSetOthersToIdle = false);
// PURCHASE
void DisplayFullVersionPurchase(bool bRequired, int iQuadrant,
int iUpsellParam = -1);
void SetUpsellCallback(void (*Func)(void* lpParam, eUpsellType type,
eUpsellResponse response,
int iUserData),
void* lpParam);
// Debug
void SetDebugFullOverride(
bool bVal); // To override the license version (trail/full). Only in
// debug/release, not ContentPackage
};
// Singleton
extern C_4JProfile ProfileManager;
View File
View File
View File
View File
View File
+22
View File
@@ -0,0 +1,22 @@
profile_sources = files(
'4J_Profile.cpp',
'PRO_AwardManager.cpp',
'PRO_Data.cpp',
'PRO_Main.cpp',
'PRO_RichPresence.cpp',
'PRO_Sys.cpp',
'stdafx.cpp',
)
# 4jprofile stink
lib_profile = static_library('4J_Profile',
profile_sources,
include_directories : include_directories('.'),
cpp_args : global_cpp_args + global_cpp_defs + [
'-include', meson.current_source_dir() / 'stdafx.h',
],
)
profile_dep = declare_dependency(
link_with : lib_profile,
include_directories : include_directories('.'),
)
+1
View File
@@ -0,0 +1 @@
#include "stdafx.h"
+6
View File
@@ -0,0 +1,6 @@
#ifndef _4J_PROFILE_STADAFX_H
#define _4J_PROFILE_STADAFX_H
#include "../4J.Common/4J_Compat.h"
#endif //_4J_PROFILE_STADAFX_H
@@ -1,12 +1,5 @@
#include "Render.h"
#include "../PlatformTypes.h"
#include "SDL.h"
#include "SDL_error.h"
#include "SDL_events.h"
#include "SDL_stdinc.h"
#include "SDL_video.h"
#include "gl3_loader.h"
#include "4J_Render.h"
// undefine macros from header to avoid argument mismatch
#undef glGenTextures
@@ -23,33 +16,23 @@
#undef glVertexPointer
#undef glGenQueriesARB
#undef glGetQueryObjectuARB
#undef glEnable
#undef glDisable
#undef glBlendFunc
#undef glDepthMask
#undef glColorMask
#undef glLineWidth
#undef glFrontFace
#undef glPolygonOffset
#undef glStencilFunc
#undef glStencilMask
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define GLM_FORCE_RADIANS
#include <dlfcn.h>
#include <pthread.h>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <unordered_map>
#include <utility>
#include <vector>
#include <pthread.h>
#include <dlfcn.h>
C4JRender RenderManager;
@@ -59,20 +42,17 @@ C4JRender RenderManager;
#ifdef GLES
static const char* VERT_SRC =
#include "sdl2/shaders/vertex_es.vert"
#include "shaders/vertex_es.vert"
;
static const char* FRAG_SRC =
#include "sdl2/shaders/fragment_es.frag"
#include "shaders/fragment_es.frag"
;
#else
static const char* VERT_SRC =
#include "sdl2/shaders/vertex.vert"
#include "shaders/vertex.vert"
;
static const char* FRAG_SRC =
#include "sdl2/shaders/fragment.frag"
#include "shaders/fragment.frag"
;
#endif
@@ -102,45 +82,7 @@ static pthread_mutex_t s_sharedMtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t s_glCallMtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_t s_mainThread;
static bool s_mainThreadSet = false;
static thread_local unsigned int s_rs_dirty_mask = 0xFFFFFFFF;
struct GLShadowState {
bool blend;
bool cull;
bool depth;
bool polygon;
bool stencil;
GLint blendSrc;
GLint blendDst;
GLboolean depthMask;
GLboolean colorMask[4];
float lineWidth;
GLenum frontFace;
float polySlope;
float polyBias;
GLenum stencilFunc;
GLint stencilRef;
GLuint stencilMask;
GLuint stencilWriteMask;
};
static GLShadowState s_gl_state;
static unsigned int s_gl_shadow_mask = 0;
enum GLShadowBits {
SHADOW_BLEND = 1 << 0,
SHADOW_CULL = 1 << 1,
SHADOW_DEPTH = 1 << 2,
SHADOW_BLEND_FUNC = 1 << 3,
SHADOW_DEPTH_MASK = 1 << 4,
SHADOW_COLOR_MASK = 1 << 5,
SHADOW_LINE_WIDTH = 1 << 6,
SHADOW_FRONT_FACE = 1 << 7,
SHADOW_POLY_OFFSET = 1 << 8,
SHADOW_POLY_OFFSET_PARAMS = 1 << 9,
SHADOW_STENCIL = 1 << 10,
SHADOW_STENCIL_PARAMS = 1 << 11,
};
static thread_local bool s_rs_dirty = true;
static void onFramebufferResize(int w, int h) {
if (w < 1) w = 1;
@@ -269,9 +211,7 @@ static thread_local int s_matMode = 0; // 0=MV 1=proj 2=tex0 3=tex1
static thread_local bool s_normalMatDirty = true;
static thread_local glm::mat3 s_cachedNormalMat;
static thread_local float s_cachedNormalSign = 1.0f;
static thread_local bool s_matDirty = true;
static inline void markNormalDirty() { s_normalMatDirty = true; }
static inline void markMatrixDirty() { s_matDirty = true; }
static MatrixStack& activeStack() {
switch (s_matMode) {
@@ -286,23 +226,22 @@ static MatrixStack& activeStack() {
}
static void flushMatrices() {
if (s_matDirty) {
glm::mat4 mvp = s_proj.cur() * s_mv.cur();
glUniformMatrix4fv(s_shader.uMVP, 1, GL_FALSE, glm::value_ptr(mvp));
glUniformMatrix4fv(s_shader.uMV, 1, GL_FALSE,
glm::value_ptr(s_mv.cur()));
glm::mat4 mvp = s_proj.cur() * s_mv.cur();
glUniformMatrix4fv(s_shader.uMVP, 1, GL_FALSE, glm::value_ptr(mvp));
glUniformMatrix4fv(s_shader.uMV, 1, GL_FALSE, glm::value_ptr(s_mv.cur()));
// Send the texture matrix to the depths of hell...
glUniformMatrix4fv(s_shader.uTexMat0, 1, GL_FALSE,
glm::value_ptr(s_tex[0].cur()));
s_matDirty = false;
}
// Send the texture matrix to the depths of hell...
glUniformMatrix4fv(s_shader.uTexMat0, 1, GL_FALSE,
glm::value_ptr(s_tex[0].cur()));
if (s_shader.uNormalMatrix >= 0 && s_normalMatDirty) {
glm::mat3 m3 = glm::mat3(s_mv.cur());
s_cachedNormalMat = glm::transpose(glm::inverse(m3));
s_cachedNormalSign = glm::determinant(m3) < 0.0f ? -1.0f : 1.0f;
s_normalMatDirty = false;
// if (s_shader.uLighting)
if (s_shader.uNormalMatrix >= 0) {
if (s_normalMatDirty) {
glm::mat3 m3 = glm::mat3(s_mv.cur());
s_cachedNormalMat = glm::transpose(glm::inverse(m3));
s_cachedNormalSign = glm::determinant(m3) < 0.0f ? -1.0f : 1.0f;
s_normalMatDirty = false;
}
glUniformMatrix3fv(s_shader.uNormalMatrix, 1, GL_FALSE,
glm::value_ptr(s_cachedNormalMat));
glUniform1f(s_shader.uNormalSign, s_cachedNormalSign);
@@ -326,154 +265,27 @@ struct RenderState {
glm::vec4 lmt = {1, 1, 0, 0};
glm::vec2 globalLM = {240.f, 240.f}; // fullbright default
int activeTexture = 0;
// we MAKE sure everything is FINE.
bool operator!=(const RenderState& o) const {
return baseColor != o.baseColor || fogColor != o.fogColor ||
fogStart != o.fogStart || fogEnd != o.fogEnd ||
fogDensity != o.fogDensity || fogMode != o.fogMode ||
fogEnable != o.fogEnable || alphaRef != o.alphaRef ||
gamma != o.gamma || useTexture != o.useTexture ||
useLightmap != o.useLightmap || lighting != o.lighting ||
l0 != o.l0 || l1 != o.l1 || ldiff != o.ldiff || lamb != o.lamb ||
lmt != o.lmt || globalLM != o.globalLM ||
activeTexture != o.activeTexture;
}
};
enum RenderDirtyBits {
DIRTY_BASECOLOR = 1 << 0,
DIRTY_LIGHTING = 1 << 1,
DIRTY_FOG = 1 << 2,
DIRTY_ALPHA = 1 << 3,
DIRTY_GAMMA = 1 << 4,
DIRTY_TEXTURE = 1 << 5,
DIRTY_LMT = 1 << 6,
DIRTY_GLOBAL_LM = 1 << 7,
};
static inline void markDirty(unsigned int bit) { s_rs_dirty_mask |= bit; }
static thread_local RenderState s_rs;
static RenderState s_gpu_state;
static bool s_gpu_state_valid = false;
// track currently bound program to avoid iggy shitting up
static GLuint s_boundProgram = 0;
static void glShadowSetBlend(bool e) {
if (!(s_gl_shadow_mask & SHADOW_BLEND) || s_gl_state.blend != e) {
if (e)
::glEnable(GL_BLEND);
else
::glDisable(GL_BLEND);
s_gl_state.blend = e;
s_gl_shadow_mask |= SHADOW_BLEND;
}
}
static void glShadowSetCull(bool e) {
if (!(s_gl_shadow_mask & SHADOW_CULL) || s_gl_state.cull != e) {
if (e)
::glEnable(GL_CULL_FACE);
else
::glDisable(GL_CULL_FACE);
s_gl_state.cull = e;
s_gl_shadow_mask |= SHADOW_CULL;
}
}
static void glShadowSetDepthTest(bool e) {
if (!(s_gl_shadow_mask & SHADOW_DEPTH) || s_gl_state.depth != e) {
if (e)
::glEnable(GL_DEPTH_TEST);
else
::glDisable(GL_DEPTH_TEST);
s_gl_state.depth = e;
s_gl_shadow_mask |= SHADOW_DEPTH;
}
}
static void glShadowSetBlendFunc(GLint s, GLint d) {
if (!(s_gl_shadow_mask & SHADOW_BLEND_FUNC) ||
s_gl_state.blendSrc != s || s_gl_state.blendDst != d) {
::glBlendFunc(s, d);
s_gl_state.blendSrc = s;
s_gl_state.blendDst = d;
s_gl_shadow_mask |= SHADOW_BLEND_FUNC;
}
}
static void glShadowSetDepthMask(GLboolean e) {
if (!(s_gl_shadow_mask & SHADOW_DEPTH_MASK) ||
s_gl_state.depthMask != e) {
::glDepthMask(e);
s_gl_state.depthMask = e;
s_gl_shadow_mask |= SHADOW_DEPTH_MASK;
}
}
static void glShadowSetColorMask(GLboolean r, GLboolean g, GLboolean b,
GLboolean a) {
if (!(s_gl_shadow_mask & SHADOW_COLOR_MASK) ||
s_gl_state.colorMask[0] != r || s_gl_state.colorMask[1] != g ||
s_gl_state.colorMask[2] != b || s_gl_state.colorMask[3] != a) {
::glColorMask(r, g, b, a);
s_gl_state.colorMask[0] = r;
s_gl_state.colorMask[1] = g;
s_gl_state.colorMask[2] = b;
s_gl_state.colorMask[3] = a;
s_gl_shadow_mask |= SHADOW_COLOR_MASK;
}
}
static void glShadowSetLineWidth(float w) {
if (!(s_gl_shadow_mask & SHADOW_LINE_WIDTH) || s_gl_state.lineWidth != w) {
::glLineWidth(w);
s_gl_state.lineWidth = w;
s_gl_shadow_mask |= SHADOW_LINE_WIDTH;
}
}
static void glShadowSetFrontFace(GLenum mode) {
if (!(s_gl_shadow_mask & SHADOW_FRONT_FACE) ||
s_gl_state.frontFace != mode) {
::glFrontFace(mode);
s_gl_state.frontFace = mode;
s_gl_shadow_mask |= SHADOW_FRONT_FACE;
}
}
static void glShadowSetPolygonOffset(float slope, float bias) {
bool enable = (slope != 0.0f || bias != 0.0f);
if (!(s_gl_shadow_mask & SHADOW_POLY_OFFSET) ||
s_gl_state.polygon != enable) {
if (enable)
::glEnable(GL_POLYGON_OFFSET_FILL);
else
::glDisable(GL_POLYGON_OFFSET_FILL);
s_gl_state.polygon = enable;
s_gl_shadow_mask |= SHADOW_POLY_OFFSET;
}
if (enable) {
if (!(s_gl_shadow_mask & SHADOW_POLY_OFFSET_PARAMS) ||
s_gl_state.polySlope != slope || s_gl_state.polyBias != bias) {
::glPolygonOffset(slope, bias);
s_gl_state.polySlope = slope;
s_gl_state.polyBias = bias;
s_gl_shadow_mask |= SHADOW_POLY_OFFSET_PARAMS;
}
}
}
static void glShadowSetStencil(GLenum fn, uint8_t ref, uint8_t fmask,
uint8_t wmask) {
if (!(s_gl_shadow_mask & SHADOW_STENCIL) || !s_gl_state.stencil) {
::glEnable(GL_STENCIL_TEST);
s_gl_state.stencil = true;
s_gl_shadow_mask |= SHADOW_STENCIL;
}
if (!(s_gl_shadow_mask & SHADOW_STENCIL_PARAMS) ||
s_gl_state.stencilFunc != fn || s_gl_state.stencilRef != (GLint)ref ||
s_gl_state.stencilMask != fmask ||
s_gl_state.stencilWriteMask != wmask) {
::glStencilFunc(fn, ref, fmask);
::glStencilMask(wmask);
s_gl_state.stencilFunc = fn;
s_gl_state.stencilRef = (GLint)ref;
s_gl_state.stencilMask = fmask;
s_gl_state.stencilWriteMask = wmask;
s_gl_shadow_mask |= SHADOW_STENCIL_PARAMS;
}
}
static thread_local bool s_chunkOffsetValid = false;
static thread_local glm::vec3 s_chunkOffset;
static void pushRenderState() {
if (!s_shader.prog) return;
@@ -481,52 +293,35 @@ static void pushRenderState() {
if (s_boundProgram != s_shader.prog) {
glUseProgram(s_shader.prog);
s_boundProgram = s_shader.prog;
s_matDirty = true;
s_normalMatDirty = true;
s_rs_dirty_mask = 0xFFFFFFFF;
}
if (s_rs_dirty_mask) {
if (s_rs_dirty_mask & DIRTY_BASECOLOR)
glUniform4fv(s_shader.uBaseColor, 1,
glm::value_ptr(s_rs.baseColor));
if (s_rs_dirty_mask & DIRTY_LIGHTING) {
glUniform1i(s_shader.uLighting, s_rs.lighting ? 1 : 0);
glUniform3fv(s_shader.uLight0Dir, 1, glm::value_ptr(s_rs.l0));
glUniform3fv(s_shader.uLight1Dir, 1, glm::value_ptr(s_rs.l1));
glUniform3fv(s_shader.uLightDiffuse, 1, glm::value_ptr(s_rs.ldiff));
glUniform3fv(s_shader.uLightAmbient, 1, glm::value_ptr(s_rs.lamb));
}
if (s_rs_dirty_mask & DIRTY_FOG) {
glUniform1i(s_shader.uFogMode, s_rs.fogMode);
glUniform1f(s_shader.uFogStart, s_rs.fogStart);
glUniform1f(s_shader.uFogEnd, s_rs.fogEnd);
glUniform1f(s_shader.uFogDensity, s_rs.fogDensity);
glUniform4fv(s_shader.uFogColor, 1,
glm::value_ptr(s_rs.fogColor));
glUniform1i(s_shader.uFogEnable, s_rs.fogEnable ? 1 : 0);
}
if (s_rs_dirty_mask & DIRTY_TEXTURE) {
glUniform1i(s_shader.uUseTexture, s_rs.useTexture ? 1 : 0);
glUniform1i(s_shader.uUseLightmap, s_rs.useLightmap ? 1 : 0);
}
if (s_rs_dirty_mask & DIRTY_ALPHA)
glUniform1f(s_shader.uAlphaRef, s_rs.alphaRef);
if (s_rs_dirty_mask & DIRTY_GAMMA)
glUniform1f(s_shader.uInvGamma, 1.0f / s_rs.gamma);
if (s_rs_dirty_mask & DIRTY_LMT)
glUniform4fv(s_shader.uLMTransform, 1,
glm::value_ptr(s_rs.lmt));
if (s_rs_dirty_mask & DIRTY_GLOBAL_LM)
glUniform2fv(s_shader.uGlobalLM, 1,
glm::value_ptr(s_rs.globalLM));
s_rs_dirty_mask = 0;
if (!s_gpu_state_valid || s_gpu_state != s_rs) {
glUniform4fv(s_shader.uBaseColor, 1, glm::value_ptr(s_rs.baseColor));
glUniform1i(s_shader.uLighting, s_rs.lighting ? 1 : 0);
glUniform3fv(s_shader.uLight0Dir, 1, glm::value_ptr(s_rs.l0));
glUniform3fv(s_shader.uLight1Dir, 1, glm::value_ptr(s_rs.l1));
glUniform3fv(s_shader.uLightDiffuse, 1, glm::value_ptr(s_rs.ldiff));
glUniform3fv(s_shader.uLightAmbient, 1, glm::value_ptr(s_rs.lamb));
glUniform1i(s_shader.uFogMode, s_rs.fogMode);
glUniform1f(s_shader.uFogStart, s_rs.fogStart);
glUniform1f(s_shader.uFogEnd, s_rs.fogEnd);
glUniform1f(s_shader.uFogDensity, s_rs.fogDensity);
glUniform4fv(s_shader.uFogColor, 1, glm::value_ptr(s_rs.fogColor));
glUniform1i(s_shader.uFogEnable, s_rs.fogEnable ? 1 : 0);
glUniform1i(s_shader.uUseTexture, s_rs.useTexture ? 1 : 0);
glUniform1i(s_shader.uUseLightmap, s_rs.useLightmap ? 1 : 0);
glUniform1f(s_shader.uAlphaRef, s_rs.alphaRef);
glUniform1f(s_shader.uInvGamma, 1.0f / s_rs.gamma);
glUniform4fv(s_shader.uLMTransform, 1, glm::value_ptr(s_rs.lmt));
glUniform2fv(s_shader.uGlobalLM, 1, glm::value_ptr(s_rs.globalLM));
s_gpu_state = s_rs;
s_gpu_state_valid = true;
s_rs_dirty = false;
}
flushMatrices();
}
static GLuint s_sVAO_std = 0, s_sVBO_std = 0;
static GLsizeiptr s_streamVBOSize = 0;
static void bindStdAttribs() {
glEnableVertexAttribArray(0);
@@ -667,18 +462,18 @@ void C4JRender::Initialise() {
int fw, fh;
SDL_GetWindowSize(s_window, &fw, &fh);
onFramebufferResize(fw, fh);
glShadowSetDepthTest(true);
::glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
#ifdef GLES
glClearDepthf(1.0f);
#else
glClearDepth(1.0);
#endif
glShadowSetBlend(true);
glShadowSetBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadowSetCull(true);
::glCullFace(GL_BACK);
::glClearColor(0, 0, 0, 1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glClearColor(0, 0, 0, 1);
glViewport(0, 0, s_windowWidth, s_windowHeight);
s_shader.build(VERT_SRC, FRAG_SRC);
initStreamingVAOs();
@@ -743,7 +538,6 @@ void C4JRender::InitialiseContext() {
}
void C4JRender::StartFrame() {
Set_matrixDirty();
int w, h;
SDL_GetWindowSize(s_window, &w, &h);
s_windowWidth = w > 0 ? w : 1;
@@ -903,10 +697,10 @@ void C4JRender::DrawVertices(ePrimitiveType ptype, int count, void* dataIn,
glBindVertexArray(s_sVAO_std);
glBindBuffer(GL_ARRAY_BUFFER, s_sVBO_std);
// Standard orphaning
// orphan buffer
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)bytes, nullptr, GL_STREAM_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, (GLsizeiptr)bytes, dataIn);
s_streamVBOSize = (GLsizeiptr)bytes;
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)bytes, dataIn, GL_STREAM_DRAW);
bindStdAttribs();
glDrawArrays(glMode, 0, count);
@@ -942,16 +736,6 @@ void C4JRender::CBuffDelete(int first, int count) {
pthread_mutex_unlock(&s_glCallMtx);
}
void C4JRender::CBuffDeleteAll() {
pthread_mutex_lock(&s_glCallMtx);
for (auto& kv : s_chunkPool) {
kv.second.destroy();
}
s_chunkPool.clear();
s_nextListBase = 1;
pthread_mutex_unlock(&s_glCallMtx);
}
void C4JRender::CBuffStart(int index, bool) {
s_recListId = index;
s_recVerts.clear();
@@ -980,10 +764,7 @@ void C4JRender::CBuffEnd() {
void C4JRender::CBuffClear(int index) {
pthread_mutex_lock(&s_glCallMtx);
auto it = s_chunkPool.find(index);
if (it != s_chunkPool.end()) {
it->second.destroy();
s_chunkPool.erase(it);
}
if (it != s_chunkPool.end()) it->second.destroy();
pthread_mutex_unlock(&s_glCallMtx);
}
@@ -1037,48 +818,39 @@ void C4JRender::MatrixMode(int t) {
void C4JRender::MatrixSetIdentity() {
activeStack().load(glm::mat4(1.f));
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixPush() {
activeStack().push();
// push doesn't change cur() so no dirty needed but mark anyway to be safe
// ;w;
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixPop() {
activeStack().pop();
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixTranslate(float x, float y, float z) {
activeStack().mul(glm::translate(glm::mat4(1.f), {x, y, z}));
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixRotate(float a, float x, float y, float z) {
activeStack().mul(glm::rotate(glm::mat4(1.f), a, {x, y, z}));
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixScale(float x, float y, float z) {
activeStack().mul(glm::scale(glm::mat4(1.f), {x, y, z}));
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
void C4JRender::MatrixPerspective(float fovy, float asp, float zn, float zf) {
s_proj.cur() = glm::perspective(glm::radians(fovy), asp, zn, zf);
markMatrixDirty();
}
void C4JRender::MatrixOrthogonal(float l, float r, float b, float t, float zn,
float zf) {
s_proj.cur() = glm::ortho(l, r, b, t, zn, zf);
markMatrixDirty();
}
void C4JRender::MatrixMult(float* m) {
activeStack().mul(glm::make_mat4(m));
markMatrixDirty();
if (s_matMode == 0) markNormalDirty();
}
const float* C4JRender::MatrixGet(int t) {
@@ -1093,11 +865,8 @@ const float* C4JRender::MatrixGet(int t) {
void C4JRender::Set_matrixDirty() {
// iggy wipes opengl state
s_boundProgram = 0;
s_rs_dirty_mask = 0xFFFFFFFF;
s_gl_shadow_mask = 0;
s_normalMatDirty = true; // normal matrix dirt after iggy reset
s_matDirty = true;
s_chunkOffsetValid = false;
s_gpu_state_valid = false;
s_normalMatDirty = true; // normal matrix dirt after iggy reset
if (s_shader.prog) {
glUseProgram(s_shader.prog);
s_boundProgram = s_shader.prog;
@@ -1111,61 +880,56 @@ void C4JRender::SetClearColour(const float c[4]) {
bool C4JRender::IsWidescreen() { return true; }
bool C4JRender::IsHiDef() { return true; }
void C4JRender::StateSetColour(float r, float g, float b, float a) {
glm::vec4 v = {r, g, b, a};
if (s_rs.baseColor != v) {
s_rs.baseColor = v;
markDirty(DIRTY_BASECOLOR);
}
s_rs.baseColor = {r, g, b, a};
}
void C4JRender::SetChunkOffset(float x, float y, float z) {
if (s_shader.uChunkOffset < 0) return;
glm::vec3 v = {x, y, z};
if (!s_chunkOffsetValid || s_chunkOffset != v) {
s_chunkOffset = v;
s_chunkOffsetValid = true;
}
if (s_boundProgram == s_shader.prog) {
glUniform3f(s_shader.uChunkOffset, x, y, z);
}
if (s_shader.uChunkOffset >= 0) glUniform3f(s_shader.uChunkOffset, x, y, z);
}
void C4JRender::StateSetDepthMask(bool e) {
glShadowSetDepthMask(e ? GL_TRUE : GL_FALSE);
glDepthMask(e ? GL_TRUE : GL_FALSE);
}
void C4JRender::StateSetBlendEnable(bool e) { glShadowSetBlend(e); }
void C4JRender::StateSetBlendFunc(int s, int d) {
glShadowSetBlendFunc(s, d);
void C4JRender::StateSetBlendEnable(bool e) {
if (e)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
}
void C4JRender::StateSetDepthFunc(int f) { ::glDepthFunc(f); }
void C4JRender::StateSetFaceCull(bool e) { glShadowSetCull(e); }
void C4JRender::StateSetFaceCullCW(bool e) {
glShadowSetFrontFace(e ? GL_CW : GL_CCW);
void C4JRender::StateSetBlendFunc(int s, int d) { glBlendFunc(s, d); }
void C4JRender::StateSetDepthFunc(int f) { glDepthFunc(f); }
void C4JRender::StateSetFaceCull(bool e) {
if (e)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
}
void C4JRender::StateSetFaceCullCW(bool e) { glFrontFace(e ? GL_CW : GL_CCW); }
void C4JRender::StateSetLineWidth(float w) {
#ifndef GLES
glShadowSetLineWidth(w);
glLineWidth(w);
#else
(void)w;
#endif
}
void C4JRender::StateSetWriteEnable(bool r, bool g, bool b, bool a) {
glShadowSetColorMask(r, g, b, a);
glColorMask(r, g, b, a);
}
void C4JRender::StateSetDepthTestEnable(bool e) {
if (e)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
}
void C4JRender::StateSetDepthTestEnable(bool e) { glShadowSetDepthTest(e); }
void C4JRender::StateSetAlphaTestEnable(bool e) {
float v = e ? 0.1f : 0.f;
if (s_rs.alphaRef != v) {
s_rs.alphaRef = v;
markDirty(DIRTY_ALPHA);
}
}
void C4JRender::StateSetAlphaFunc(int, float p) {
if (s_rs.alphaRef != p) {
s_rs.alphaRef = p;
markDirty(DIRTY_ALPHA);
}
s_rs.alphaRef = e ? 0.1f : 0.f;
}
void C4JRender::StateSetAlphaFunc(int, float p) { s_rs.alphaRef = p; }
void C4JRender::StateSetDepthSlopeAndBias(float s, float b) {
glShadowSetPolygonOffset(s, b);
if (s || b) {
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(s, b);
} else {
glDisable(GL_POLYGON_OFFSET_FILL);
}
}
void C4JRender::StateSetBlendFactor(unsigned int col) {
float a = ((col >> 24) & 0xFF) / 255.f;
@@ -1174,96 +938,58 @@ void C4JRender::StateSetBlendFactor(unsigned int col) {
float b = (col & 0xFF) / 255.f;
glBlendColor(r, g, b, a);
}
void C4JRender::StateSetFogEnable(bool e) {
if (s_rs.fogEnable != e) {
s_rs.fogEnable = e;
markDirty(DIRTY_FOG);
}
}
void C4JRender::StateSetFogEnable(bool e) { s_rs.fogEnable = e; }
void C4JRender::StateSetFogMode(int mode) {
int v = (mode == GL_LINEAR) ? 1 : (mode == GL_EXP) ? 2 : (mode == 0x0801) ? 3 : 0;
if (s_rs.fogMode != v) {
s_rs.fogMode = v;
markDirty(DIRTY_FOG);
}
}
void C4JRender::StateSetFogNearDistance(float d) {
if (s_rs.fogStart != d) {
s_rs.fogStart = d;
markDirty(DIRTY_FOG);
}
}
void C4JRender::StateSetFogFarDistance(float d) {
if (s_rs.fogEnd != d) {
s_rs.fogEnd = d;
markDirty(DIRTY_FOG);
}
}
void C4JRender::StateSetFogDensity(float d) {
if (s_rs.fogDensity != d) {
s_rs.fogDensity = d;
markDirty(DIRTY_FOG);
}
s_rs.fogMode = (mode == GL_LINEAR) ? 1
: (mode == GL_EXP) ? 2
: (mode == 0x0801) ? 3
: 0;
}
void C4JRender::StateSetFogNearDistance(float d) { s_rs.fogStart = d; }
void C4JRender::StateSetFogFarDistance(float d) { s_rs.fogEnd = d; }
void C4JRender::StateSetFogDensity(float d) { s_rs.fogDensity = d; }
void C4JRender::StateSetFogColour(float r, float g, float b) {
glm::vec4 v = {r, g, b, 1};
if (s_rs.fogColor != v) {
s_rs.fogColor = v;
markDirty(DIRTY_FOG);
}
s_rs.fogColor = {r, g, b, 1};
}
void C4JRender::StateSetLightingEnable(bool e) {
if (s_rs.lighting != e) {
s_rs.lighting = e;
markDirty(DIRTY_LIGHTING);
}
s_rs.lighting = e;
s_rs_dirty = true;
}
void C4JRender::StateSetLightColour(int, float r, float g, float b) {
glm::vec3 v = {r, g, b};
if (s_rs.ldiff != v) {
s_rs.ldiff = v;
markDirty(DIRTY_LIGHTING);
}
s_rs.ldiff = {r, g, b};
s_rs_dirty = true;
}
void C4JRender::StateSetLightAmbientColour(float r, float g, float b) {
glm::vec3 v = {r, g, b};
if (s_rs.lamb != v) {
s_rs.lamb = v;
markDirty(DIRTY_LIGHTING);
}
s_rs.lamb = {r, g, b};
s_rs_dirty = true;
}
void C4JRender::StateSetLightDirection(int light, float x, float y, float z) {
glm::vec3 d = glm::normalize(glm::mat3(s_mv.cur()) * glm::vec3(x, y, z));
if (light == 0) {
if (s_rs.l0 != d) {
s_rs.l0 = d;
markDirty(DIRTY_LIGHTING);
}
} else {
if (s_rs.l1 != d) {
s_rs.l1 = d;
markDirty(DIRTY_LIGHTING);
}
}
if (light == 0)
s_rs.l0 = d;
else
s_rs.l1 = d;
}
void C4JRender::StateSetViewport(eViewportType) {
glViewport(0, 0, s_windowWidth, s_windowHeight);
}
void C4JRender::StateSetVertexTextureUV(float u, float v) {
glm::vec2 val = {u, v};
if (s_rs.globalLM != val) {
s_rs.globalLM = val;
markDirty(DIRTY_GLOBAL_LM);
}
s_rs.globalLM = {u, v};
}
void C4JRender::StateSetStencil(int fn, uint8_t ref, uint8_t fmask,
uint8_t wmask) {
glShadowSetStencil(fn, ref, fmask, wmask);
glEnable(GL_STENCIL_TEST);
glStencilFunc(fn, ref, fmask);
glStencilMask(wmask);
}
void C4JRender::StateSetTextureEnable(bool e) {
if (s_rs.activeTexture == 0 && s_rs.useTexture != e) {
if (s_rs.activeTexture == 0) {
s_rs.useTexture = e;
markDirty(DIRTY_TEXTURE);
if (s_shader.prog) {
glUseProgram(s_shader.prog);
s_boundProgram = s_shader.prog;
glUniform1i(s_shader.uUseTexture, e ? 1 : 0);
}
}
}
void C4JRender::StateSetActiveTexture(int tex) {
@@ -1285,10 +1011,7 @@ void C4JRender::TextureBind(int idx) {
}
void C4JRender::TextureBindVertex(int idx, bool scaleLight) {
if (idx < 0) {
if (s_rs.useLightmap) {
s_rs.useLightmap = false;
markDirty(DIRTY_TEXTURE);
}
s_rs.useLightmap = false;
glActiveTexture(GL_TEXTURE0);
return;
}
@@ -1299,17 +1022,9 @@ void C4JRender::TextureBindVertex(int idx, bool scaleLight) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glActiveTexture(GL_TEXTURE0);
if (!s_rs.useLightmap) {
s_rs.useLightmap = true;
markDirty(DIRTY_TEXTURE);
}
glm::vec4 newLmt =
scaleLight ? glm::vec4{1.f, 1.f, 8.f / 256.f, 8.f / 256.f}
: glm::vec4{1.f, 1.f, 0.f, 0.f};
if (s_rs.lmt != newLmt) {
s_rs.lmt = newLmt;
markDirty(DIRTY_LMT);
}
s_rs.useLightmap = true;
s_rs.lmt = scaleLight ? glm::vec4{1.f, 1.f, 8.f / 256.f, 8.f / 256.f}
: glm::vec4{1.f, 1.f, 0.f, 0.f};
}
void C4JRender::TextureSetTextureLevels(int l) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, l > 0 ? l - 1 : 0);
@@ -1373,7 +1088,6 @@ int C4JRender::LoadTextureData(uint8_t* pb, uint32_t nb, D3DXIMAGE_INFO* i,
return hr;
}
// TODO: TO REMOVE SOON.
void C4JRender::UpdateGamma(unsigned short usGamma) {
constexpr unsigned short GAMMA_MAX = 32768;
s_rs.gamma = 0.5f + ((float)(usGamma) * (1.0f / GAMMA_MAX));
@@ -1470,4 +1184,4 @@ void glLightModelfv(GLenum pname, const GLfloat* params) {
void glShadeModel(GLenum) {}
void glColorMaterial(GLenum, GLenum) {}
void glNormal3f(GLfloat, GLfloat, GLfloat) {}
}
}
@@ -1,16 +1,42 @@
#pragma once
#include "gl3_loader.h"
// NOTE: gl3_loader.h must be included before these two
#include <GL/gl.h>
#include <GL/glu.h>
#include <cstdint>
#include <cstdlib>
#include "../IPlatformRenderer.h"
#ifdef __linux__
#include <GL/gl.h>
#include <GL/glu.h>
#endif
class C4JRender : public IPlatformRenderer {
class ImageFileBuffer {
public:
enum EImageType { e_typePNG, e_typeJPG };
EImageType m_type;
void* m_pBuffer;
int m_bufferSize;
int GetType() { return m_type; }
void* GetBufferPointer() { return m_pBuffer; }
int GetBufferSize() { return m_bufferSize; }
void Release() {
std::free(m_pBuffer);
m_pBuffer = nullptr;
}
bool Allocated() { return m_pBuffer != nullptr; }
};
typedef struct {
int Width;
int Height;
} D3DXIMAGE_INFO;
typedef struct _XSOCIAL_PREVIEWIMAGE {
std::uint8_t* pBytes;
std::uint32_t Pitch;
std::uint32_t Width;
std::uint32_t Height;
} XSOCIAL_PREVIEWIMAGE, *PXSOCIAL_PREVIEWIMAGE;
class C4JRender {
public:
void Tick();
void UpdateGamma(unsigned short usGamma);
@@ -52,14 +78,60 @@ public:
void BeginConditionalRendering(int identifier);
void EndConditionalRendering();
// Vertex data handling
typedef enum {
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1, // Position 3 x float, texture 2 x
// float, colour 4 x byte, normal 4 x
// byte, padding 1 32-bit word
VERTEX_TYPE_COMPRESSED, // Compressed format - see comment at top of
// VS_PS3_TS2_CS1.hlsl for description of
// layout
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT, // as
// VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1
// with lighting applied,
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN, // as
// VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1
// with tex gen
VERTEX_TYPE_COUNT
} eVertexType;
// Pixel shader
typedef enum {
PIXEL_SHADER_TYPE_STANDARD,
PIXEL_SHADER_TYPE_PROJECTION,
PIXEL_SHADER_TYPE_FORCELOD,
PIXEL_SHADER_COUNT
} ePixelShaderType;
typedef enum {
VIEWPORT_TYPE_FULLSCREEN,
VIEWPORT_TYPE_SPLIT_TOP,
VIEWPORT_TYPE_SPLIT_BOTTOM,
VIEWPORT_TYPE_SPLIT_LEFT,
VIEWPORT_TYPE_SPLIT_RIGHT,
VIEWPORT_TYPE_QUADRANT_TOP_LEFT,
VIEWPORT_TYPE_QUADRANT_TOP_RIGHT,
VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT,
VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT,
} eViewportType;
typedef enum {
PRIMITIVE_TYPE_TRIANGLE_LIST,
PRIMITIVE_TYPE_TRIANGLE_STRIP,
PRIMITIVE_TYPE_TRIANGLE_FAN,
PRIMITIVE_TYPE_QUAD_LIST,
PRIMITIVE_TYPE_LINE_LIST,
PRIMITIVE_TYPE_LINE_STRIP,
PRIMITIVE_TYPE_COUNT
} ePrimitiveType;
void DrawVertices(ePrimitiveType PrimitiveType, int count, void* dataIn,
eVertexType vType, ePixelShaderType psType);
eVertexType vType, C4JRender::ePixelShaderType psType);
// Command buffers
void CBuffLockStaticCreations();
int CBuffCreate(int count);
void CBuffDelete(int first, int count);
void CBuffDeleteAll();
void CBuffStart(int index, bool full = false);
void CBuffClear(int index);
int CBuffSize(int index);
@@ -69,6 +141,18 @@ public:
void CBuffDeferredModeStart();
void CBuffDeferredModeEnd();
typedef enum {
TEXTURE_FORMAT_RxGyBzAw, // Normal 32-bit RGBA texture, 8 bits per
// component
/* Don't think these are all directly available on D3D 11 - leaving for
now TEXTURE_FORMAT_R0G0B0Ax, // One 8-bit component mapped to
alpha channel, R=G=B=0 TEXTURE_FORMAT_R1G1B1Ax, // One 8-bit
component mapped to alpha channel, R=G=B=1 TEXTURE_FORMAT_RxGxBxAx,
// One 8-bit component mapped to all channels
*/
MAX_TEXTURE_FORMATS
} eTextureFormat;
// Textures
int TextureCreate();
void TextureFree(int idx);
@@ -491,12 +575,6 @@ extern C4JRender RenderManager;
RenderManager.StateSetAlphaTestEnable(true); \
else if ((cap) == 0x0DE1 /*GL_TEXTURE_2D*/) \
RenderManager.StateSetTextureEnable(true); \
else if ((cap) == 0x0BE2 /*GL_BLEND*/) \
RenderManager.StateSetBlendEnable(true); \
else if ((cap) == 0x0B44 /*GL_CULL_FACE*/) \
RenderManager.StateSetFaceCull(true); \
else if ((cap) == 0x0B71 /*GL_DEPTH_TEST*/) \
RenderManager.StateSetDepthTestEnable(true); \
else if ((cap) == 0x4000 /*GL_LIGHT0*/) \
RenderManager.StateSetLightEnable(0, true); \
else if ((cap) == 0x4001 /*GL_LIGHT1*/) \
@@ -523,12 +601,6 @@ extern C4JRender RenderManager;
RenderManager.StateSetAlphaTestEnable(false); \
else if ((cap) == 0x0DE1 /*GL_TEXTURE_2D*/) \
RenderManager.StateSetTextureEnable(false); \
else if ((cap) == 0x0BE2 /*GL_BLEND*/) \
RenderManager.StateSetBlendEnable(false); \
else if ((cap) == 0x0B44 /*GL_CULL_FACE*/) \
RenderManager.StateSetFaceCull(false); \
else if ((cap) == 0x0B71 /*GL_DEPTH_TEST*/) \
RenderManager.StateSetDepthTestEnable(false); \
else if ((cap) == 0x4000 /*GL_LIGHT0*/) \
RenderManager.StateSetLightEnable(0, false); \
else if ((cap) == 0x4001 /*GL_LIGHT1*/) \
@@ -5,7 +5,6 @@
#include <GL/gl.h>
#include <GL/glext.h>
#include <SDL2/SDL.h>
#include <cstdio>
#ifndef GL_ARRAY_BUFFER
#define GL_ARRAY_BUFFER 0x8892
+37
View File
@@ -0,0 +1,37 @@
# isolate em
_sdl2 = dependency('sdl2')
_threads = dependency('threads')
_glm = dependency('glm')
_defs = []
if get_option('renderer') == 'gles'
_gl = dependency('glesv2', required: true)
_defs += ['-DGLES']
else
_gl = dependency('gl', required: true)
endif
render_sources = files(
'4J_Render.cpp',
'stdafx.cpp',
'stubs.cpp',
)
lib_render = static_library(
'4J_Render',
render_sources,
include_directories: include_directories('.'),
dependencies: [_sdl2, _gl, _threads, _glm],
cpp_args: _defs
+ [
'-fpermissive',
'-Wshift-count-overflow',
'-pipe',
'-include', meson.current_source_dir() / 'stdafx.h',
],
)
render_dep = declare_dependency(
link_with: lib_render,
include_directories: include_directories('.'),
)
@@ -46,7 +46,7 @@ void main() {
float d0 = max(dot(n, uLight0Dir), 0.0);
float d1 = max(dot(n, uLight1Dir), 0.0);
vColor = vec4(col.rgb * (uLightAmbient + uLightDiffuse * clamp(d0 + d1, 0.0, 1.0)), col.a);
vColor = vec4(col.rgb * (uLightAmbient + uLightDiffuse * (d0 + d1)), col.a);
} else {
vColor = col;
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
#include "stdafx.h"
+6
View File
@@ -0,0 +1,6 @@
#ifndef _4J_RENDER_STADAFX_H
#define _4J_RENDER_STADAFX_H
#include "../4J.Common/4J_Compat.h"
#endif //_4J_RENDER_STADAFX_H
@@ -1,4 +1,4 @@
#include "Render.h"
#include "4J_Render.h"
// Command Buffers
void C4JRender::CBuffLockStaticCreations() {}
+224
View File
@@ -0,0 +1,224 @@
#include "4J_Storage.h"
#include <cstring>
#include <vector>
#include <string>
C4JStorage StorageManager;
static XMARKETPLACE_CONTENTOFFER_INFO s_dummyOffer = {};
static XCONTENT_DATA s_dummyContentData = {};
C4JStorage::C4JStorage() : m_pStringTable(nullptr) {}
void C4JStorage::Tick(void) {}
C4JStorage::EMessageResult C4JStorage::RequestMessageBox(
unsigned int uiTitle, unsigned int uiText, unsigned int* uiOptionA,
unsigned int uiOptionC, unsigned int pad,
int (*Func)(void*, int, const C4JStorage::EMessageResult), void* lpParam,
C4JStringTable* pStringTable, wchar_t* pwchFormatString,
unsigned int focusButton) {
return EMessage_ResultAccept;
}
C4JStorage::EMessageResult C4JStorage::GetMessageBoxResult() {
return EMessage_Undefined;
}
bool C4JStorage::SetSaveDevice(int (*Func)(void*, const bool), void* lpParam,
bool bForceResetOfSaveDevice) {
return true;
}
void C4JStorage::Init(unsigned int uiSaveVersion,
const wchar_t* pwchDefaultSaveName, char* pszSavePackName,
int iMinimumSaveSize,
int (*Func)(void*, const ESavingMessage, int),
void* lpParam, const char* szGroupID) {}
void C4JStorage::ResetSaveData() {}
void C4JStorage::SetDefaultSaveNameForKeyboardDisplay(
const wchar_t* pwchDefaultSaveName) {}
void C4JStorage::SetSaveTitle(const wchar_t* pwchDefaultSaveName) {}
bool C4JStorage::GetSaveUniqueNumber(int* piVal) {
if (piVal) *piVal = 0;
return true;
}
bool C4JStorage::GetSaveUniqueFilename(char* pszName) {
if (pszName) pszName[0] = '\0';
return true;
}
void C4JStorage::SetSaveUniqueFilename(char* szFilename) {}
void C4JStorage::SetState(ESaveGameControlState eControlState,
int (*Func)(void*, const bool), void* lpParam) {}
void C4JStorage::SetSaveDisabled(bool bDisable) {}
bool C4JStorage::GetSaveDisabled(void) { return false; }
unsigned int C4JStorage::GetSaveSize() { return 0; }
void C4JStorage::GetSaveData(void* pvData, unsigned int* puiBytes) {
if (puiBytes) *puiBytes = 0;
}
void* C4JStorage::AllocateSaveData(unsigned int uiBytes) {
return malloc(uiBytes);
}
void C4JStorage::SetSaveImages(std::uint8_t* pbThumbnail,
unsigned int thumbnailBytes,
std::uint8_t* pbImage, unsigned int imageBytes,
std::uint8_t* pbTextData,
unsigned int textDataBytes) {}
C4JStorage::ESaveGameState C4JStorage::SaveSaveData(int (*Func)(void*,
const bool),
void* lpParam) {
return ESaveGame_Idle;
}
void C4JStorage::CopySaveDataToNewSave(std::uint8_t* pbThumbnail,
unsigned int cbThumbnail,
wchar_t* wchNewName,
int (*Func)(void* lpParam, bool),
void* lpParam) {}
void C4JStorage::SetSaveDeviceSelected(unsigned int uiPad, bool bSelected) {}
bool C4JStorage::GetSaveDeviceSelected(unsigned int iPad) { return true; }
C4JStorage::ESaveGameState C4JStorage::DoesSaveExist(bool* pbExists) {
if (pbExists) *pbExists = false;
return ESaveGame_Idle;
}
bool C4JStorage::EnoughSpaceForAMinSaveGame() { return true; }
void C4JStorage::SetSaveMessageVPosition(float fY) {}
C4JStorage::ESaveGameState C4JStorage::GetSavesInfo(
int iPad,
int (*Func)(void* lpParam, SAVE_DETAILS* pSaveDetails, const bool),
void* lpParam, char* pszSavePackName) {
return ESaveGame_Idle;
}
PSAVE_DETAILS C4JStorage::ReturnSavesInfo() { return nullptr; }
void C4JStorage::ClearSavesInfo() {}
C4JStorage::ESaveGameState C4JStorage::LoadSaveDataThumbnail(
PSAVE_INFO pSaveInfo,
int (*Func)(void* lpParam, std::uint8_t* thumbnailData,
unsigned int thumbnailBytes),
void* lpParam) {
return ESaveGame_Idle;
}
void C4JStorage::GetSaveCacheFileInfo(unsigned int fileIndex,
XCONTENT_DATA& xContentData) {
memset(&xContentData, 0, sizeof(xContentData));
}
void C4JStorage::GetSaveCacheFileInfo(unsigned int fileIndex,
std::uint8_t** ppbImageData,
unsigned int* pImageBytes) {
if (ppbImageData) *ppbImageData = nullptr;
if (pImageBytes) *pImageBytes = 0;
}
C4JStorage::ESaveGameState C4JStorage::LoadSaveData(
PSAVE_INFO pSaveInfo, int (*Func)(void* lpParam, const bool, const bool),
void* lpParam) {
return ESaveGame_Idle;
}
C4JStorage::ESaveGameState C4JStorage::DeleteSaveData(PSAVE_INFO pSaveInfo,
int (*Func)(void* lpParam,
const bool),
void* lpParam) {
return ESaveGame_Idle;
}
void C4JStorage::RegisterMarketplaceCountsCallback(
int (*Func)(void* lpParam, C4JStorage::DLC_TMS_DETAILS*, int),
void* lpParam) {}
void C4JStorage::SetDLCPackageRoot(char* pszDLCRoot) {}
C4JStorage::EDLCStatus C4JStorage::GetDLCOffers(
int iPad, int (*Func)(void*, int, std::uint32_t, int), void* lpParam,
std::uint32_t dwOfferTypesBitmask) {
return EDLC_NoOffers;
}
unsigned int C4JStorage::CancelGetDLCOffers() { return 0; }
void C4JStorage::ClearDLCOffers() {}
XMARKETPLACE_CONTENTOFFER_INFO& C4JStorage::GetOffer(unsigned int dw) {
return s_dummyOffer;
}
int C4JStorage::GetOfferCount() { return 0; }
unsigned int C4JStorage::InstallOffer(int iOfferIDC, std::uint64_t* ullOfferIDA,
int (*Func)(void*, int, int),
void* lpParam, bool bTrial) {
return 0;
}
unsigned int C4JStorage::GetAvailableDLCCount(int iPad) { return 0; }
C4JStorage::EDLCStatus C4JStorage::GetInstalledDLC(int iPad,
int (*Func)(void*, int, int),
void* lpParam) {
if (Func) {
Func(lpParam, 0, iPad);
}
return EDLC_NoInstalledDLC;
}
XCONTENT_DATA& C4JStorage::GetDLC(unsigned int dw) {
return s_dummyContentData;
}
std::uint32_t C4JStorage::MountInstalledDLC(
int iPad, std::uint32_t dwDLC,
int (*Func)(void*, int, std::uint32_t, std::uint32_t), void* lpParam,
const char* szMountDrive) {
return 0;
}
unsigned int C4JStorage::UnmountInstalledDLC(const char* szMountDrive) {
return 0;
}
void C4JStorage::GetMountedDLCFileList(const char* szMountDrive,
std::vector<std::string>& fileList) {
fileList.clear();
}
std::string C4JStorage::GetMountedPath(std::string szMount) { return ""; }
C4JStorage::ETMSStatus C4JStorage::ReadTMSFile(
int iQuadrant, eGlobalStorage eStorageFacility,
C4JStorage::eTMS_FileType eFileType, wchar_t* pwchFilename,
std::uint8_t** ppBuffer, unsigned int* pBufferSize,
int (*Func)(void*, wchar_t*, int, bool, int), void* lpParam, int iAction) {
return ETMSStatus_Fail;
}
bool C4JStorage::WriteTMSFile(int iQuadrant, eGlobalStorage eStorageFacility,
wchar_t* pwchFilename, std::uint8_t* pBuffer,
unsigned int bufferSize) {
return false;
}
bool C4JStorage::DeleteTMSFile(int iQuadrant, eGlobalStorage eStorageFacility,
wchar_t* pwchFilename) {
return false;
}
void C4JStorage::StoreTMSPathName(wchar_t* pwchName) {}
C4JStorage::ETMSStatus C4JStorage::TMSPP_ReadFile(
int iPad, C4JStorage::eGlobalStorage eStorageFacility,
C4JStorage::eTMS_FILETYPEVAL eFileTypeVal, const char* szFilename,
int (*Func)(void*, int, int, PTMSPP_FILEDATA, const char*), void* lpParam,
int iUserData) {
return ETMSStatus_Fail;
}
unsigned int C4JStorage::CRC(unsigned char* buf, int len) {
unsigned int crc = 0xFFFFFFFF;
for (int i = 0; i < len; i++) {
crc ^= buf[i];
for (int j = 0; j < 8; j++) {
crc = (crc >> 1) ^ (0xEDB88320 & (-(crc & 1)));
}
}
return ~crc;
}
int C4JStorage::AddSubfile(int regionIndex) {
(void)regionIndex;
return 0;
}
unsigned int C4JStorage::GetSubfileCount() { return 0; }
void C4JStorage::GetSubfileDetails(unsigned int i, int* regionIndex,
void** data, unsigned int* size) {
(void)i;
if (regionIndex) *regionIndex = 0;
if (data) *data = 0;
if (size) *size = 0;
}
void C4JStorage::ResetSubfiles() {}
void C4JStorage::UpdateSubfile(int index, void* data, unsigned int size) {
(void)index;
(void)data;
(void)size;
}
void C4JStorage::SaveSubfiles(int (*Func)(void*, const bool), void* param) {
if (Func) Func(param, true);
}
C4JStorage::ESaveGameState C4JStorage::GetSaveState() { return ESaveGame_Idle; }
void C4JStorage::ContinueIncompleteOperation() {}
@@ -2,13 +2,11 @@
#include <cstdint>
#include <ctime>
#include <functional>
#include <string>
#include <vector>
// #include <xtms.h>
#include "../PlatformTypes.h"
#include "../IPlatformStorage.h"
#include "../4J.Common/4J_Compat.h"
class C4JStringTable;
@@ -16,25 +14,23 @@ class C4JStringTable;
#define MAX_DETAILS_LENGTH 128 // CELL_SAVEDATA_SYSP_SUBTITLE_SIZE on PS3
#define MAX_SAVEFILENAME_LENGTH 32 // CELL_SAVEDATA_DIRNAME_SIZE
struct CONTAINER_METADATA {
typedef struct {
time_t modifiedTime;
unsigned int dataSize;
unsigned int thumbnailSize;
};
} CONTAINER_METADATA;
struct SAVE_INFO {
typedef struct {
char UTF8SaveFilename[MAX_SAVEFILENAME_LENGTH];
char UTF8SaveTitle[MAX_DISPLAYNAME_LENGTH];
CONTAINER_METADATA metaData;
std::uint8_t* thumbnailData;
};
using PSAVE_INFO = SAVE_INFO*;
} SAVE_INFO, *PSAVE_INFO;
struct SAVE_DETAILS {
typedef struct {
int iSaveC;
PSAVE_INFO SaveInfoA;
};
using PSAVE_DETAILS = SAVE_DETAILS*;
} SAVE_DETAILS, *PSAVE_DETAILS;
typedef std::vector<PXMARKETPLACE_CONTENTOFFER_INFO> OfferDataArray;
typedef std::vector<PXCONTENT_DATA> XContentDataArray;
@@ -43,32 +39,89 @@ typedef std::vector<PXCONTENT_DATA> XContentDataArray;
// Current version of the dlc data creator
#define CURRENT_DLC_VERSION_NUM 3
class C4JStorage : public IPlatformStorage {
class C4JStorage {
public:
struct DLC_FILE_DETAILS {
// Structs defined in the DLC_Creator, but added here to be used in the app
typedef struct {
unsigned int uiFileSize;
std::uint32_t dwType;
std::uint32_t dwWchCount;
std::uint32_t dwWchCount; // count of wchar_t in next array
wchar_t wchFile[1];
};
using PDLC_FILE_DETAILS = DLC_FILE_DETAILS*;
} DLC_FILE_DETAILS, *PDLC_FILE_DETAILS;
struct DLC_FILE_PARAM {
typedef struct {
std::uint32_t dwType;
std::uint32_t dwWchCount;
wchar_t wchData[1];
};
using PDLC_FILE_PARAM = DLC_FILE_PARAM*;
std::uint32_t dwWchCount; // count of wchar_t in next array;
wchar_t wchData[1]; // will be an array of size dwBytes
} DLC_FILE_PARAM, *PDLC_FILE_PARAM;
// End of DLC_Creator structs
struct CACHEINFOSTRUCT {
typedef struct {
wchar_t wchDisplayName[XCONTENT_MAX_DISPLAYNAME_LENGTH];
char szFileName[XCONTENT_MAX_FILENAME_LENGTH];
std::uint32_t dwImageOffset;
std::uint32_t dwImageBytes;
};
} CACHEINFOSTRUCT;
// structure to hold DLC info in TMS
typedef struct {
std::uint32_t dwVersion;
std::uint32_t dwNewOffers;
std::uint32_t dwTotalOffers;
std::uint32_t dwInstalledTotalOffers;
std::uint8_t bPadding[1024 - sizeof(std::uint32_t) * 4];
// future expansion
} DLC_TMS_DETAILS;
enum eGTS_FileTypes { eGTS_Type_Skin = 0, eGTS_Type_Cape, eGTS_Type_MAX };
enum eGlobalStorage {
// eGlobalStorage_GameClip=0,
eGlobalStorage_Title = 0,
eGlobalStorage_TitleUser,
eGlobalStorage_Max
};
enum EMessageResult {
EMessage_Undefined = 0,
EMessage_Busy,
EMessage_Pending,
EMessage_Cancelled,
EMessage_ResultAccept,
EMessage_ResultDecline,
EMessage_ResultThirdOption,
EMessage_ResultFourthOption
};
enum ESaveGameControlState {
ESaveGameControl_Idle = 0,
ESaveGameControl_Save,
ESaveGameControl_InternalRequestingDevice,
ESaveGameControl_InternalGetSaveName,
ESaveGameControl_InternalSaving,
ESaveGameControl_CopySave,
ESaveGameControl_CopyingSave,
};
enum ESaveGameState {
ESaveGame_Idle = 0,
ESaveGame_Save,
ESaveGame_InternalRequestingDevice,
ESaveGame_InternalGetSaveName,
ESaveGame_InternalSaving,
ESaveGame_CopySave,
ESaveGame_CopyingSave,
ESaveGame_Load,
ESaveGame_GetSavesInfo,
ESaveGame_Rename,
ESaveGame_Delete,
ESaveGame_GetSaveThumbnail // Not used as an actual state in the PS4,
// but the game expects this to be returned
// to indicate success when getting a
// thumbnail
};
enum ELoadGameStatus {
ELoadGame_Idle = 0,
ELoadGame_InProgress,
@@ -89,20 +142,60 @@ public:
ESGIStatus_NoSaves,
};
enum EDLCStatus {
EDLC_Error = 0,
EDLC_Idle,
EDLC_NoOffers,
EDLC_AlreadyEnumeratedAllOffers,
EDLC_NoInstalledDLC,
EDLC_Pending,
EDLC_LoadInProgress,
EDLC_Loaded,
EDLC_ChangedDevice
};
enum ESavingMessage {
ESavingMessage_None = 0,
ESavingMessage_Short,
ESavingMessage_Long
};
enum ETMSStatus {
ETMSStatus_Idle = 0,
ETMSStatus_Fail,
ETMSStatus_Fail_ReadInProgress,
ETMSStatus_Fail_WriteInProgress,
ETMSStatus_Pending,
};
enum eTMS_FileType {
eTMS_FileType_Normal = 0,
eTMS_FileType_Graphic,
};
enum eTMS_FILETYPEVAL {
TMS_FILETYPE_BINARY,
TMS_FILETYPE_CONFIG,
TMS_FILETYPE_JSON,
TMS_FILETYPE_MAX
};
enum eTMS_UGCTYPE { TMS_UGCTYPE_NONE, TMS_UGCTYPE_IMAGE, TMS_UGCTYPE_MAX };
struct TMSPP_FILE_DETAILS {
typedef struct {
char szFilename[256];
int iFileSize;
eTMS_FILETYPEVAL eFileTypeVal;
};
using PTMSPP_FILE_DETAILS = TMSPP_FILE_DETAILS*;
} TMSPP_FILE_DETAILS, *PTMSPP_FILE_DETAILS;
struct TMSPP_FILE_LIST {
typedef struct {
int iCount;
PTMSPP_FILE_DETAILS FileDetailsA;
};
using PTMSPP_FILE_LIST = TMSPP_FILE_LIST*;
} TMSPP_FILE_LIST, *PTMSPP_FILE_LIST;
typedef struct {
unsigned int size;
std::uint8_t* pbData;
} TMSPP_FILEDATA, *PTMSPP_FILEDATA;
C4JStorage();
@@ -112,21 +205,20 @@ public:
C4JStorage::EMessageResult RequestMessageBox(
unsigned int uiTitle, unsigned int uiText, unsigned int* uiOptionA,
unsigned int uiOptionC, unsigned int pad = XUSER_INDEX_ANY,
std::function<int(int, const C4JStorage::EMessageResult)> callback =
nullptr,
C4JStringTable* pStringTable = nullptr,
int (*Func)(void*, int, const C4JStorage::EMessageResult) = nullptr,
void* lpParam = nullptr, C4JStringTable* pStringTable = nullptr,
wchar_t* pwchFormatString = nullptr, unsigned int focusButton = 0);
C4JStorage::EMessageResult GetMessageBoxResult();
// save device
bool SetSaveDevice(std::function<int(const bool)> callback,
bool SetSaveDevice(int (*Func)(void*, const bool), void* lpParam,
bool bForceResetOfSaveDevice = false);
// savegame
void Init(unsigned int uiSaveVersion, const wchar_t* pwchDefaultSaveName,
char* pszSavePackName, int iMinimumSaveSize,
std::function<int(const ESavingMessage, int)> callback,
int (*Func)(void*, const ESavingMessage, int), void* lpParam,
const char* szGroupID);
void ResetSaveData(); // Call before a new save to clear out stored save
// file name
@@ -137,7 +229,7 @@ public:
bool GetSaveUniqueFilename(char* pszName);
void SetSaveUniqueFilename(char* szFilename);
void SetState(ESaveGameControlState eControlState,
std::function<int(const bool)> callback);
int (*Func)(void*, const bool), void* lpParam);
void SetSaveDisabled(bool bDisable);
bool GetSaveDisabled(void);
unsigned int GetSaveSize();
@@ -150,11 +242,11 @@ public:
unsigned int textDataBytes); // Sets the thumbnail & image for the
// save, optionally setting the
// metadata in the png
C4JStorage::ESaveGameState SaveSaveData(
std::function<int(const bool)> callback);
C4JStorage::ESaveGameState SaveSaveData(int (*Func)(void*, const bool),
void* lpParam);
void CopySaveDataToNewSave(std::uint8_t* pbThumbnail,
unsigned int cbThumbnail, wchar_t* wchNewName,
std::function<int(bool)> callback);
int (*Func)(void* lpParam, bool), void* lpParam);
void SetSaveDeviceSelected(unsigned int uiPad, bool bSelected);
bool GetSaveDeviceSelected(unsigned int iPad);
C4JStorage::ESaveGameState DoesSaveExist(bool* pbExists);
@@ -166,16 +258,16 @@ public:
// Get the info for the saves
C4JStorage::ESaveGameState GetSavesInfo(
int iPad,
std::function<int(SAVE_DETAILS* pSaveDetails, const bool)> callback,
char* pszSavePackName);
int (*Func)(void* lpParam, SAVE_DETAILS* pSaveDetails, const bool),
void* lpParam, char* pszSavePackName);
PSAVE_DETAILS ReturnSavesInfo();
void ClearSavesInfo(); // Clears results
C4JStorage::ESaveGameState LoadSaveDataThumbnail(
PSAVE_INFO pSaveInfo,
std::function<int(std::uint8_t* thumbnailData,
unsigned int thumbnailBytes)>
callback); // Get the thumbnail for an individual save referenced
// by pSaveInfo
int (*Func)(void* lpParam, std::uint8_t* thumbnailData,
unsigned int thumbnailBytes),
void* lpParam); // Get the thumbnail for an individual save referenced
// by pSaveInfo
void GetSaveCacheFileInfo(unsigned int fileIndex,
XCONTENT_DATA& xContentData);
@@ -184,36 +276,41 @@ public:
unsigned int* pImageBytes);
// Load the save. Need to call GetSaveData once the callback is called
C4JStorage::ESaveGameState LoadSaveData(
PSAVE_INFO pSaveInfo,
std::function<int(const bool, const bool)> callback);
C4JStorage::ESaveGameState DeleteSaveData(
PSAVE_INFO pSaveInfo,
std::function<int(const bool)> callback);
C4JStorage::ESaveGameState LoadSaveData(PSAVE_INFO pSaveInfo,
int (*Func)(void* lpParam,
const bool, const bool),
void* lpParam);
C4JStorage::ESaveGameState DeleteSaveData(PSAVE_INFO pSaveInfo,
int (*Func)(void* lpParam,
const bool),
void* lpParam);
// DLC
void RegisterMarketplaceCountsCallback(
std::function<int(C4JStorage::DLC_TMS_DETAILS*, int)> callback);
int (*Func)(void* lpParam, C4JStorage::DLC_TMS_DETAILS*, int),
void* lpParam);
void SetDLCPackageRoot(char* pszDLCRoot);
C4JStorage::EDLCStatus GetDLCOffers(
int iPad, std::function<int(int, std::uint32_t, int)> callback,
int iPad, int (*Func)(void*, int, std::uint32_t, int), void* lpParam,
std::uint32_t dwOfferTypesBitmask = XMARKETPLACE_OFFERING_TYPE_CONTENT);
unsigned int CancelGetDLCOffers();
void ClearDLCOffers();
XMARKETPLACE_CONTENTOFFER_INFO& GetOffer(unsigned int dw);
int GetOfferCount();
unsigned int InstallOffer(int iOfferIDC, std::uint64_t* ullOfferIDA,
std::function<int(int, int)> callback,
int (*Func)(void*, int, int), void* lpParam,
bool bTrial = false);
unsigned int GetAvailableDLCCount(int iPad);
C4JStorage::EDLCStatus GetInstalledDLC(
int iPad, std::function<int(int, int)> callback);
C4JStorage::EDLCStatus GetInstalledDLC(int iPad,
int (*Func)(void*, int, int),
void* lpParam);
XCONTENT_DATA& GetDLC(unsigned int dw);
std::uint32_t MountInstalledDLC(
int iPad, std::uint32_t dwDLC,
std::function<int(int, std::uint32_t, std::uint32_t)> callback,
const char* szMountDrive = nullptr);
std::uint32_t MountInstalledDLC(int iPad, std::uint32_t dwDLC,
int (*Func)(void*, int, std::uint32_t,
std::uint32_t),
void* lpParam,
const char* szMountDrive = nullptr);
unsigned int UnmountInstalledDLC(const char* szMountDrive = nullptr);
void GetMountedDLCFileList(const char* szMountDrive,
std::vector<std::string>& fileList);
@@ -224,8 +321,8 @@ public:
int iQuadrant, eGlobalStorage eStorageFacility,
C4JStorage::eTMS_FileType eFileType, wchar_t* pwchFilename,
std::uint8_t** ppBuffer, unsigned int* pBufferSize,
std::function<int(wchar_t*, int, bool, int)> callback = nullptr,
int iAction = 0);
int (*Func)(void*, wchar_t*, int, bool, int) = nullptr,
void* lpParam = nullptr, int iAction = 0);
bool WriteTMSFile(int iQuadrant, eGlobalStorage eStorageFacility,
wchar_t* pwchFilename, std::uint8_t* pBuffer,
unsigned int bufferSize);
@@ -247,9 +344,8 @@ public:
C4JStorage::ETMSStatus TMSPP_ReadFile(
int iPad, C4JStorage::eGlobalStorage eStorageFacility,
C4JStorage::eTMS_FILETYPEVAL eFileTypeVal, const char* szFilename,
std::function<int(int, int, PTMSPP_FILEDATA, const char*)> callback =
nullptr,
int iUserData = 0);
int (*Func)(void*, int, int, PTMSPP_FILEDATA, const char*) = nullptr,
void* lpParam = nullptr, int iUserData = 0);
// Older TMS++ list/delete helpers stayed platform-specific. The shared
// surface keeps the read path plus CRC/subfile helpers below.
@@ -271,7 +367,7 @@ public:
unsigned int* size);
void ResetSubfiles();
void UpdateSubfile(int index, void* data, unsigned int size);
void SaveSubfiles(std::function<int(const bool)> callback);
void SaveSubfiles(int (*Func)(void*, const bool), void* param);
ESaveGameState GetSaveState();
void ContinueIncompleteOperation();
View File
View File
View File
+20
View File
@@ -0,0 +1,20 @@
storage_sources = files(
'4J_Storage.cpp',
'stdafx.cpp',
'STO_DLC.cpp',
'STO_Main.cpp',
'STO_SaveGame.cpp',
)
lib_storage = static_library('4J_Storage',
storage_sources,
include_directories : include_directories('.'),
cpp_args : global_cpp_args + global_cpp_defs + [
'-include', meson.current_source_dir() / 'stdafx.h',
],
)
storage_dep = declare_dependency(
link_with : lib_storage,
include_directories : include_directories('.'),
)
+1
View File
@@ -0,0 +1 @@
#include "stdafx.h"
+6
View File
@@ -0,0 +1,6 @@
#ifndef _4J_STORAGE_STADAFX_H
#define _4J_STORAGE_STADAFX_H
#include "../4J.Common/4J_Compat.h"
#endif //_4J_STORAGE_STADAFX_H

Some files were not shown because too many files have changed in this diff Show More