jpd002 / Play-

Play! - PlayStation2 Emulator
http://purei.org
Other
2.03k stars 249 forks source link

Champions: Return to Arms memcard hang #1329

Closed bigianb closed 5 months ago

bigianb commented 5 months ago

Quick summary

Champions return to arms PAL version (SLES 530.39) hangs reading from the memory card.

System Details

MacOS, m1 MacBook Pro commit: 906c4d2d969bfbee8316d33213d2f6d090ae878a

Issue Details

Reading from the memory card throws an exception. The game tries to read /BSLES-52325/BSLES-52325_config which is the config file for Champions of Norrath (previous game in the series). I don't have a save game for that on my memory card and so the directory does not exist. I suspect the IOP module is not returning the correct signal for that. The assert happens because the game tries to read the file with handle 0. Removing that assert (or running in release mode) will cause the game to hang. I changed it to return -4 in this case rather than 0 (no file found) which seems more correct but a hang still happens.

Screenshots/Videos

iop_mcserv.log

Init();
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetDir(port = 0, slot = 0, flags = 0, maxEntries = 64, tableAddress = 0x004FAE80, name = 'debug.sht');
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetInfo(port = 0, slot = 1, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 1
GetInfo(port = 0, slot = 2, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 2
GetInfo(port = 0, slot = 3, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 3
GetInfo(port = 1, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetInfo(port = 1, slot = 1, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 1
GetInfo(port = 1, slot = 2, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 2
GetInfo(port = 1, slot = 3, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 3
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetInfo(port = 0, slot = 1, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 1
GetInfo(port = 0, slot = 2, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 2
GetInfo(port = 0, slot = 3, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 3
GetInfo(port = 1, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetInfo(port = 1, slot = 1, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 1
GetInfo(port = 1, slot = 2, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 2
GetInfo(port = 1, slot = 3, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
Called mc function with invalid slot 3
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 0, wantFormatted = 0, retBuffer = 0x008FA140);
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 1, wantFormatted = 0, retBuffer = 0x008FA140);
ChDir(port = 0, slot = 0, tableAddress = 0x008FA200, name = '/BESLES-53039');
GetInfo(port = 0, slot = 0, wantType = 1, wantFreeSpace = 1, wantFormatted = 0, retBuffer = 0x008FA140);
Open(port = 0, slot = 0, flags = 64, name = '/BESLES-53039');
ChDir(port = 0, slot = 0, tableAddress = 0x008FA200, name = '/BESLES-53039');
GetDir(port = 0, slot = 0, flags = 0, maxEntries = 64, tableAddress = 0x004FAE80, name = '*');
ChDir(port = 0, slot = 0, tableAddress = 0x008FA200, name = '/BESLES-52325');
GetDir(port = 0, slot = 0, flags = 0, maxEntries = 64, tableAddress = 0x004FAE80, name = 'BESLES-52325_config');
Open(port = 0, slot = 0, flags = 1, name = 'BESLES-52325_config');
Read(handle = 0, size = 0x00000004, bufferAddress = 0x0088FDCC, paramAddress = 0x008FA140);
bigianb commented 5 months ago

The hang was because the param was not being filled in when the file does not have a valid handle.

The code below solves that ... but it still has problems - I think because the params are not right. Needs more investigation.

void CMcServ::Read(uint32* args, uint32 argsSize, uint32* ret, uint32 retSize, uint8* ram)
{
    FILECMD* cmd = reinterpret_cast<FILECMD*>(args);

    CLog::GetInstance().Print(LOG_NAME, "Read(handle = %i, size = 0x%08X, bufferAddress = 0x%08X, paramAddress = 0x%08X);\r\n",
                              cmd->handle, cmd->size, cmd->bufferAddress, cmd->paramAddress);

    if(cmd->paramAddress != 0)
    {
        //This param buffer is used in the callback after calling this method... No clue what it's for
        reinterpret_cast<uint32*>(&ram[cmd->paramAddress])[0] = 0;
        reinterpret_cast<uint32*>(&ram[cmd->paramAddress])[1] = 0;
    }

    assert(cmd->bufferAddress != 0);
    void* dst = &ram[cmd->bufferAddress];

    auto file = GetFileFromHandle(cmd->handle);
    if(file == nullptr)
    {
        ret[0] = -4;
        } 
        else if(file->IsEOF())
    {
        ret[0] = 0;
    }
    else
    {
        ret[0] = static_cast<uint32>(file->Read(dst, cmd->size));

        //Sync pointer for games that write after read without seeking or flushing
        file->Seek(file->Tell(), Framework::STREAM_SEEK_SET);
    }
}