This commit is contained in:
Connor McLaughlin 2021-02-15 02:36:15 +10:00
parent 0110295f2c
commit e9413f6d94
10 changed files with 402 additions and 1 deletions

View File

@ -190,6 +190,64 @@ std::string EncodeHex(const u8* data, int length)
return ss.str();
}
void TrimWhitespace(std::string& str)
{
size_t pos = 0;
while (pos < str.size() && std::isspace(str[pos]))
pos++;
if (pos > 0)
str.erase(0, pos);
while (!str.empty() && std::isspace(str.back()))
str.pop_back();
}
std::string_view TrimWhitespace(const std::string_view& str)
{
if (str.empty())
return {};
size_t start_pos = 0;
while (start_pos < str.size() && std::isspace(str[start_pos]))
start_pos++;
if (start_pos == str.size())
return {};
size_t end_pos = str.size() - 1;
while (end_pos > start_pos && std::isspace(str[end_pos]))
end_pos--;
return str.substr(start_pos, end_pos - start_pos + 1);
}
void SplitString(const std::string_view& str, char delim, std::vector<std::string_view>* tokens)
{
tokens->clear();
std::string::size_type start_pos = 0;
std::string::size_type current_pos = 0;
while (current_pos < str.size())
{
if (str[current_pos] != delim)
{
current_pos++;
continue;
}
if (current_pos != start_pos)
tokens->push_back(str.substr(start_pos, current_pos - start_pos));
else
tokens->emplace_back();
start_pos = ++current_pos;
}
if (start_pos != current_pos)
tokens->push_back(str.substr(start_pos, current_pos - start_pos));
}
#ifdef WIN32
std::wstring UTF8StringToWideString(const std::string_view& str)

View File

@ -118,6 +118,13 @@ inline std::optional<bool> FromChars(const std::string_view& str, int base)
std::optional<std::vector<u8>> DecodeHex(const std::string_view& str);
std::string EncodeHex(const u8* data, int length);
/// Trims whitespace characters
void TrimWhitespace(std::string& str);
std::string_view TrimWhitespace(const std::string_view& str);
/// Splits a string into tokens.
void SplitString(const std::string_view& str, char delim, std::vector<std::string_view>* tokens);
/// starts_with from C++20
ALWAYS_INLINE static bool StartsWith(const std::string_view& str, const char* prefix)
{

View File

@ -697,6 +697,8 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
ticks += static_cast<u32>(static_cast<double>(tps) * 0.1);
}
ticks = 20000;
Log_DevPrintf("Seek time for %u LBAs: %d", lba_diff, ticks);
return ticks;
}

View File

@ -136,6 +136,7 @@
<ClCompile Include="mdec.cpp" />
<ClCompile Include="memory_card.cpp" />
<ClCompile Include="memory_card_image.cpp" />
<ClCompile Include="movie.cpp" />
<ClCompile Include="namco_guncon.cpp" />
<ClCompile Include="negcon.cpp" />
<ClCompile Include="pad.cpp" />
@ -213,6 +214,7 @@
<ClInclude Include="mdec.h" />
<ClInclude Include="memory_card.h" />
<ClInclude Include="memory_card_image.h" />
<ClInclude Include="movie.h" />
<ClInclude Include="namco_guncon.h" />
<ClInclude Include="negcon.h" />
<ClInclude Include="pad.h" />

View File

@ -57,6 +57,7 @@
<ClCompile Include="libcrypt_game_codes.cpp" />
<ClCompile Include="texture_replacements.cpp" />
<ClCompile Include="gdb_protocol.h" />
<ClCompile Include="movie.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -116,5 +117,6 @@
<ClInclude Include="libcrypt_game_codes.h" />
<ClInclude Include="texture_replacements.h" />
<ClInclude Include="shader_cache_version.h" />
<ClInclude Include="movie.h" />
</ItemGroup>
</Project>

View File

