bugfixes
This commit is contained in:
@@ -1,7 +1,92 @@
|
||||
#include "controls.hh"
|
||||
|
||||
#include <psyqo/hardware/cpu.hh>
|
||||
#include <psyqo/hardware/sio.hh>
|
||||
#include <psyqo/vector.hh>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace psyqo::Hardware;
|
||||
|
||||
void busyLoop(unsigned delay) {
|
||||
unsigned cycles = 0;
|
||||
while (++cycles < delay) asm("");
|
||||
}
|
||||
|
||||
void flushRxBuffer() {
|
||||
while (SIO::Stat & SIO::Status::STAT_RXRDY) {
|
||||
SIO::Data.throwAway();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t transceive(uint8_t dataOut) {
|
||||
SIO::Ctrl |= SIO::Control::CTRL_ERRRES;
|
||||
CPU::IReg.clear(CPU::IRQ::Controller);
|
||||
SIO::Data = dataOut;
|
||||
while (!(SIO::Stat & SIO::Status::STAT_RXRDY));
|
||||
return SIO::Data;
|
||||
}
|
||||
|
||||
bool waitForAck() {
|
||||
int cyclesWaited = 0;
|
||||
static constexpr int ackTimeout = 0x137;
|
||||
while (!(CPU::IReg.isSet(CPU::IRQ::Controller)) && ++cyclesWaited < ackTimeout);
|
||||
if (cyclesWaited >= ackTimeout) return false;
|
||||
while (SIO::Stat & SIO::Status::STAT_ACK); // Wait for ACK to go high
|
||||
return true;
|
||||
}
|
||||
|
||||
void configurePort(uint8_t port) {
|
||||
SIO::Ctrl = (port * SIO::Control::CTRL_PORTSEL) | SIO::Control::CTRL_DTR;
|
||||
SIO::Baud = 0x88;
|
||||
flushRxBuffer();
|
||||
SIO::Ctrl |= (SIO::Control::CTRL_TXEN | SIO::Control::CTRL_ACKIRQEN);
|
||||
busyLoop(100);
|
||||
}
|
||||
|
||||
// Send a command sequence to the pad and wait for ACK between each byte.
|
||||
// Returns false if ACK was lost at any point.
|
||||
bool sendCommand(const uint8_t *cmd, unsigned len) {
|
||||
for (unsigned i = 0; i < len; i++) {
|
||||
transceive(cmd[i]);
|
||||
if (i < len - 1) {
|
||||
if (!waitForAck()) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void psxsplash::Controls::forceAnalogMode() {
|
||||
// Initialize SIO for pad communication
|
||||
using namespace psyqo::Hardware;
|
||||
SIO::Ctrl = SIO::Control::CTRL_IR;
|
||||
SIO::Baud = 0x88;
|
||||
SIO::Mode = 0xd;
|
||||
SIO::Ctrl = 0;
|
||||
|
||||
// Sequence for port 0 (Pad 1):
|
||||
// 1) Enter config mode
|
||||
static const uint8_t enterConfig[] = {0x01, 0x43, 0x00, 0x01, 0x00};
|
||||
// 2) Set analog mode (0x01) + lock (0x03)
|
||||
static const uint8_t setAnalog[] = {0x01, 0x44, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
// 3) Exit config mode
|
||||
static const uint8_t exitConfig[] = {0x01, 0x43, 0x00, 0x00, 0x00};
|
||||
|
||||
configurePort(0);
|
||||
sendCommand(enterConfig, sizeof(enterConfig));
|
||||
SIO::Ctrl = 0;
|
||||
|
||||
configurePort(0);
|
||||
sendCommand(setAnalog, sizeof(setAnalog));
|
||||
SIO::Ctrl = 0;
|
||||
|
||||
configurePort(0);
|
||||
sendCommand(exitConfig, sizeof(exitConfig));
|
||||
SIO::Ctrl = 0;
|
||||
}
|
||||
|
||||
void psxsplash::Controls::Init() { m_input.initialize(); }
|
||||
|
||||
bool psxsplash::Controls::isDigitalPad() const {
|
||||
|
||||
@@ -12,6 +12,10 @@ using namespace psyqo::trig_literals;
|
||||
|
||||
class Controls {
|
||||
public:
|
||||
/// Force DualShock into analog mode (LED=Red) via raw SIO commands.
|
||||
/// Must be called BEFORE Init() since Init() hands SIO control to AdvancedPad.
|
||||
void forceAnalogMode();
|
||||
|
||||
void Init();
|
||||
void HandleControls(psyqo::Vec3 &playerPosition, psyqo::Angle &playerRotationX, psyqo::Angle &playerRotationY,
|
||||
psyqo::Angle &playerRotationZ, bool freecam, int deltaFrames);
|
||||
|
||||
41
src/lua.cpp
41
src/lua.cpp
@@ -460,6 +460,47 @@ void psxsplash::Lua::OnUpdate(GameObject* go, int deltaFrames) {
|
||||
onUpdateMethodWrapper.callMethod(*this, go, deltaFrames);
|
||||
}
|
||||
|
||||
void psxsplash::Lua::RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta) {
|
||||
auto L = m_state;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
// objects[i] is already the NEW pointer (relocated by shrinkBuffer).
|
||||
// Compute the OLD pointer by subtracting delta.
|
||||
uint8_t* newPtr = reinterpret_cast<uint8_t*>(objects[i]);
|
||||
uint8_t* oldPtr = newPtr - delta;
|
||||
|
||||
// Re-key the main game object table: registry[oldPtr] -> registry[newPtr]
|
||||
L.push(oldPtr);
|
||||
L.rawGet(LUA_REGISTRYINDEX);
|
||||
if (L.isTable(-1)) {
|
||||
// Update __cpp_ptr inside the table
|
||||
L.push(newPtr);
|
||||
L.setField(-2, "__cpp_ptr");
|
||||
// Store at new key
|
||||
L.push(newPtr);
|
||||
L.copy(-2);
|
||||
L.rawSet(LUA_REGISTRYINDEX);
|
||||
// Remove old key
|
||||
L.push(oldPtr);
|
||||
L.push(); // nil
|
||||
L.rawSet(LUA_REGISTRYINDEX);
|
||||
}
|
||||
L.pop();
|
||||
|
||||
// Re-key the methods table: registry[oldPtr+1] -> registry[newPtr+1]
|
||||
L.push(oldPtr + 1);
|
||||
L.rawGet(LUA_REGISTRYINDEX);
|
||||
if (L.isTable(-1)) {
|
||||
L.push(newPtr + 1);
|
||||
L.copy(-2);
|
||||
L.rawSet(LUA_REGISTRYINDEX);
|
||||
L.push(oldPtr + 1);
|
||||
L.push();
|
||||
L.rawSet(LUA_REGISTRYINDEX);
|
||||
}
|
||||
L.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void psxsplash::Lua::PushGameObject(GameObject* go) {
|
||||
auto L = m_state;
|
||||
L.push(go);
|
||||
|
||||
@@ -49,6 +49,7 @@ class Lua {
|
||||
void LoadLuaFile(const char* code, size_t len, int index);
|
||||
void RegisterSceneScripts(int index);
|
||||
void RegisterGameObject(GameObject* go);
|
||||
void RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta);
|
||||
|
||||
// Get the underlying psyqo::Lua state for API registration
|
||||
psyqo::Lua& getState() { return m_state; }
|
||||
|
||||
@@ -1179,31 +1179,38 @@ int LuaAPI::Camera_LookAt(lua_State* L) {
|
||||
// ============================================================================
|
||||
|
||||
int LuaAPI::Audio_Play(lua_State* L) {
|
||||
printf("[Audio_Play] ENTER top=%d\n", lua_gettop(L));
|
||||
psyqo::Lua lua(L);
|
||||
|
||||
|
||||
int soundId = -1;
|
||||
|
||||
|
||||
// Accept number (index) or string (name lookup) like Entity.Find
|
||||
// Check isNumber FIRST — in Lua, numbers pass isString too.
|
||||
if (lua.isNumber(1)) {
|
||||
soundId = static_cast<int>(lua.toNumber(1));
|
||||
printf("[Audio_Play] by index: %d\n", soundId);
|
||||
} else if (lua.isString(1)) {
|
||||
const char* name = lua.toString(1);
|
||||
printf("[Audio_Play] by name: '%s'\n", name ? name : "(null)");
|
||||
soundId = s_sceneManager->findAudioClipByName(name);
|
||||
if (soundId < 0) {
|
||||
printf("[Lua] Audio.Play: clip '%s' not found\n", name);
|
||||
printf("[Audio_Play] clip not found\n");
|
||||
lua.pushNumber(-1);
|
||||
return 1;
|
||||
}
|
||||
printf("[Audio_Play] found at index %d\n", soundId);
|
||||
} else {
|
||||
printf("[Audio_Play] invalid arg type\n");
|
||||
lua.pushNumber(-1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int volume = static_cast<int>(lua.optNumber(2, 100));
|
||||
int pan = static_cast<int>(lua.optNumber(3, 64));
|
||||
|
||||
|
||||
printf("[Audio_Play] play(%d, vol=%d, pan=%d)\n", soundId, volume, pan);
|
||||
int voice = s_sceneManager->getAudio().play(soundId, volume, pan);
|
||||
printf("[Audio_Play] voice=%d OK\n", voice);
|
||||
lua.pushNumber(voice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -231,6 +231,7 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
||||
L.RegisterGameObject(object);
|
||||
}
|
||||
|
||||
m_controls.forceAnalogMode();
|
||||
m_controls.Init();
|
||||
Renderer::GetInstance().SetCamera(m_currentCamera);
|
||||
|
||||
@@ -815,6 +816,17 @@ void psxsplash::SceneManager::shrinkBuffer() {
|
||||
|
||||
m_uiSystem.relocate(delta);
|
||||
|
||||
// Re-key Lua registry entries for game objects. RegisterGameObject stored
|
||||
// lightuserdata keys using the OLD buffer addresses. After relocation, the
|
||||
// game object pointers changed but the registry keys are stale. Without
|
||||
// this, Entity.Find/FindByIndex return nil (wrong key), and in release
|
||||
// builds (-Os) the optimizer can turn this into a hard crash.
|
||||
if (!m_gameObjects.empty()) {
|
||||
L.RelocateGameObjects(
|
||||
reinterpret_cast<GameObject**>(m_gameObjects.data()),
|
||||
m_gameObjects.size(), delta);
|
||||
}
|
||||
|
||||
FileLoader::Get().FreeFile(oldBase);
|
||||
m_currentSceneData = newBase;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user