@ -112,6 +112,16 @@ bool DigitalController::Transfer(const u8 data_in, u8* data_out)
case TransferState::ButtonsLSB:
{
System::InputPolled();
{
for (u32 i = 0; i < 16; i++)
{
if (m_button_state & (1 << i))
continue;
printf("Button %u was down on poll\n", i);
}
}
*data_out = Truncate8(m_button_state) & GetButtonsLSBMask();
m_transfer_state = TransferState::ButtonsMSB;
return true;

244
src/core/movie.cpp Normal file
View File

@ -0,0 +1,244 @@
#include "movie.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/string_util.h"
#include "controller.h"
#include "pad.h"
#include "settings.h"
#include "system.h"
#include <sstream>
Log_SetChannel(Movie);
Movie::Movie() = default;
Movie::~Movie() = default;
void Movie::Rewind()
{
m_current_frame = START_OF_MOVIE;
}
void Movie::NextFrame()
{
if (m_current_frame == m_num_frames)
return;
m_current_frame++;
ApplyInputsForFrame(m_current_frame);
}
void Movie::ApplyInputsForFrame(u32 frame_number)
{
if (frame_number >= m_num_frames)
return;
const std::size_t start_index = frame_number * m_input_mappings.size();
Assert((start_index + m_input_mappings.size()) <= m_input_values.size());
std::size_t index = start_index;
for (const InputMapping& im : m_input_mappings)
{
const s8 value = m_input_values[index++];
switch (im.type)
{
case InputMapping::Type::Button:
{
Controller* controller = g_pad.GetController(im.controller_index);
if (controller)
{
if (value != 0)
Log_InfoPrintf("Frame %u button %d down", frame_number + 1, im.axis_or_button_code);
controller->SetButtonState(im.axis_or_button_code, (value != 0));
}
}
break;
}
}
}
std::unique_ptr<Movie> Movie::Load(const char* filename)
{
std::unique_ptr<Movie> movie = std::unique_ptr<Movie>(new Movie());
if (!movie->LoadBk2(filename))
return {};
return movie;
}
bool Movie::LoadBk2(const char* filename)
{
std::optional<std::string> data = FileSystem::ReadFileToString(filename);
if (!data.has_value() || data->empty())
return false;
return LoadBk2Txt(data.value());
}
static void SplitBk2InputString(const std::string_view& str, char delim, std::vector<std::string_view>* tokens)
{
tokens->clear();
std::string::size_type start_pos = 0;
std::string::size_type current_pos = 0;
while (current_pos < str.size())
{
if (str[current_pos] == ' ' || str[current_pos] == ',' || str[current_pos] == '|')
{
if (current_pos != start_pos)
tokens->push_back(str.substr(start_pos, current_pos - start_pos));
current_pos++;
start_pos = current_pos;
continue;
}
if (str[current_pos] >= '0' && str[current_pos] <= '9')
{
current_pos++;
continue;
}
current_pos++;
tokens->push_back(str.substr(start_pos, current_pos - start_pos));
start_pos = current_pos;
}
if (start_pos != current_pos)
tokens->push_back(str.substr(start_pos, current_pos - start_pos));
}
bool Movie::LoadBk2Txt(const std::string& data)
{
std::istringstream iss(data);
std::string line;
std::vector<std::string_view> tokens;
bool in_input_section = false;
while (std::getline(iss, line))
{
StringUtil::TrimWhitespace(line);
if (line.empty())
continue;
if (line == "[Input]")
{
in_input_section = true;
continue;
}
else if (line == "[/Input]")
{
in_input_section = false;
break;
}
else if (!in_input_section)
{
continue;
}
else if (StringUtil::StartsWith(line, "LogKey:") && line.size() >= 8)
{
// parse key - extra character here for the terminating |
const std::string_view sv(line.c_str() + 7, line.size() - 8);
StringUtil::SplitString(sv, '|', &tokens);
for (const std::string_view& token : tokens)
{
InputMapping mapping = {};
mapping.type = InputMapping::Type::None;
if (token.size() > 2 && token[0] == 'P' && token[1] >= '1' && token[1] <= '2' && token[2] == ' ')
{
mapping.controller_index = token[1] - '1';
const ControllerType ctype = g_settings.controller_types[mapping.controller_index];
if (ctype != ControllerType::None)
{
const std::string_view& bsv(token.substr(3));
const Controller::ButtonList blist(Controller::GetButtonNames(ctype));
for (const auto& it : blist)
{
if (bsv == it.first)
{
mapping.axis_or_button_code = it.second;
mapping.type = InputMapping::Type::Button;
break;
}
}
if (mapping.type == InputMapping::Type::None)
Log_WarningPrintf("Input '%s' was not mapped", std::string(token).c_str());
}
}
else
{
Log_WarningPrintf("Unhandled key token '%s'", std::string(token).c_str());
}
m_input_mappings.push_back(mapping);
}
}
else if (line.size() >= 2 && line.front() == '|' && line.back() == '|')
{
if (m_input_mappings.empty())
{
Log_ErrorPrintf("Mappings not set");
return false;
}
// parse line
const std::string_view sv(line.c_str() + 1, line.size() - 2);
SplitBk2InputString(sv, ',', &tokens);
if (tokens.size() != m_input_mappings.size())
{
Log_ErrorPrintf("Incorrect number of mappings: got %zu expected %zu", tokens.size(), m_input_mappings.size());
return false;
}
for (const std::string_view& token : tokens)
{
const std::string_view& trimmed_token(StringUtil::TrimWhitespace(token));
if (trimmed_token.empty())
{
// ???
m_input_values.push_back(0);
}
else if (trimmed_token[0] == '.')
{
// button off
m_input_values.push_back(0);
}
else if (trimmed_token[0] < '0' || trimmed_token[0] > '1')
{
// button on
m_input_values.push_back(1);
}
else
{
const s32 value = StringUtil::FromChars<s32>(trimmed_token).value_or(0);
m_input_values.push_back(static_cast<s8>(std::clamp<s32>(value, -127, 128)));
}
}
}
else
{
Log_ErrorPrintf("Malformed line: '%s'", line.c_str());
return false;
}
}
if (m_input_mappings.empty())
{
Log_ErrorPrintf("Missing input mapping");
return false;
}
m_num_frames = static_cast<u32>(m_input_values.size() / m_input_mappings.size());
if (m_num_frames == 0)
{
Log_ErrorPrintf("Missing input data");
return false;
}
Log_InfoPrintf("Mapped %zu inputs", m_input_mappings.size());
Log_InfoPrintf("Loaded inputs for %u frames", m_num_frames);
return true;
}

56
src/core/movie.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include "types.h"
#include <string>
#include <vector>
#include <memory>
class Movie
{
public:
~Movie();
static std::unique_ptr<Movie> Load(const char* filename);
ALWAYS_INLINE u32 GetNumFrames() const { return m_num_frames; }
ALWAYS_INLINE bool AtEnd() const { return (m_current_frame == m_num_frames); }
void Rewind();
void NextFrame();
private:
enum : u32
{
START_OF_MOVIE = 0xFFFFFFFFu
};
using InputValue = s8;
struct InputMapping
{
enum class Type
{
None,
DiscSelect,
DiscOpen,
DiscClose,
DiscReset,
Button,
Axis,
};
Type type;
u32 controller_index;
s32 axis_or_button_code;
};
Movie();
bool LoadBk2(const char* filename);
bool LoadBk2Txt(const std::string& data);
void ApplyInputsForFrame(u32 frame_number);
std::vector<InputMapping> m_input_mappings;
std::vector<InputValue> m_input_values;
u32 m_num_frames = 0;
u32 m_current_frame = START_OF_MOVIE;
};

View File

@ -30,6 +30,7 @@
#include "spu.h"
#include "texture_replacements.h"
#include "timers.h"
#include "movie.h"
#include <cctype>
#include <cinttypes>
#include <cmath>
@ -119,6 +120,7 @@ static std::vector<std::string> s_media_playlist;
static std::string s_media_playlist_filename;
static std::unique_ptr<CheatList> s_cheat_list;
static std::unique_ptr<Movie> s_movie;
static bool s_memory_saves_enabled = false;
@ -220,6 +222,15 @@ void IncrementInternalFrameNumber()
s_internal_frame_number++;
}
void InputPolled()
{
#if 1
if (s_movie)
s_movie->NextFrame();
#endif
Log_InfoPrintf("Input polled at frame %u", s_frame_number);
}
const std::string& GetRunningPath()
{
return s_running_game_path;
@ -773,6 +784,9 @@ bool Boot(const SystemBootParameters& params)
// Good to go.
s_state = (g_settings.start_paused || params.override_start_paused.value_or(false)) ? State::Paused : State::Running;
s_movie = Movie::Load("D:\\test.txt");
return true;
}
@ -1264,6 +1278,11 @@ void SingleStepCPU()
void DoRunFrame()
{
#if 0
if (s_movie)
s_movie->NextFrame();
#endif
g_gpu->RestoreGraphicsAPIState();
if (CPU::g_state.use_debug_dispatcher)

View File

@ -129,6 +129,7 @@ u32 GetFrameNumber();
u32 GetInternalFrameNumber();
void FrameDone();
void IncrementInternalFrameNumber();
void InputPolled();
const std::string& GetRunningPath();
const std::string& GetRunningCode();