Merge b577cad440
into 48c58dbd18
This commit is contained in:
commit
e0d495266e
|
@ -0,0 +1,46 @@
|
|||
BasedOnStyle: LLVM
|
||||
UseTab: Always
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Allman
|
||||
ColumnLimit: 0
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakTemplateDeclarations: No
|
||||
AlignOperands: Align
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignArrayOfStructures: Right
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 2
|
||||
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
AccessModifierOffset: -4
|
||||
IndentAccessModifiers: false
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
NamespaceIndentation: None
|
||||
Standard: c++17
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
Cpp11BracedListStyle: false
|
||||
FixNamespaceComments: false
|
||||
|
||||
KeepEmptyLines:
|
||||
AtEndOfFile: true
|
||||
AtStartOfBlock: false
|
||||
AtStartOfFile: false
|
||||
|
||||
InsertNewlineAtEOF: true
|
||||
AllowShortEnumsOnASingleLine: true
|
|
@ -0,0 +1 @@
|
|||
core/deps/*
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Derived from duckstation: https://github.com/stenzek/duckstation/blob/master/src/core/achievements.cpp
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
|
@ -20,26 +20,26 @@
|
|||
#include "achievements.h"
|
||||
#include "serialize.h"
|
||||
#ifdef USE_RACHIEVEMENTS
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "imgread/common.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "oslib/http_client.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "ui/gui_achievements.h"
|
||||
#include "imgread/common.h"
|
||||
#include "cfg/option.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "emulator.h"
|
||||
#include "stdclass.h"
|
||||
#include "ui/gui_achievements.h"
|
||||
#include "util/periodic_thread.h"
|
||||
#include "util/worker_thread.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <rc_client.h>
|
||||
#include <rc_hash.h>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <xxhash.h>
|
||||
#include <functional>
|
||||
#include "util/worker_thread.h"
|
||||
#include "util/periodic_thread.h"
|
||||
|
||||
namespace achievements
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ public:
|
|||
~Achievements();
|
||||
bool init();
|
||||
void term();
|
||||
std::future<void> login(const char *username, const char *password);
|
||||
std::future<void> login(const char* username, const char* password);
|
||||
void logout();
|
||||
bool isLoggedOn() const { return loggedOn; }
|
||||
bool isActive() const { return active; }
|
||||
|
@ -67,42 +67,42 @@ private:
|
|||
bool createClient();
|
||||
std::string getGameHash();
|
||||
void loadGame();
|
||||
void gameLoaded(int result, const char *errorMessage);
|
||||
void gameLoaded(int result, const char* errorMessage);
|
||||
void unloadGame();
|
||||
void pauseGame();
|
||||
void resumeGame();
|
||||
void loadCache();
|
||||
std::string getOrDownloadImage(const char *url);
|
||||
std::pair<std::string, bool> getCachedImage(const char *url);
|
||||
std::string getOrDownloadImage(const char* url);
|
||||
std::pair<std::string, bool> getCachedImage(const char* url);
|
||||
void diskChange();
|
||||
void asyncTask(std::function<void()>&& f);
|
||||
void stopThreads();
|
||||
|
||||
static void clientLoginWithTokenCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
|
||||
static void clientLoginWithPasswordCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
|
||||
void authenticationSuccess(const rc_client_user_t *user);
|
||||
static void clientMessageCallback(const char *message, const rc_client_t *client);
|
||||
static u32 clientReadMemory(u32 address, u8 *buffer, u32 num_bytes, rc_client_t *client);
|
||||
static void clientServerCall(const rc_api_request_t *request, rc_client_server_callback_t callback,
|
||||
void *callback_data, rc_client_t *client);
|
||||
static void clientEventHandler(const rc_client_event_t *event, rc_client_t *client);
|
||||
void handleResetEvent(const rc_client_event_t *event);
|
||||
void handleUnlockEvent(const rc_client_event_t *event);
|
||||
void handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t *event);
|
||||
void handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t *event);
|
||||
void handleGameCompleted(const rc_client_event_t *event);
|
||||
void handleShowAchievementProgress(const rc_client_event_t *event);
|
||||
void handleHideAchievementProgress(const rc_client_event_t *event);
|
||||
void handleUpdateAchievementProgress(const rc_client_event_t *event);
|
||||
void handleLeaderboardStarted(const rc_client_event_t *event);
|
||||
void handleLeaderboardFailed(const rc_client_event_t *event);
|
||||
void handleLeaderboardSubmitted(const rc_client_event_t *event);
|
||||
void handleShowLeaderboardTracker(const rc_client_event_t *event);
|
||||
void handleHideLeaderboardTracker(const rc_client_event_t *event);
|
||||
void handleUpdateLeaderboardTracker(const rc_client_event_t *event);
|
||||
static void emuEventCallback(Event event, void *arg);
|
||||
static void clientLoginWithTokenCallback(int result, const char* error_message, rc_client_t* client, void* userdata);
|
||||
static void clientLoginWithPasswordCallback(int result, const char* error_message, rc_client_t* client, void* userdata);
|
||||
void authenticationSuccess(const rc_client_user_t* user);
|
||||
static void clientMessageCallback(const char* message, const rc_client_t* client);
|
||||
static u32 clientReadMemory(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
||||
static void clientServerCall(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
||||
void* callback_data, rc_client_t* client);
|
||||
static void clientEventHandler(const rc_client_event_t* event, rc_client_t* client);
|
||||
void handleResetEvent(const rc_client_event_t* event);
|
||||
void handleUnlockEvent(const rc_client_event_t* event);
|
||||
void handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t* event);
|
||||
void handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t* event);
|
||||
void handleGameCompleted(const rc_client_event_t* event);
|
||||
void handleShowAchievementProgress(const rc_client_event_t* event);
|
||||
void handleHideAchievementProgress(const rc_client_event_t* event);
|
||||
void handleUpdateAchievementProgress(const rc_client_event_t* event);
|
||||
void handleLeaderboardStarted(const rc_client_event_t* event);
|
||||
void handleLeaderboardFailed(const rc_client_event_t* event);
|
||||
void handleLeaderboardSubmitted(const rc_client_event_t* event);
|
||||
void handleShowLeaderboardTracker(const rc_client_event_t* event);
|
||||
void handleHideLeaderboardTracker(const rc_client_event_t* event);
|
||||
void handleUpdateLeaderboardTracker(const rc_client_event_t* event);
|
||||
static void emuEventCallback(Event event, void* arg);
|
||||
|
||||
rc_client_t *rc_client = nullptr;
|
||||
rc_client_t* rc_client = nullptr;
|
||||
bool loggedOn = false;
|
||||
std::atomic_bool loadingGame {};
|
||||
bool active = false;
|
||||
|
@ -111,63 +111,77 @@ private:
|
|||
std::string cachePath;
|
||||
std::unordered_map<u64, std::string> cacheMap;
|
||||
std::mutex cacheMutex;
|
||||
WorkerThread taskThread {"RA-background"};
|
||||
WorkerThread taskThread { "RA-background" };
|
||||
|
||||
PeriodicThread idleThread { "RA-idle", [this]() {
|
||||
if (active)
|
||||
rc_client_idle(rc_client);
|
||||
}};
|
||||
PeriodicThread idleThread { "RA-idle", [this]()
|
||||
{
|
||||
if (active)
|
||||
rc_client_idle(rc_client);
|
||||
} };
|
||||
};
|
||||
|
||||
bool init() {
|
||||
bool init()
|
||||
{
|
||||
return Achievements::Instance().init();
|
||||
}
|
||||
|
||||
void term() {
|
||||
void term()
|
||||
{
|
||||
Achievements::Instance().term();
|
||||
}
|
||||
|
||||
std::future<void> login(const char *username, const char *password) {
|
||||
std::future<void> login(const char* username, const char* password)
|
||||
{
|
||||
return Achievements::Instance().login(username, password);
|
||||
}
|
||||
|
||||
void logout() {
|
||||
void logout()
|
||||
{
|
||||
Achievements::Instance().logout();
|
||||
}
|
||||
|
||||
bool isLoggedOn() {
|
||||
bool isLoggedOn()
|
||||
{
|
||||
return Achievements::Instance().isLoggedOn();
|
||||
}
|
||||
|
||||
bool isActive() {
|
||||
bool isActive()
|
||||
{
|
||||
return Achievements::Instance().isActive();
|
||||
}
|
||||
|
||||
Game getCurrentGame() {
|
||||
Game getCurrentGame()
|
||||
{
|
||||
return Achievements::Instance().getCurrentGame();
|
||||
}
|
||||
|
||||
std::vector<Achievement> getAchievementList() {
|
||||
std::vector<Achievement> getAchievementList()
|
||||
{
|
||||
return Achievements::Instance().getAchievementList();
|
||||
}
|
||||
|
||||
bool canPause() {
|
||||
bool canPause()
|
||||
{
|
||||
return Achievements::Instance().canPause();
|
||||
}
|
||||
|
||||
void serialize(Serializer& ser) {
|
||||
void serialize(Serializer& ser)
|
||||
{
|
||||
Achievements::Instance().serialize(ser);
|
||||
}
|
||||
void deserialize(Deserializer& deser) {
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
Achievements::Instance().deserialize(deser);
|
||||
}
|
||||
|
||||
Achievements& Achievements::Instance() {
|
||||
Achievements& Achievements::Instance()
|
||||
{
|
||||
static Achievements instance;
|
||||
return instance;
|
||||
}
|
||||
// create the instance at start up
|
||||
OnLoad _([]() { Achievements::Instance(); });
|
||||
OnLoad _([]()
|
||||
{ Achievements::Instance(); });
|
||||
|
||||
Achievements::Achievements()
|
||||
{
|
||||
|
@ -189,11 +203,13 @@ Achievements::~Achievements()
|
|||
term();
|
||||
}
|
||||
|
||||
void Achievements::asyncTask(std::function<void()>&& f) {
|
||||
void Achievements::asyncTask(std::function<void()>&& f)
|
||||
{
|
||||
taskThread.run(std::move(f));
|
||||
}
|
||||
|
||||
void Achievements::stopThreads() {
|
||||
void Achievements::stopThreads()
|
||||
{
|
||||
taskThread.stop();
|
||||
idleThread.stop();
|
||||
}
|
||||
|
@ -209,16 +225,16 @@ bool Achievements::init()
|
|||
rc_client_set_event_handler(rc_client, clientEventHandler);
|
||||
rc_client_set_hardcore_enabled(rc_client, 0);
|
||||
// TODO Expose these settings?
|
||||
//rc_client_set_encore_mode_enabled(rc_client, 0);
|
||||
//rc_client_set_unofficial_enabled(rc_client, 0);
|
||||
//rc_client_set_spectator_mode_enabled(rc_client, 0);
|
||||
// rc_client_set_encore_mode_enabled(rc_client, 0);
|
||||
// rc_client_set_unofficial_enabled(rc_client, 0);
|
||||
// rc_client_set_spectator_mode_enabled(rc_client, 0);
|
||||
loadCache();
|
||||
|
||||
if (!config::AchievementsUserName.get().empty() && !config::AchievementsToken.get().empty())
|
||||
{
|
||||
INFO_LOG(COMMON, "RA: Attempting login with user '%s'...", config::AchievementsUserName.get().c_str());
|
||||
rc_client_begin_login_with_token(rc_client, config::AchievementsUserName.get().c_str(),
|
||||
config::AchievementsToken.get().c_str(), clientLoginWithTokenCallback, nullptr);
|
||||
config::AchievementsToken.get().c_str(), clientLoginWithTokenCallback, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -249,12 +265,12 @@ void Achievements::loadCache()
|
|||
{
|
||||
cachePath = get_writable_data_path("achievements/");
|
||||
flycast::mkdir(cachePath.c_str(), 0755);
|
||||
DIR *dir = flycast::opendir(cachePath.c_str());
|
||||
DIR* dir = flycast::opendir(cachePath.c_str());
|
||||
if (dir != nullptr)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
dirent *direntry = flycast::readdir(dir);
|
||||
dirent* direntry = flycast::readdir(dir);
|
||||
if (direntry == nullptr)
|
||||
break;
|
||||
std::string name = direntry->d_name;
|
||||
|
@ -269,16 +285,18 @@ void Achievements::loadCache()
|
|||
}
|
||||
}
|
||||
|
||||
static u64 hashUrl(const char *url) {
|
||||
static u64 hashUrl(const char* url)
|
||||
{
|
||||
return XXH3_64bits(url, strlen(url));
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
|
||||
std::pair<std::string, bool> Achievements::getCachedImage(const char* url)
|
||||
{
|
||||
u64 hash = hashUrl(url);
|
||||
std::lock_guard<std::mutex> _(cacheMutex);
|
||||
auto it = cacheMap.find(hash);
|
||||
if (it != cacheMap.end()) {
|
||||
if (it != cacheMap.end())
|
||||
{
|
||||
return std::make_pair(cachePath + it->second, true);
|
||||
}
|
||||
else
|
||||
|
@ -289,7 +307,7 @@ std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
|
|||
}
|
||||
}
|
||||
|
||||
std::string Achievements::getOrDownloadImage(const char *url)
|
||||
std::string Achievements::getOrDownloadImage(const char* url)
|
||||
{
|
||||
u64 hash = hashUrl(url);
|
||||
{
|
||||
|
@ -306,8 +324,9 @@ std::string Achievements::getOrDownloadImage(const char *url)
|
|||
std::stringstream stream;
|
||||
stream << std::hex << hash << ".png";
|
||||
std::string localPath = cachePath + stream.str();
|
||||
FILE *f = nowide::fopen(localPath.c_str(), "wb");
|
||||
if (f == nullptr) {
|
||||
FILE* f = nowide::fopen(localPath.c_str(), "wb");
|
||||
if (f == nullptr)
|
||||
{
|
||||
WARN_LOG(COMMON, "Can't save image to %s", localPath.c_str());
|
||||
return {};
|
||||
}
|
||||
|
@ -331,28 +350,28 @@ void Achievements::term()
|
|||
rc_client = nullptr;
|
||||
}
|
||||
|
||||
void Achievements::authenticationSuccess(const rc_client_user_t *user)
|
||||
void Achievements::authenticationSuccess(const rc_client_user_t* user)
|
||||
{
|
||||
NOTICE_LOG(COMMON, "RA Login successful");
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
asyncTask([this, url]()
|
||||
{
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
|
||||
notifier.notify(Notification::Login, image, text);
|
||||
});
|
||||
notifier.notify(Notification::Login, image, text); });
|
||||
}
|
||||
loggedOn = true;
|
||||
if (!settings.content.fileName.empty()) // TODO better test?
|
||||
loadGame();
|
||||
}
|
||||
|
||||
void Achievements::clientLoginWithTokenCallback(int result, const char *error_message, rc_client_t *client,
|
||||
void *userdata)
|
||||
void Achievements::clientLoginWithTokenCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata)
|
||||
{
|
||||
Achievements *achievements = (Achievements *)rc_client_get_userdata(client);
|
||||
Achievements* achievements = (Achievements*)rc_client_get_userdata(client);
|
||||
if (result != RC_OK)
|
||||
{
|
||||
WARN_LOG(COMMON, "RA Login failed: %s", error_message);
|
||||
|
@ -365,17 +384,17 @@ void Achievements::clientLoginWithTokenCallback(int result, const char *error_me
|
|||
std::future<void> Achievements::login(const char* username, const char* password)
|
||||
{
|
||||
init();
|
||||
std::promise<void> *promise = new std::promise<void>();
|
||||
std::promise<void>* promise = new std::promise<void>();
|
||||
auto future = promise->get_future();
|
||||
rc_client_begin_login_with_password(rc_client, username, password, clientLoginWithPasswordCallback, promise);
|
||||
return future;
|
||||
}
|
||||
|
||||
void Achievements::clientLoginWithPasswordCallback(int result, const char *error_message, rc_client_t *client,
|
||||
void *userdata)
|
||||
void Achievements::clientLoginWithPasswordCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata)
|
||||
{
|
||||
Achievements *achievements = (Achievements *)rc_client_get_userdata(client);
|
||||
std::promise<void> *promise = (std::promise<void> *)userdata;
|
||||
Achievements* achievements = (Achievements*)rc_client_get_userdata(client);
|
||||
std::promise<void>* promise = (std::promise<void>*)userdata;
|
||||
if (result != RC_OK)
|
||||
{
|
||||
std::string errorMsg = rc_error_str(result);
|
||||
|
@ -431,13 +450,13 @@ u32 Achievements::clientReadMemory(u32 address, u8* buffer, u32 num_bytes, rc_cl
|
|||
switch (num_bytes)
|
||||
{
|
||||
case 1:
|
||||
*buffer = ReadMem8_nommu(address);
|
||||
break;
|
||||
*buffer = ReadMem8_nommu(address);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)buffer = ReadMem16_nommu(address);
|
||||
*(u16*)buffer = ReadMem16_nommu(address);
|
||||
break;
|
||||
case 4:
|
||||
*(u32 *)buffer = ReadMem32_nommu(address);
|
||||
*(u32*)buffer = ReadMem32_nommu(address);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
@ -445,18 +464,19 @@ u32 Achievements::clientReadMemory(u32 address, u8* buffer, u32 num_bytes, rc_cl
|
|||
return num_bytes;
|
||||
}
|
||||
|
||||
void Achievements::clientServerCall(const rc_api_request_t *request, rc_client_server_callback_t callback,
|
||||
void *callback_data, rc_client_t *client)
|
||||
void Achievements::clientServerCall(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
||||
void* callback_data, rc_client_t* client)
|
||||
{
|
||||
Achievements *achievements = (Achievements *)rc_client_get_userdata(client);
|
||||
std::string url {request->url};
|
||||
Achievements* achievements = (Achievements*)rc_client_get_userdata(client);
|
||||
std::string url { request->url };
|
||||
std::string payload;
|
||||
if (request->post_data != nullptr)
|
||||
payload = request->post_data;
|
||||
std::string contentType;
|
||||
if (request->content_type != nullptr)
|
||||
contentType = request->content_type;
|
||||
achievements->asyncTask([url, contentType, payload, callback, callback_data]() {
|
||||
achievements->asyncTask([url, contentType, payload, callback, callback_data]()
|
||||
{
|
||||
int rc;
|
||||
std::vector<u8> reply;
|
||||
if (!payload.empty())
|
||||
|
@ -467,8 +487,7 @@ void Achievements::clientServerCall(const rc_api_request_t *request, rc_client_s
|
|||
rr.http_status_code = rc; // TODO RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR if connection fails?
|
||||
rr.body_length = reply.size();
|
||||
rr.body = (const char *)reply.data();
|
||||
callback(&rr, callback_data);
|
||||
});
|
||||
callback(&rr, callback_data); });
|
||||
}
|
||||
|
||||
static void handleServerError(const rc_client_server_error_t* error)
|
||||
|
@ -485,7 +504,7 @@ static void notifyError(const std::string& msg)
|
|||
|
||||
void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_t* client)
|
||||
{
|
||||
Achievements *achievements = (Achievements *)rc_client_get_userdata(client);
|
||||
Achievements* achievements = (Achievements*)rc_client_get_userdata(client);
|
||||
switch (event->type)
|
||||
{
|
||||
case RC_CLIENT_EVENT_RESET:
|
||||
|
@ -540,7 +559,7 @@ void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_
|
|||
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE:
|
||||
achievements->handleUpdateLeaderboardTracker(event);
|
||||
break;
|
||||
// TODO case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
|
||||
// TODO case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
|
||||
|
||||
case RC_CLIENT_EVENT_DISCONNECTED:
|
||||
notifyError("RetroAchievements disconnected");
|
||||
|
@ -556,14 +575,14 @@ void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_
|
|||
}
|
||||
}
|
||||
|
||||
void Achievements::handleResetEvent(const rc_client_event_t *event)
|
||||
void Achievements::handleResetEvent(const rc_client_event_t* event)
|
||||
{
|
||||
// This never seems to be called, probably because hardcore mode is enabled before starting the game.
|
||||
INFO_LOG(COMMON, "RA: Resetting runtime due to reset event");
|
||||
rc_client_reset(rc_client);
|
||||
}
|
||||
|
||||
void Achievements::handleUnlockEvent(const rc_client_event_t *event)
|
||||
void Achievements::handleUnlockEvent(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
assert(cheevo != nullptr);
|
||||
|
@ -575,15 +594,15 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
|
|||
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url, title, description]() {
|
||||
asyncTask([this, url, title, description]()
|
||||
{
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "Achievement " + title + " unlocked!";
|
||||
notifier.notify(Notification::Login, image, text, description);
|
||||
});
|
||||
notifier.notify(Notification::Login, image, text, description); });
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t *event)
|
||||
void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge: %s", cheevo->title);
|
||||
|
@ -591,14 +610,14 @@ void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_
|
|||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
asyncTask([this, url]()
|
||||
{
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.showChallenge(image);
|
||||
});
|
||||
notifier.showChallenge(image); });
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t *event)
|
||||
void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge hidden: %s", cheevo->title);
|
||||
|
@ -606,54 +625,54 @@ void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_
|
|||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
asyncTask([this, url]()
|
||||
{
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.hideChallenge(image);
|
||||
});
|
||||
notifier.hideChallenge(image); });
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
|
||||
void Achievements::handleLeaderboardStarted(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
const rc_client_leaderboard_t* leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard started: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " started";
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleLeaderboardFailed(const rc_client_event_t *event)
|
||||
void Achievements::handleLeaderboardFailed(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
const rc_client_leaderboard_t* leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard failed: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " failed";
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleLeaderboardSubmitted(const rc_client_event_t *event)
|
||||
void Achievements::handleLeaderboardSubmitted(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
const rc_client_leaderboard_t* leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard submitted: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " submitted";
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleShowLeaderboardTracker(const rc_client_event_t *event)
|
||||
void Achievements::handleShowLeaderboardTracker(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker;
|
||||
const rc_client_leaderboard_tracker_t* leaderboard = event->leaderboard_tracker;
|
||||
DEBUG_LOG(COMMON, "RA: Show leaderboard[%d]: %s", leaderboard->id, leaderboard->display);
|
||||
notifier.showLeaderboard(leaderboard->id, leaderboard->display);
|
||||
}
|
||||
void Achievements::handleHideLeaderboardTracker(const rc_client_event_t *event)
|
||||
void Achievements::handleHideLeaderboardTracker(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker;
|
||||
const rc_client_leaderboard_tracker_t* leaderboard = event->leaderboard_tracker;
|
||||
DEBUG_LOG(COMMON, "RA: Hide leaderboard[%d]: %s", leaderboard->id, leaderboard->display);
|
||||
notifier.hideLeaderboard(leaderboard->id);
|
||||
}
|
||||
void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event)
|
||||
void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker;
|
||||
const rc_client_leaderboard_tracker_t* leaderboard = event->leaderboard_tracker;
|
||||
DEBUG_LOG(COMMON, "RA: Update leaderboard[%d]: %s", leaderboard->id, leaderboard->display);
|
||||
notifier.showLeaderboard(leaderboard->id, leaderboard->display);
|
||||
}
|
||||
|
||||
void Achievements::handleGameCompleted(const rc_client_event_t *event)
|
||||
void Achievements::handleGameCompleted(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
|
@ -666,52 +685,52 @@ void Achievements::handleGameCompleted(const rc_client_event_t *event)
|
|||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
asyncTask([this, url, text1, text2, text3]() {
|
||||
asyncTask([this, url, text1, text2, text3]()
|
||||
{
|
||||
std::string image;
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Mastery, image, text1, text2, text3);
|
||||
});
|
||||
notifier.notify(Notification::Mastery, image, text1, text2, text3); });
|
||||
}
|
||||
|
||||
void Achievements::handleShowAchievementProgress(const rc_client_event_t *event)
|
||||
void Achievements::handleShowAchievementProgress(const rc_client_event_t* event)
|
||||
{
|
||||
handleUpdateAchievementProgress(event);
|
||||
}
|
||||
void Achievements::handleHideAchievementProgress(const rc_client_event_t *event)
|
||||
void Achievements::handleHideAchievementProgress(const rc_client_event_t* event)
|
||||
{
|
||||
notifier.notify(Notification::Progress, "", "");
|
||||
}
|
||||
void Achievements::handleUpdateAchievementProgress(const rc_client_event_t *event)
|
||||
void Achievements::handleUpdateAchievementProgress(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
std::string progress(cheevo->measured_progress);
|
||||
asyncTask([this, url, progress]() {
|
||||
asyncTask([this, url, progress]()
|
||||
{
|
||||
std::string image;
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Progress, image, progress);
|
||||
});
|
||||
notifier.notify(Notification::Progress, image, progress); });
|
||||
}
|
||||
|
||||
static Disc *hashDisk;
|
||||
static Disc* hashDisk;
|
||||
static bool add150;
|
||||
|
||||
static void *cdreader_open_track(const char* path, u32 track)
|
||||
static void* cdreader_open_track(const char* path, u32 track)
|
||||
{
|
||||
DEBUG_LOG(COMMON, "RA: cdreader_open_track %s track %d", path, track);
|
||||
if (track == RC_HASH_CDTRACK_FIRST_DATA)
|
||||
{
|
||||
for (const Track& track : hashDisk->tracks)
|
||||
if (track.isDataTrack())
|
||||
return const_cast<Track *>(&track);
|
||||
return const_cast<Track*>(&track);
|
||||
return nullptr;
|
||||
}
|
||||
if (track <= hashDisk->tracks.size())
|
||||
return const_cast<Track *>(&hashDisk->tracks[track - 1]);
|
||||
return const_cast<Track*>(&hashDisk->tracks[track - 1]);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -722,7 +741,7 @@ static size_t cdreader_read_sector(void* track_handle, u32 sector, void* buffer,
|
|||
// add 150 sectors to FAD corresponding to files
|
||||
// FIXME get rid of this
|
||||
add150 = true;
|
||||
//DEBUG_LOG(COMMON, "RA: cdreader_read_sector track %p sec %d+%d num %zd", track_handle, sector, add150 ? 150 : 0, requested_bytes);
|
||||
// DEBUG_LOG(COMMON, "RA: cdreader_read_sector track %p sec %d+%d num %zd", track_handle, sector, add150 ? 150 : 0, requested_bytes);
|
||||
if (add150)
|
||||
sector += 150;
|
||||
u8 locbuf[2048];
|
||||
|
@ -739,7 +758,7 @@ static void cdreader_close_track(void* track_handle)
|
|||
|
||||
static u32 cdreader_first_track_sector(void* track_handle)
|
||||
{
|
||||
Track& track = *static_cast<Track *>(track_handle);
|
||||
Track& track = *static_cast<Track*>(track_handle);
|
||||
DEBUG_LOG(COMMON, "RA: cdreader_first_track_sector track %p -> %d", track_handle, track.StartFAD);
|
||||
return track.StartFAD;
|
||||
}
|
||||
|
@ -751,31 +770,32 @@ std::string Achievements::getGameHash()
|
|||
if (!gdr::isLoaded())
|
||||
return {};
|
||||
// Reopen the disk locally to avoid threading issues (CHD)
|
||||
try {
|
||||
try
|
||||
{
|
||||
hashDisk = OpenDisc(settings.content.path);
|
||||
} catch (const FlycastException& e) {
|
||||
}
|
||||
catch (const FlycastException& e)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
add150 = false;
|
||||
rc_hash_cdreader hooks = {
|
||||
cdreader_open_track,
|
||||
cdreader_read_sector,
|
||||
cdreader_close_track,
|
||||
cdreader_first_track_sector
|
||||
cdreader_open_track,
|
||||
cdreader_read_sector,
|
||||
cdreader_close_track,
|
||||
cdreader_first_track_sector
|
||||
};
|
||||
rc_hash_init_custom_cdreader(&hooks);
|
||||
rc_hash_init_error_message_callback([](const char *msg) {
|
||||
WARN_LOG(COMMON, "cdreader: %s", msg);
|
||||
});
|
||||
rc_hash_init_error_message_callback([](const char* msg)
|
||||
{ WARN_LOG(COMMON, "cdreader: %s", msg); });
|
||||
#if !defined(NDEBUG) || defined(DEBUGFAST)
|
||||
rc_hash_init_verbose_message_callback([](const char *msg) {
|
||||
DEBUG_LOG(COMMON, "cdreader: %s", msg);
|
||||
});
|
||||
rc_hash_init_verbose_message_callback([](const char* msg)
|
||||
{ DEBUG_LOG(COMMON, "cdreader: %s", msg); });
|
||||
#endif
|
||||
}
|
||||
char hash[33] {};
|
||||
rc_hash_generate_from_file(hash, settings.platform.isConsole() ? RC_CONSOLE_DREAMCAST : RC_CONSOLE_ARCADE,
|
||||
settings.content.fileName.c_str()); // fileName is only used for arcade
|
||||
settings.content.fileName.c_str()); // fileName is only used for arcade
|
||||
delete hashDisk;
|
||||
hashDisk = nullptr;
|
||||
|
||||
|
@ -806,9 +826,9 @@ void Achievements::resumeGame()
|
|||
term();
|
||||
}
|
||||
|
||||
void Achievements::emuEventCallback(Event event, void *arg)
|
||||
void Achievements::emuEventCallback(Event event, void* arg)
|
||||
{
|
||||
Achievements *instance = ((Achievements *)arg);
|
||||
Achievements* instance = ((Achievements*)arg);
|
||||
switch (event)
|
||||
{
|
||||
case Event::Start:
|
||||
|
@ -843,7 +863,8 @@ void Achievements::loadGame()
|
|||
loadingGame = false;
|
||||
return;
|
||||
}
|
||||
if (!init() || !isLoggedOn()) {
|
||||
if (!init() || !isLoggedOn())
|
||||
{
|
||||
if (!isLoggedOn())
|
||||
INFO_LOG(COMMON, "Not logged on. Not loading game yet");
|
||||
loadingGame = false;
|
||||
|
@ -854,25 +875,26 @@ void Achievements::loadGame()
|
|||
{
|
||||
// settings.raHardcoreMode is set before enabling cheats and loading the initial savestate
|
||||
rc_client_set_hardcore_enabled(rc_client, settings.raHardcoreMode);
|
||||
rc_client_begin_load_game(rc_client, gameHash.c_str(), [](int result, const char *error_message, rc_client_t *client, void *userdata) {
|
||||
((Achievements *)userdata)->gameLoaded(result, error_message);
|
||||
}, this);
|
||||
rc_client_begin_load_game(rc_client, gameHash.c_str(), [](int result, const char* error_message, rc_client_t* client, void* userdata)
|
||||
{ ((Achievements*)userdata)->gameLoaded(result, error_message); }, this);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
INFO_LOG(COMMON, "RA: empty hash. Aborting load");
|
||||
loadingGame = false;
|
||||
settings.raHardcoreMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::gameLoaded(int result, const char *errorMessage)
|
||||
void Achievements::gameLoaded(int result, const char* errorMessage)
|
||||
{
|
||||
if (result != RC_OK)
|
||||
{
|
||||
if (result == RC_NO_GAME_LOADED)
|
||||
// Unknown game.
|
||||
INFO_LOG(COMMON, "RA: Unknown game, disabling achievements.");
|
||||
else if (result == RC_LOGIN_REQUIRED) {
|
||||
else if (result == RC_LOGIN_REQUIRED)
|
||||
{
|
||||
// We would've asked to re-authenticate, so leave HC on for now.
|
||||
// Once we've done so, we'll reload the game.
|
||||
}
|
||||
|
@ -894,7 +916,7 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
|
|||
loadingGame = false;
|
||||
EventManager::listen(Event::VBlank, emuEventCallback, this);
|
||||
NOTICE_LOG(COMMON, "RA: game %d loaded: %s, achievements %d leaderboards %d rich presence %d", info->id, info->title,
|
||||
rc_client_has_achievements(rc_client), rc_client_has_leaderboards(rc_client), rc_client_has_rich_presence(rc_client));
|
||||
rc_client_has_achievements(rc_client), rc_client_has_leaderboards(rc_client), rc_client_has_rich_presence(rc_client));
|
||||
if (!rc_client_is_processing_required(rc_client))
|
||||
settings.raHardcoreMode = false;
|
||||
else
|
||||
|
@ -911,13 +933,13 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
|
|||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
else
|
||||
text2 = "This game has no achievements.";
|
||||
asyncTask([this, url, text1, text2]() {
|
||||
asyncTask([this, url, text1, text2]()
|
||||
{
|
||||
std::string image;
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
notifier.notify(Notification::Login, image, text1, text2, text3);
|
||||
});
|
||||
notifier.notify(Notification::Login, image, text1, text2, text3); });
|
||||
}
|
||||
|
||||
void Achievements::unloadGame()
|
||||
|
@ -939,11 +961,13 @@ void Achievements::diskChange()
|
|||
// Don't unload the game when the lid is open while swapping disks
|
||||
return;
|
||||
std::string hash = getGameHash();
|
||||
if (hash == "") {
|
||||
if (hash == "")
|
||||
{
|
||||
unloadGame();
|
||||
return;
|
||||
}
|
||||
rc_client_begin_change_media_from_hash(rc_client, hash.c_str(), [](int result, const char *errorMessage, rc_client_t *client, void *userdata) {
|
||||
rc_client_begin_change_media_from_hash(rc_client, hash.c_str(), [](int result, const char* errorMessage, rc_client_t* client, void* userdata)
|
||||
{
|
||||
if (result == RC_HARDCORE_DISABLED) {
|
||||
settings.raHardcoreMode = false;
|
||||
notifier.notify(Notification::Login, "", "Hardcore mode disabled", "Unrecognized media inserted");
|
||||
|
@ -954,47 +978,48 @@ void Achievements::diskChange()
|
|||
if (errorMessage == nullptr)
|
||||
errorMessage = rc_error_str(result);
|
||||
notifier.notify(Notification::Login, "", "Media change failed", errorMessage);
|
||||
}
|
||||
}, this);
|
||||
} }, this);
|
||||
}
|
||||
|
||||
Game Achievements::getCurrentGame()
|
||||
{
|
||||
if (!active)
|
||||
return Game{};
|
||||
const rc_client_game_t *info = rc_client_get_game_info(rc_client);
|
||||
return Game {};
|
||||
const rc_client_game_t* info = rc_client_get_game_info(rc_client);
|
||||
if (info == nullptr)
|
||||
return Game{};
|
||||
return Game {};
|
||||
char url[128];
|
||||
std::string image;
|
||||
if (rc_client_game_get_image_url(info, url, sizeof(url)) == RC_OK)
|
||||
{
|
||||
bool cached;
|
||||
std::tie(image, cached) = getCachedImage(url);
|
||||
if (!cached) {
|
||||
if (!cached)
|
||||
{
|
||||
std::string surl = url;
|
||||
asyncTask([this, surl]() { getOrDownloadImage(surl.c_str()); });
|
||||
asyncTask([this, surl]()
|
||||
{ getOrDownloadImage(surl.c_str()); });
|
||||
}
|
||||
}
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
|
||||
return Game{ image, info->title, summary.num_unlocked_achievements, summary.num_core_achievements, summary.points_unlocked, summary.points_core };
|
||||
return Game { image, info->title, summary.num_unlocked_achievements, summary.num_core_achievements, summary.points_unlocked, summary.points_core };
|
||||
}
|
||||
|
||||
std::vector<Achievement> Achievements::getAchievementList()
|
||||
{
|
||||
std::vector<Achievement> achievements;
|
||||
rc_client_achievement_list_t *list = rc_client_create_achievement_list(rc_client,
|
||||
rc_client_achievement_list_t* list = rc_client_create_achievement_list(rc_client,
|
||||
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
||||
std::vector<std::string> uncachedImages;
|
||||
for (u32 i = 0; i < list->num_buckets; i++)
|
||||
{
|
||||
const char *label = list->buckets[i].label;
|
||||
const char* label = list->buckets[i].label;
|
||||
for (u32 j = 0; j < list->buckets[i].num_achievements; j++)
|
||||
{
|
||||
const rc_client_achievement_t *achievement = list->buckets[i].achievements[j];
|
||||
const rc_client_achievement_t* achievement = list->buckets[i].achievements[j];
|
||||
char url[128];
|
||||
std::string image;
|
||||
if (rc_client_achievement_get_image_url(achievement, achievement->state, url, sizeof(url)) == RC_OK)
|
||||
|
@ -1012,10 +1037,10 @@ std::vector<Achievement> Achievements::getAchievementList()
|
|||
}
|
||||
rc_client_destroy_achievement_list(list);
|
||||
if (!uncachedImages.empty())
|
||||
asyncTask([this, uncachedImages]() {
|
||||
asyncTask([this, uncachedImages]()
|
||||
{
|
||||
for (const std::string& url : uncachedImages)
|
||||
getOrDownloadImage(url.c_str());
|
||||
});
|
||||
getOrDownloadImage(url.c_str()); });
|
||||
|
||||
return achievements;
|
||||
}
|
||||
|
@ -1036,41 +1061,45 @@ void Achievements::serialize(Serializer& ser)
|
|||
u32 size = (u32)rc_client_progress_size(rc_client);
|
||||
if (size > 0)
|
||||
{
|
||||
u8 *buffer = new u8[size];
|
||||
u8* buffer = new u8[size];
|
||||
if (rc_client_serialize_progress(rc_client, buffer) != RC_OK)
|
||||
size = 0;
|
||||
ser << size;
|
||||
ser.serialize(buffer, size);
|
||||
delete[] buffer;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ser << size;
|
||||
}
|
||||
}
|
||||
void Achievements::deserialize(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() < Deserializer::V50) {
|
||||
rc_client_deserialize_progress(rc_client, nullptr);
|
||||
if (deser.version() < Deserializer::V50)
|
||||
{
|
||||
rc_client_deserialize_progress(rc_client, nullptr);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
u32 size;
|
||||
deser >> size;
|
||||
if (size > 0)
|
||||
{
|
||||
u8 *buffer = new u8[size];
|
||||
u8* buffer = new u8[size];
|
||||
deser.deserialize(buffer, size);
|
||||
rc_client_deserialize_progress(rc_client, buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
rc_client_deserialize_progress(rc_client, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace achievements
|
||||
} // namespace achievements
|
||||
|
||||
#else // !USE_RACHIEVEMENTS
|
||||
#else // !USE_RACHIEVEMENTS
|
||||
|
||||
namespace achievements
|
||||
{
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
@ -47,7 +47,7 @@ struct Achievement
|
|||
|
||||
bool init();
|
||||
void term();
|
||||
std::future<void> login(const char *username, const char *password);
|
||||
std::future<void> login(const char* username, const char* password);
|
||||
void logout();
|
||||
bool isLoggedOn();
|
||||
bool isActive();
|
||||
|
@ -57,10 +57,12 @@ bool canPause();
|
|||
|
||||
#else
|
||||
|
||||
static inline bool isActive() {
|
||||
static inline bool isActive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline bool canPause() {
|
||||
static inline bool canPause()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 22, 2018
|
||||
Created on: Nov 22, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "7zArchive.h"
|
||||
#include "deps/lzma/7z.h"
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
static bool crc_tables_generated;
|
||||
|
||||
bool SzArchive::Open(FILE *file)
|
||||
bool SzArchive::Open(FILE* file)
|
||||
{
|
||||
SzArEx_Init(&szarchive);
|
||||
|
||||
|
@ -39,7 +39,7 @@ bool SzArchive::Open(FILE *file)
|
|||
|
||||
FileInStream_CreateVTable(&archiveStream);
|
||||
LookToRead2_CreateVTable(&lookStream, 0);
|
||||
lookStream.buf = (Byte *)ISzAlloc_Alloc(&g_Alloc, kInputBufSize);
|
||||
lookStream.buf = (Byte*)ISzAlloc_Alloc(&g_Alloc, kInputBufSize);
|
||||
if (lookStream.buf == NULL)
|
||||
{
|
||||
File_Close(&archiveStream.file);
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 23, 2018
|
||||
Created on: Nov 23, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -30,46 +30,47 @@
|
|||
class SzArchive : public Archive
|
||||
{
|
||||
public:
|
||||
SzArchive() : out_buffer(NULL) {
|
||||
SzArchive() : out_buffer(NULL)
|
||||
{
|
||||
memset(&archiveStream, 0, sizeof(archiveStream));
|
||||
memset(&lookStream, 0, sizeof(lookStream));
|
||||
}
|
||||
~SzArchive() override;
|
||||
|
||||
ArchiveFile* OpenFile(const char* name) override;
|
||||
ArchiveFile *OpenFileByCrc(u32 crc) override;
|
||||
ArchiveFile* OpenFileByCrc(u32 crc) override;
|
||||
|
||||
protected:
|
||||
bool Open(FILE *file) override;
|
||||
bool Open(FILE* file) override;
|
||||
|
||||
private:
|
||||
CSzArEx szarchive;
|
||||
UInt32 block_idx; /* it can have any value before first call (if outBuffer = 0) */
|
||||
Byte *out_buffer; /* it must be 0 before first call for each new archive. */
|
||||
size_t out_buffer_size; /* it can have any value before first call (if outBuffer = 0) */
|
||||
UInt32 block_idx; /* it can have any value before first call (if outBuffer = 0) */
|
||||
Byte* out_buffer; /* it must be 0 before first call for each new archive. */
|
||||
size_t out_buffer_size; /* it can have any value before first call (if outBuffer = 0) */
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead2 lookStream;
|
||||
|
||||
};
|
||||
|
||||
class SzArchiveFile : public ArchiveFile
|
||||
{
|
||||
public:
|
||||
SzArchiveFile(u8 *data, u32 offset, u32 length)
|
||||
SzArchiveFile(u8* data, u32 offset, u32 length)
|
||||
: data(data), offset(offset), _length(length) {}
|
||||
u32 Read(void *buffer, u32 length) override
|
||||
u32 Read(void* buffer, u32 length) override
|
||||
{
|
||||
length = std::min(length, this->_length);
|
||||
memcpy(buffer, data + offset, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t length() override {
|
||||
size_t length() override
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 *data;
|
||||
u8* data;
|
||||
u32 offset;
|
||||
u32 _length;
|
||||
};
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 23, 2018
|
||||
Created on: Nov 23, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "ZipArchive.h"
|
||||
|
||||
|
@ -25,10 +25,10 @@ ZipArchive::~ZipArchive()
|
|||
zip_close(zip);
|
||||
}
|
||||
|
||||
bool ZipArchive::Open(FILE *file)
|
||||
bool ZipArchive::Open(FILE* file)
|
||||
{
|
||||
zip_error_t error;
|
||||
zip_source_t *source = zip_source_filep_create(file, 0, -1, &error);
|
||||
zip_source_t* source = zip_source_filep_create(file, 0, -1, &error);
|
||||
if (source == nullptr)
|
||||
{
|
||||
std::fclose(file);
|
||||
|
@ -42,7 +42,7 @@ bool ZipArchive::Open(FILE *file)
|
|||
|
||||
ArchiveFile* ZipArchive::OpenFile(const char* name)
|
||||
{
|
||||
zip_file_t *zip_file = zip_fopen(zip, name, 0);
|
||||
zip_file_t* zip_file = zip_fopen(zip, name, 0);
|
||||
if (zip_file == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
|
@ -50,7 +50,7 @@ ArchiveFile* ZipArchive::OpenFile(const char* name)
|
|||
return new ZipArchiveFile(zip_file, stat.size, stat.name);
|
||||
}
|
||||
|
||||
static zip_file *zip_fopen_by_crc(zip_t *za, u32 crc, int flags, zip_uint64_t& index)
|
||||
static zip_file* zip_fopen_by_crc(zip_t* za, u32 crc, int flags, zip_uint64_t& index)
|
||||
{
|
||||
if (crc == 0)
|
||||
return nullptr;
|
||||
|
@ -71,7 +71,7 @@ static zip_file *zip_fopen_by_crc(zip_t *za, u32 crc, int flags, zip_uint64_t& i
|
|||
ArchiveFile* ZipArchive::OpenFileByCrc(u32 crc)
|
||||
{
|
||||
zip_uint64_t index;
|
||||
zip_file_t *zip_file = zip_fopen_by_crc(zip, crc, 0, index);
|
||||
zip_file_t* zip_file = zip_fopen_by_crc(zip, crc, 0, index);
|
||||
if (zip_file == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
|
@ -85,10 +85,10 @@ u32 ZipArchiveFile::Read(void* buffer, u32 length)
|
|||
return zip_fread(zip_file, buffer, length);
|
||||
}
|
||||
|
||||
bool ZipArchive::Open(const void *data, size_t size)
|
||||
bool ZipArchive::Open(const void* data, size_t size)
|
||||
{
|
||||
zip_error_t error;
|
||||
zip_source_t *source = zip_source_buffer_create(data, size, 0, &error);
|
||||
zip_source_t* source = zip_source_buffer_create(data, size, 0, &error);
|
||||
if (source == nullptr)
|
||||
return false;
|
||||
zip = zip_open_from_source(source, 0, nullptr);
|
||||
|
@ -97,9 +97,9 @@ bool ZipArchive::Open(const void *data, size_t size)
|
|||
return zip != nullptr;
|
||||
}
|
||||
|
||||
ArchiveFile *ZipArchive::OpenFirstFile()
|
||||
ArchiveFile* ZipArchive::OpenFirstFile()
|
||||
{
|
||||
zip_file_t *zipFile = zip_fopen_index(zip, 0, 0);
|
||||
zip_file_t* zipFile = zip_fopen_index(zip, 0, 0);
|
||||
if (zipFile == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
|
@ -107,9 +107,9 @@ ArchiveFile *ZipArchive::OpenFirstFile()
|
|||
return new ZipArchiveFile(zipFile, stat.size, stat.name);
|
||||
}
|
||||
|
||||
ArchiveFile *ZipArchive::OpenFileByIndex(size_t index)
|
||||
ArchiveFile* ZipArchive::OpenFileByIndex(size_t index)
|
||||
{
|
||||
zip_file_t *zipFile = zip_fopen_index(zip, index, 0);
|
||||
zip_file_t* zipFile = zip_fopen_index(zip, index, 0);
|
||||
if (zipFile == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 23, 2018
|
||||
Created on: Nov 23, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -31,34 +31,37 @@ public:
|
|||
ArchiveFile* OpenFile(const char* name) override;
|
||||
ArchiveFile* OpenFileByCrc(u32 crc) override;
|
||||
|
||||
bool Open(FILE *file) override;
|
||||
bool Open(const void *data, size_t size);
|
||||
bool Open(FILE* file) override;
|
||||
bool Open(const void* data, size_t size);
|
||||
|
||||
ArchiveFile *OpenFirstFile();
|
||||
ArchiveFile *OpenFileByIndex(size_t index);
|
||||
ArchiveFile* OpenFirstFile();
|
||||
ArchiveFile* OpenFileByIndex(size_t index);
|
||||
|
||||
private:
|
||||
zip_t *zip = nullptr;
|
||||
zip_t* zip = nullptr;
|
||||
};
|
||||
|
||||
class ZipArchiveFile : public ArchiveFile
|
||||
{
|
||||
public:
|
||||
ZipArchiveFile(zip_file_t *zip_file, size_t length, const char *name)
|
||||
ZipArchiveFile(zip_file_t* zip_file, size_t length, const char* name)
|
||||
: zip_file(zip_file), _length(length), name(name) {}
|
||||
~ZipArchiveFile() override {
|
||||
~ZipArchiveFile() override
|
||||
{
|
||||
zip_fclose(zip_file);
|
||||
}
|
||||
u32 Read(void* buffer, u32 length) override;
|
||||
size_t length() override {
|
||||
size_t length() override
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
const char *getName() override {
|
||||
const char* getName() override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
private:
|
||||
zip_file_t *zip_file;
|
||||
zip_file_t* zip_file;
|
||||
size_t _length;
|
||||
const char *name;
|
||||
const char* name;
|
||||
};
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 23, 2018
|
||||
Created on: Nov 23, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "archive.h"
|
||||
|
@ -24,15 +24,18 @@
|
|||
#include "ZipArchive.h"
|
||||
#include "oslib/storage.h"
|
||||
|
||||
Archive *OpenArchive(const std::string& path)
|
||||
Archive* OpenArchive(const std::string& path)
|
||||
{
|
||||
FILE *file = nullptr;
|
||||
FILE* file = nullptr;
|
||||
hostfs::FileInfo fileInfo;
|
||||
try {
|
||||
try
|
||||
{
|
||||
fileInfo = hostfs::storage().getFileInfo(path);
|
||||
if (!fileInfo.isDirectory)
|
||||
file = hostfs::storage().openFile(path, "rb");
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
}
|
||||
catch (const hostfs::StorageException& e)
|
||||
{
|
||||
}
|
||||
if (file == nullptr)
|
||||
{
|
||||
|
@ -42,17 +45,20 @@ Archive *OpenArchive(const std::string& path)
|
|||
}
|
||||
if (file != nullptr)
|
||||
{
|
||||
Archive *sz_archive = new SzArchive();
|
||||
Archive* sz_archive = new SzArchive();
|
||||
if (sz_archive->Open(file))
|
||||
return sz_archive;
|
||||
delete sz_archive;
|
||||
file = nullptr;
|
||||
}
|
||||
// Retry as a zip file
|
||||
try {
|
||||
try
|
||||
{
|
||||
if (!fileInfo.isDirectory)
|
||||
file = hostfs::storage().openFile(path, "rb");
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
}
|
||||
catch (const hostfs::StorageException& e)
|
||||
{
|
||||
}
|
||||
if (file == nullptr)
|
||||
{
|
||||
|
@ -64,7 +70,7 @@ Archive *OpenArchive(const std::string& path)
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
Archive *zip_archive = new ZipArchive();
|
||||
Archive* zip_archive = new ZipArchive();
|
||||
if (zip_archive->Open(file))
|
||||
return zip_archive;
|
||||
delete zip_archive;
|
||||
|
@ -74,7 +80,7 @@ Archive *OpenArchive(const std::string& path)
|
|||
|
||||
bool Archive::Open(const char* path)
|
||||
{
|
||||
FILE *file = nowide::fopen(path, "rb");
|
||||
FILE* file = nowide::fopen(path, "rb");
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
return Open(file);
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/*
|
||||
Created on: Nov 23, 2018
|
||||
Created on: Nov 23, 2018
|
||||
|
||||
Copyright 2018 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -26,25 +26,25 @@ class ArchiveFile
|
|||
{
|
||||
public:
|
||||
virtual ~ArchiveFile() = default;
|
||||
virtual u32 Read(void *buffer, u32 length) = 0;
|
||||
virtual u32 Read(void* buffer, u32 length) = 0;
|
||||
virtual size_t length() = 0;
|
||||
virtual const char *getName() { return nullptr; }
|
||||
virtual const char* getName() { return nullptr; }
|
||||
};
|
||||
|
||||
class Archive
|
||||
{
|
||||
public:
|
||||
virtual ~Archive() = default;
|
||||
virtual ArchiveFile *OpenFile(const char *name) = 0;
|
||||
virtual ArchiveFile *OpenFileByCrc(u32 crc) = 0;
|
||||
virtual ArchiveFile* OpenFile(const char* name) = 0;
|
||||
virtual ArchiveFile* OpenFileByCrc(u32 crc) = 0;
|
||||
|
||||
protected:
|
||||
virtual bool Open(FILE *file) = 0;
|
||||
virtual bool Open(FILE* file) = 0;
|
||||
|
||||
private:
|
||||
bool Open(const char *name);
|
||||
bool Open(const char* name);
|
||||
|
||||
friend Archive *OpenArchive(const std::string& path);
|
||||
friend Archive* OpenArchive(const std::string& path);
|
||||
};
|
||||
|
||||
Archive *OpenArchive(const std::string& path);
|
||||
Archive* OpenArchive(const std::string& path);
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "rzip.h"
|
||||
#include <zlib.h>
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
const u8 RZipHeader[8] = { '#', 'R', 'Z', 'I', 'P', 'v', 1, '#' };
|
||||
|
||||
bool RZipFile::Open(FILE *file, bool write)
|
||||
bool RZipFile::Open(FILE* file, bool write)
|
||||
{
|
||||
verify(this->file == nullptr);
|
||||
verify(file != nullptr);
|
||||
|
@ -67,10 +67,11 @@ bool RZipFile::Open(FILE *file, bool write)
|
|||
|
||||
bool RZipFile::Open(const std::string& path, bool write)
|
||||
{
|
||||
FILE *f = nowide::fopen(path.c_str(), write ? "wb" : "rb");
|
||||
FILE* f = nowide::fopen(path.c_str(), write ? "wb" : "rb");
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
if (!Open(f, write)) {
|
||||
if (!Open(f, write))
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
@ -90,18 +91,18 @@ void RZipFile::Close()
|
|||
file = nullptr;
|
||||
if (chunk != nullptr)
|
||||
{
|
||||
delete [] chunk;
|
||||
delete[] chunk;
|
||||
chunk = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t RZipFile::Read(void *data, size_t length)
|
||||
size_t RZipFile::Read(void* data, size_t length)
|
||||
{
|
||||
verify(file != nullptr);
|
||||
verify(!write);
|
||||
|
||||
u8 *p = (u8 *)data;
|
||||
u8* p = (u8*)data;
|
||||
size_t rv = 0;
|
||||
while (rv < length)
|
||||
{
|
||||
|
@ -114,19 +115,19 @@ size_t RZipFile::Read(void *data, size_t length)
|
|||
break;
|
||||
if (zippedSize == 0)
|
||||
continue;
|
||||
u8 *zipped = new u8[zippedSize];
|
||||
u8* zipped = new u8[zippedSize];
|
||||
if (std::fread(zipped, zippedSize, 1, file) != 1)
|
||||
{
|
||||
delete [] zipped;
|
||||
delete[] zipped;
|
||||
break;
|
||||
}
|
||||
uLongf tl = maxChunkSize;
|
||||
if (uncompress(chunk, &tl, zipped, zippedSize) != Z_OK)
|
||||
{
|
||||
delete [] zipped;
|
||||
delete[] zipped;
|
||||
break;
|
||||
}
|
||||
delete [] zipped;
|
||||
delete[] zipped;
|
||||
chunkSize = (u32)tl;
|
||||
}
|
||||
u32 l = std::min(chunkSize - chunkIndex, (u32)(length - rv));
|
||||
|
@ -139,16 +140,16 @@ size_t RZipFile::Read(void *data, size_t length)
|
|||
return rv;
|
||||
}
|
||||
|
||||
size_t RZipFile::Write(const void *data, size_t length)
|
||||
size_t RZipFile::Write(const void* data, size_t length)
|
||||
{
|
||||
verify(file != nullptr);
|
||||
verify(write);
|
||||
|
||||
size += length;
|
||||
const u8 *p = (const u8 *)data;
|
||||
const u8* p = (const u8*)data;
|
||||
// compression output buffer must be 0.1% larger + 12 bytes
|
||||
uLongf maxZippedSize = maxChunkSize + maxChunkSize / 1000 + 12;
|
||||
u8 *zipped = new u8[maxZippedSize];
|
||||
u8* zipped = new u8[maxZippedSize];
|
||||
size_t rv = 0;
|
||||
while (rv < length)
|
||||
{
|
||||
|
@ -170,7 +171,7 @@ size_t RZipFile::Write(const void *data, size_t length)
|
|||
p += uncompressedSize;
|
||||
rv += uncompressedSize;
|
||||
}
|
||||
delete [] zipped;
|
||||
|
||||
delete[] zipped;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Implementation of the RZIP stream format as defined by libretro
|
||||
// https://github.com/libretro/libretro-common/blob/master/include/streams/rzip_stream.h
|
||||
|
@ -28,18 +28,18 @@ public:
|
|||
~RZipFile() { Close(); }
|
||||
|
||||
bool Open(const std::string& path, bool write);
|
||||
bool Open(FILE *file, bool write);
|
||||
bool Open(FILE* file, bool write);
|
||||
void Close();
|
||||
size_t Size() const { return size; }
|
||||
size_t Read(void *data, size_t length);
|
||||
size_t Write(const void *data, size_t length);
|
||||
FILE *rawFile() const { return file; }
|
||||
size_t Read(void* data, size_t length);
|
||||
size_t Write(const void* data, size_t length);
|
||||
FILE* rawFile() const { return file; }
|
||||
|
||||
private:
|
||||
FILE *file = nullptr;
|
||||
FILE* file = nullptr;
|
||||
u64 size = 0;
|
||||
u32 maxChunkSize = 0;
|
||||
u8 *chunk = nullptr;
|
||||
u8* chunk = nullptr;
|
||||
u32 chunkSize = 0;
|
||||
u32 chunkIndex = 0;
|
||||
bool write = false;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#if USE_ALSA
|
||||
#include "audiostream.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "cfg/cfg.h"
|
||||
#include "cfg/option.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
class AlsaAudioBackend : public AudioBackend
|
||||
{
|
||||
snd_pcm_t *handle = nullptr;
|
||||
snd_pcm_t* handle = nullptr;
|
||||
bool pcm_blocking = true;
|
||||
snd_pcm_uframes_t buffer_size = 0;
|
||||
snd_pcm_uframes_t period_size = 0;
|
||||
snd_pcm_t *handle_record = nullptr;
|
||||
snd_pcm_t* handle_record = nullptr;
|
||||
|
||||
public:
|
||||
AlsaAudioBackend()
|
||||
|
@ -18,12 +18,13 @@ public:
|
|||
|
||||
bool init() override
|
||||
{
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_hw_params_t* params;
|
||||
|
||||
std::string device = cfgLoadStr("alsa", "device", "");
|
||||
|
||||
int rc = -1;
|
||||
if (!device.empty() && device != "auto") {
|
||||
if (!device.empty() && device != "auto")
|
||||
{
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (rc < 0)
|
||||
WARN_LOG(AUDIO, "ALSA: Cannot open device %s. Trying auto", device.c_str());
|
||||
|
@ -79,7 +80,7 @@ public:
|
|||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
||||
/* Fill it in with default values. */
|
||||
rc=snd_pcm_hw_params_any(handle, params);
|
||||
rc = snd_pcm_hw_params_any(handle, params);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_any %s", snd_strerror(rc));
|
||||
|
@ -90,7 +91,7 @@ public:
|
|||
/* Set the desired hardware parameters. */
|
||||
|
||||
/* Interleaved mode */
|
||||
rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_access %s", snd_strerror(rc));
|
||||
|
@ -99,7 +100,7 @@ public:
|
|||
}
|
||||
|
||||
/* Signed 16-bit little-endian format */
|
||||
rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
|
||||
rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_format %s", snd_strerror(rc));
|
||||
|
@ -108,7 +109,7 @@ public:
|
|||
}
|
||||
|
||||
/* Two channels (stereo) */
|
||||
rc=snd_pcm_hw_params_set_channels(handle, params, 2);
|
||||
rc = snd_pcm_hw_params_set_channels(handle, params, 2);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_channels %s", snd_strerror(rc));
|
||||
|
@ -167,7 +168,7 @@ public:
|
|||
ERROR_LOG(AUDIO, "ALSA: Cannot open default audio capture device: %s", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot allocate hardware parameter structure: %s", snd_strerror(err));
|
||||
|
@ -245,7 +246,7 @@ public:
|
|||
err = 0;
|
||||
err = snd_pcm_prepare(handle_record);
|
||||
}
|
||||
u8 *buffer = (u8 *)frame + err;
|
||||
u8* buffer = (u8*)frame + err;
|
||||
memset(buffer, 0, (samples - err) * 2);
|
||||
}
|
||||
|
||||
|
@ -254,7 +255,8 @@ public:
|
|||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if (wait != pcm_blocking) {
|
||||
if (wait != pcm_blocking)
|
||||
{
|
||||
snd_pcm_nonblock(handle, wait ? 0 : 1);
|
||||
pcm_blocking = wait;
|
||||
}
|
||||
|
@ -268,7 +270,7 @@ public:
|
|||
// EPIPE means underrun
|
||||
// Write some silence then our samples
|
||||
const size_t silence_size = buffer_size - samples;
|
||||
void *silence = alloca(silence_size * 4);
|
||||
void* silence = alloca(silence_size * 4);
|
||||
memset(silence, 0, silence_size * 4);
|
||||
snd_pcm_writei(handle, silence, silence_size);
|
||||
snd_pcm_writei(handle, frame, samples);
|
||||
|
@ -287,7 +289,7 @@ public:
|
|||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
char **hints;
|
||||
char** hints;
|
||||
int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
|
||||
|
||||
// Error initializing ALSA
|
||||
|
@ -301,8 +303,8 @@ public:
|
|||
while (*n != NULL)
|
||||
{
|
||||
// Get the type (NULL/Input/Output)
|
||||
char *type = snd_device_name_get_hint(*n, "IOID");
|
||||
char *name = snd_device_name_get_hint(*n, "NAME");
|
||||
char* type = snd_device_name_get_hint(*n, "IOID");
|
||||
char* name = snd_device_name_get_hint(*n, "NAME");
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
|
@ -323,7 +325,6 @@ public:
|
|||
|
||||
result.emplace_back(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (type != NULL)
|
||||
|
@ -340,7 +341,7 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
Option* getOptions(int *count) override
|
||||
Option* getOptions(int* count) override
|
||||
{
|
||||
*count = 1;
|
||||
static Option result;
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/*
|
||||
Simple Core Audio backend for osx (and maybe ios?)
|
||||
Based off various audio core samples and dolphin's code
|
||||
Simple Core Audio backend for osx (and maybe ios?)
|
||||
Based off various audio core samples and dolphin's code
|
||||
|
||||
This is part of the Reicast project, please consult the
|
||||
LICENSE file for licensing & related information
|
||||
This is part of the Reicast project, please consult the
|
||||
LICENSE file for licensing & related information
|
||||
|
||||
This could do with some locking logic to avoid
|
||||
race conditions, and some variable length buffer
|
||||
logic to support chunk sizes other than 512 bytes
|
||||
This could do with some locking logic to avoid
|
||||
race conditions, and some variable length buffer
|
||||
logic to support chunk sizes other than 512 bytes
|
||||
|
||||
It does work on my macmini though
|
||||
It does work on my macmini though
|
||||
*/
|
||||
#if defined(__APPLE__)
|
||||
#include "audiostream.h"
|
||||
|
@ -18,15 +18,15 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
class CoreAudioBackend : public AudioBackend
|
||||
{
|
||||
AudioUnit audioUnit;
|
||||
|
||||
u32 BUFSIZE = 0;
|
||||
u8 *samples_temp = nullptr;
|
||||
u8* samples_temp = nullptr;
|
||||
|
||||
std::atomic<int> samples_wptr;
|
||||
std::atomic<int> samples_rptr;
|
||||
|
@ -40,19 +40,19 @@ class CoreAudioBackend : public AudioBackend
|
|||
AudioQueueRef recordQueue;
|
||||
|
||||
static OSStatus renderCallback(void* ctx, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* ts,
|
||||
UInt32 bus, UInt32 frames, AudioBufferList* abl)
|
||||
UInt32 bus, UInt32 frames, AudioBufferList* abl)
|
||||
{
|
||||
CoreAudioBackend *backend = (CoreAudioBackend *)ctx;
|
||||
CoreAudioBackend* backend = (CoreAudioBackend*)ctx;
|
||||
for (int i = 0; i < abl->mNumberBuffers; i++)
|
||||
{
|
||||
int size = abl->mBuffers[i].mDataByteSize;
|
||||
u8 *outBuffer = (u8 *)abl->mBuffers[i].mData;
|
||||
u8* outBuffer = (u8*)abl->mBuffers[i].mData;
|
||||
while (size != 0)
|
||||
{
|
||||
int avail = (backend->samples_wptr - backend->samples_rptr + backend->BUFSIZE) % backend->BUFSIZE;
|
||||
if (avail == 0)
|
||||
{
|
||||
//printf("Core Audio: buffer underrun %d bytes (%d)", size, abl->mBuffers[i].mDataByteSize);
|
||||
// printf("Core Audio: buffer underrun %d bytes (%d)", size, abl->mBuffers[i].mDataByteSize);
|
||||
memset(outBuffer, '\0', size);
|
||||
return noErr;
|
||||
}
|
||||
|
@ -93,23 +93,25 @@ public:
|
|||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
component = AudioComponentFindNext(nullptr, &desc);
|
||||
|
||||
if (component == nullptr) {
|
||||
if (component == nullptr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioComponentFindNext failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceNew(component, &audioUnit);
|
||||
if (err != noErr) {
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioComponentInstanceNew failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
FillOutASBDForLPCM(format, 44100,
|
||||
2, 16, 16, false, false, false);
|
||||
2, 16, 16, false, false, false);
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &format,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &format,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
|
||||
|
@ -120,9 +122,9 @@ public:
|
|||
callback_struct.inputProc = renderCallback;
|
||||
callback_struct.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callback_struct,
|
||||
sizeof callback_struct);
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callback_struct,
|
||||
sizeof callback_struct);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
|
||||
|
@ -182,10 +184,10 @@ public:
|
|||
avail = std::min(avail, (int)BUFSIZE - samples_wptr);
|
||||
memcpy(&samples_temp[samples_wptr], frame, avail);
|
||||
samples_wptr = (samples_wptr + avail) % BUFSIZE;
|
||||
frame = (u8 *)frame + avail;
|
||||
frame = (u8*)frame + avail;
|
||||
size -= avail;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -195,15 +197,15 @@ public:
|
|||
AudioUnitUninitialize(audioUnit);
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
bufferEmpty.Set();
|
||||
delete [] samples_temp;
|
||||
delete[] samples_temp;
|
||||
samples_temp = nullptr;
|
||||
}
|
||||
|
||||
static void recordCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime, UInt32 frameSize, const AudioStreamPacketDescription *dataFormat)
|
||||
static void recordCallback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp* inStartTime, UInt32 frameSize, const AudioStreamPacketDescription* dataFormat)
|
||||
{
|
||||
//DEBUG_LOG(AUDIO, "AudioQueue callback: wptr %d rptr %d bytes %d", (int)input_wptr, (int)input_rptr, inBuffer->mAudioDataByteSize);
|
||||
CoreAudioBackend *backend = (CoreAudioBackend *)inUserData;
|
||||
// DEBUG_LOG(AUDIO, "AudioQueue callback: wptr %d rptr %d bytes %d", (int)input_wptr, (int)input_rptr, inBuffer->mAudioDataByteSize);
|
||||
CoreAudioBackend* backend = (CoreAudioBackend*)inUserData;
|
||||
UInt32 size = inBuffer->mAudioDataByteSize;
|
||||
UInt32 freeSpace = (backend->input_rptr - backend->input_wptr - 2 + InputBufSize) % InputBufSize;
|
||||
if (size > freeSpace)
|
||||
|
@ -233,7 +235,7 @@ public:
|
|||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
AudioStreamBasicDescription desc{};
|
||||
AudioStreamBasicDescription desc {};
|
||||
desc.mFormatID = kAudioFormatLinearPCM;
|
||||
desc.mSampleRate = (double)sampling_freq;
|
||||
desc.mChannelsPerFrame = 1;
|
||||
|
@ -242,14 +244,14 @@ public:
|
|||
desc.mFramesPerPacket = 1;
|
||||
desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
desc.mReserved = 0;
|
||||
|
||||
|
||||
OSStatus err = AudioQueueNewInput(&desc,
|
||||
recordCallback,
|
||||
this,
|
||||
nullptr,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&recordQueue);
|
||||
recordCallback,
|
||||
this,
|
||||
nullptr,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&recordQueue);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "AudioQueueNewInput failed: %d", err);
|
||||
|
@ -280,7 +282,7 @@ public:
|
|||
|
||||
u32 record(void* frame, u32 samples) override
|
||||
{
|
||||
// DEBUG_LOG(AUDIO, "coreaudio_record: wptr %d rptr %d", (int)input_wptr, (int)input_rptr);
|
||||
// DEBUG_LOG(AUDIO, "coreaudio_record: wptr %d rptr %d", (int)input_wptr, (int)input_rptr);
|
||||
u32 size = samples * 2;
|
||||
while (size != 0)
|
||||
{
|
||||
|
@ -294,7 +296,7 @@ public:
|
|||
avail = std::min(avail, (u32)(InputBufSize - input_rptr));
|
||||
|
||||
memcpy(frame, &samples_input[input_rptr], avail);
|
||||
frame = (u8 *)frame + avail;
|
||||
frame = (u8*)frame + avail;
|
||||
input_rptr = (input_rptr + avail) % InputBufSize;
|
||||
size -= avail;
|
||||
}
|
||||
|
@ -304,5 +306,4 @@ public:
|
|||
};
|
||||
static CoreAudioBackend coreAudioBackend;
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,22 +2,23 @@
|
|||
#if defined(_WIN32) && !defined(TARGET_UWP)
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include <initguid.h>
|
||||
#include "oslib/oslib.h"
|
||||
#include "stdclass.h"
|
||||
#include <dsound.h>
|
||||
#include <vector>
|
||||
#include <initguid.h>
|
||||
|
||||
#include "windows/comptr.h"
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include "stdclass.h"
|
||||
#include "windows/comptr.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include <vector>
|
||||
|
||||
HWND getNativeHwnd();
|
||||
#define verifyc(x) verify(!FAILED(x))
|
||||
|
||||
#ifdef __CRT_UUID_DECL
|
||||
__CRT_UUID_DECL(IDirectSoundBuffer8, 0x6825A449,0x7524,0x4D82,0x92,0x0F,0x50,0xE3,0x6A,0xB3,0xAB,0x1E);
|
||||
__CRT_UUID_DECL(IDirectSoundNotify, 0xB0210783,0x89cd,0x11d0,0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16);
|
||||
__CRT_UUID_DECL(IDirectSoundCaptureBuffer8, 0x00990DF4,0x0DBB,0x4872,0x83,0x3E,0x6D,0x30,0x3E,0x80,0xAE,0xB6);
|
||||
__CRT_UUID_DECL(IDirectSoundBuffer8, 0x6825A449, 0x7524, 0x4D82, 0x92, 0x0F, 0x50, 0xE3, 0x6A, 0xB3, 0xAB, 0x1E);
|
||||
__CRT_UUID_DECL(IDirectSoundNotify, 0xB0210783, 0x89cd, 0x11d0, 0xAF, 0x08, 0x00, 0xA0, 0xC9, 0x25, 0xCD, 0x16);
|
||||
__CRT_UUID_DECL(IDirectSoundCaptureBuffer8, 0x00990DF4, 0x0DBB, 0x4872, 0x83, 0x3E, 0x6D, 0x30, 0x3E, 0x80, 0xAE, 0xB6);
|
||||
#else
|
||||
struct __declspec(uuid("{6825A449-7524-4D82-920F-50E36AB3AB1E}")) IDirectSoundBuffer8;
|
||||
struct __declspec(uuid("{B0210783-89cd-11d0-AF08-00A0C925CD16}")) IDirectSoundNotify;
|
||||
|
@ -41,7 +42,8 @@ class DirectSoundBackend : public AudioBackend
|
|||
|
||||
RingBuffer ringBuffer;
|
||||
|
||||
u32 notificationOffset(int index) {
|
||||
u32 notificationOffset(int index)
|
||||
{
|
||||
return index * SAMPLE_BYTES;
|
||||
}
|
||||
|
||||
|
@ -83,11 +85,13 @@ public:
|
|||
|
||||
bool init() override
|
||||
{
|
||||
if (FAILED(DirectSoundCreate8(NULL, &dsound.get(), NULL))) {
|
||||
if (FAILED(DirectSoundCreate8(NULL, &dsound.get(), NULL)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound8 initialization failed");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dsound->SetCooperativeLevel(getNativeHwnd(), DSSCL_PRIORITY))) {
|
||||
if (FAILED(dsound->SetCooperativeLevel(getNativeHwnd(), DSSCL_PRIORITY)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound8 SetCooperativeLevel failed");
|
||||
dsound.reset();
|
||||
return false;
|
||||
|
@ -114,7 +118,7 @@ public:
|
|||
// Create the buffer
|
||||
ComPtr<IDirectSoundBuffer> buffer_;
|
||||
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_.get(), 0))
|
||||
|| FAILED(buffer_.as(buffer)))
|
||||
|| FAILED(buffer_.as(buffer)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound8 CreateSoundBuffer failed");
|
||||
dsound.reset();
|
||||
|
@ -159,7 +163,7 @@ public:
|
|||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
|
||||
while (!ringBuffer.write((const u8*)frame, samples * 4) && wait)
|
||||
pushWait.Wait();
|
||||
|
||||
return 1;
|
||||
|
@ -186,8 +190,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
HRESULT hr;
|
||||
WAVEFORMATEX wfx =
|
||||
{ WAVE_FORMAT_PCM, 1, sampling_freq, sampling_freq * 2, 2, 16, 0 };
|
||||
WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, sampling_freq, sampling_freq * 2, 2, 16, 0 };
|
||||
// wFormatTag, nChannels, nSamplesPerSec, nAvgBytesPerSec,
|
||||
// nBlockAlign, wBitsPerSample, cbSize
|
||||
|
||||
|
@ -214,7 +217,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
u32 record(void* buffer, u32 samples) override
|
||||
{
|
||||
DWORD readPos;
|
||||
capture_buffer->GetCurrentPosition(NULL, &readPos);
|
||||
|
@ -223,7 +226,7 @@ public:
|
|||
capture_buffer->Lock(readPos, samples * 2, &p1, &p1bytes, &p2, &p2bytes, 0);
|
||||
memcpy(buffer, p1, p1bytes);
|
||||
if (p2bytes > 0)
|
||||
memcpy((u8 *)buffer + p1bytes, p2, p2bytes);
|
||||
memcpy((u8*)buffer + p1bytes, p2, p2bytes);
|
||||
capture_buffer->Unlock(p1, p1bytes, p2, p2bytes);
|
||||
return (p1bytes + p2bytes) / 2;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
class LibAOBackend : public AudioBackend
|
||||
{
|
||||
ao_device *aodevice = nullptr;
|
||||
ao_device* aodevice = nullptr;
|
||||
|
||||
public:
|
||||
LibAOBackend()
|
||||
|
@ -23,7 +23,8 @@ public:
|
|||
aodevice = ao_open_live(ao_default_driver_id(), &aoformat, NULL); // Live output
|
||||
if (aodevice == nullptr)
|
||||
aodevice = ao_open_live(ao_driver_id("null"), &aoformat, NULL);
|
||||
if (aodevice == nullptr) {
|
||||
if (aodevice == nullptr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot open libao driver");
|
||||
ao_shutdown();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ public:
|
|||
else
|
||||
last_time += fduration;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
last_time = the_clock::now();
|
||||
}
|
||||
return 1;
|
||||
|
@ -42,7 +43,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
u32 record(void* buffer, u32 samples) override
|
||||
{
|
||||
memset(buffer, 0, samples * 2);
|
||||
return samples;
|
||||
|
|
|
@ -3,28 +3,28 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef USE_OBOE
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include <oboe/Oboe.h>
|
||||
#include <vector>
|
||||
#include "stdclass.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include "stdclass.h"
|
||||
#include <oboe/Oboe.h>
|
||||
#include <vector>
|
||||
|
||||
class OboeBackend : AudioBackend
|
||||
{
|
||||
|
@ -37,11 +37,11 @@ class OboeBackend : AudioBackend
|
|||
class AudioCallback : public oboe::AudioStreamDataCallback
|
||||
{
|
||||
public:
|
||||
AudioCallback(OboeBackend *backend) : backend(backend) {}
|
||||
AudioCallback(OboeBackend* backend) : backend(backend) {}
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) override
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames) override
|
||||
{
|
||||
if (!backend->ringBuffer.read((u8 *)audioData, numFrames * 4))
|
||||
if (!backend->ringBuffer.read((u8*)audioData, numFrames * 4))
|
||||
// underrun
|
||||
memset(audioData, 0, numFrames * 4);
|
||||
backend->pushWait.Set();
|
||||
|
@ -49,16 +49,16 @@ class OboeBackend : AudioBackend
|
|||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
OboeBackend *backend;
|
||||
OboeBackend* backend;
|
||||
};
|
||||
AudioCallback audioCallback;
|
||||
|
||||
class AudioErrorCallback : public oboe::AudioStreamErrorCallback
|
||||
{
|
||||
public:
|
||||
AudioErrorCallback(OboeBackend *backend) : backend(backend) {}
|
||||
AudioErrorCallback(OboeBackend* backend) : backend(backend) {}
|
||||
|
||||
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) override
|
||||
void onErrorAfterClose(oboe::AudioStream* stream, oboe::Result error) override
|
||||
{
|
||||
// Only attempt to recover if init was successful
|
||||
if (backend->stream != nullptr)
|
||||
|
@ -71,7 +71,7 @@ class OboeBackend : AudioBackend
|
|||
}
|
||||
}
|
||||
|
||||
OboeBackend *backend;
|
||||
OboeBackend* backend;
|
||||
};
|
||||
AudioErrorCallback errorCallback;
|
||||
|
||||
|
@ -86,17 +86,17 @@ public:
|
|||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Stereo)
|
||||
->setSampleRate(44100)
|
||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||
->setFramesPerCallback(SAMPLE_COUNT)
|
||||
->setDataCallback(&audioCallback)
|
||||
->setErrorCallback(&errorCallback)
|
||||
->setUsage(oboe::Usage::Game)
|
||||
->openStream(stream);
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Stereo)
|
||||
->setSampleRate(44100)
|
||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||
->setFramesPerCallback(SAMPLE_COUNT)
|
||||
->setDataCallback(&audioCallback)
|
||||
->setErrorCallback(&errorCallback)
|
||||
->setUsage(oboe::Usage::Game)
|
||||
->openStream(stream);
|
||||
if (result != oboe::Result::OK)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Oboe open stream failed: %s", oboe::convertToText(result));
|
||||
|
@ -118,8 +118,8 @@ public:
|
|||
|
||||
stream->requestStart();
|
||||
NOTICE_LOG(AUDIO, "Oboe driver started. stream capacity: %d frames, size: %d frames, frames/callback: %d, frames/burst: %d",
|
||||
stream->getBufferCapacityInFrames(), stream->getBufferSizeInFrames(),
|
||||
stream->getFramesPerCallback(), stream->getFramesPerBurst());
|
||||
stream->getBufferCapacityInFrames(), stream->getBufferSizeInFrames(),
|
||||
stream->getFramesPerCallback(), stream->getFramesPerBurst());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ public:
|
|||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
|
||||
while (!ringBuffer.write((const u8*)frame, samples * 4) && wait)
|
||||
pushWait.Wait();
|
||||
|
||||
return 1;
|
||||
|
@ -163,12 +163,12 @@ public:
|
|||
{
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setDirection(oboe::Direction::Input)
|
||||
->setPerformanceMode(oboe::PerformanceMode::None)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Mono)
|
||||
->setSampleRate(sampling_freq)
|
||||
->openStream(recordStream);
|
||||
->setPerformanceMode(oboe::PerformanceMode::None)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Mono)
|
||||
->setSampleRate(sampling_freq)
|
||||
->openStream(recordStream);
|
||||
if (result != oboe::Result::OK)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Oboe open record stream failed: %s", oboe::convertToText(result));
|
||||
|
@ -176,12 +176,12 @@ public:
|
|||
}
|
||||
recordStream->requestStart();
|
||||
NOTICE_LOG(AUDIO, "Oboe recorder started. stream capacity: %d frames",
|
||||
recordStream->getBufferCapacityInFrames());
|
||||
recordStream->getBufferCapacityInFrames());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *data, u32 samples) override
|
||||
u32 record(void* data, u32 samples) override
|
||||
{
|
||||
if (recordStream == nullptr)
|
||||
return 0;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <IL/OMX_Broadcom.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PORT_INDEX 100
|
||||
#define PORT_INDEX 100
|
||||
#define OUTPUT_FREQ 44100
|
||||
|
||||
class OMXAudioBackend : public AudioBackend
|
||||
|
@ -21,16 +21,16 @@ class OMXAudioBackend : public AudioBackend
|
|||
pthread_cond_t omx_state_cond;
|
||||
|
||||
static OMX_ERRORTYPE eventHandler(
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_EVENTTYPE eEvent,
|
||||
OMX_IN OMX_U32 nData1,
|
||||
OMX_IN OMX_U32 nData2,
|
||||
OMX_IN OMX_PTR pEventData)
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_EVENTTYPE eEvent,
|
||||
OMX_IN OMX_U32 nData1,
|
||||
OMX_IN OMX_U32 nData2,
|
||||
OMX_IN OMX_PTR pEventData)
|
||||
{
|
||||
OMXAudioBackend *backend = (OMXAudioBackend *)pAddData;
|
||||
OMXAudioBackend* backend = (OMXAudioBackend*)pAddData;
|
||||
pthread_mutex_lock(&backend->audio_lock);
|
||||
if(eEvent == OMX_EventCmdComplete && nData1 == OMX_CommandStateSet)
|
||||
if (eEvent == OMX_EventCmdComplete && nData1 == OMX_CommandStateSet)
|
||||
{
|
||||
backend->omx_state = (OMX_STATETYPE)nData2;
|
||||
pthread_cond_signal(&backend->omx_state_cond);
|
||||
|
@ -40,17 +40,17 @@ class OMXAudioBackend : public AudioBackend
|
|||
}
|
||||
|
||||
static OMX_ERRORTYPE emptyBufferDone(
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
{
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
static OMX_ERRORTYPE fillBufferDone(
|
||||
OMX_OUT OMX_HANDLETYPE hComponent,
|
||||
OMX_OUT OMX_PTR pAppData,
|
||||
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
OMX_OUT OMX_HANDLETYPE hComponent,
|
||||
OMX_OUT OMX_PTR pAppData,
|
||||
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
{
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class OMXAudioBackend : public AudioBackend
|
|||
void waitForState(OMX_STATETYPE state)
|
||||
{
|
||||
pthread_mutex_lock(&audio_lock);
|
||||
while(omx_state != state)
|
||||
while (omx_state != state)
|
||||
pthread_cond_wait(&omx_state_cond, &audio_lock);
|
||||
pthread_mutex_unlock(&audio_lock);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class OMXAudioBackend : public AudioBackend
|
|||
param.nPortIndex = PORT_INDEX;
|
||||
|
||||
OMX_ERRORTYPE error = OMX_GetConfig(omx_handle, OMX_IndexConfigAudioRenderingLatency, ¶m);
|
||||
if(error != OMX_ErrorNone)
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to get OMX configuration (OMX_IndexConfigAudioRenderingLatency). Error 0x%X", error);
|
||||
|
||||
return param.nU32 * 1000 / OUTPUT_FREQ;
|
||||
|
@ -103,7 +103,7 @@ public:
|
|||
callbacks.EmptyBufferDone = emptyBufferDone;
|
||||
callbacks.FillBufferDone = fillBufferDone;
|
||||
|
||||
error = OMX_GetHandle(&omx_handle, (OMX_STRING)"OMX.broadcom.audio_render", this, &callbacks);
|
||||
error = OMX_GetHandle(&omx_handle, (OMX_STRING) "OMX.broadcom.audio_render", this, &callbacks);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: OMX_GetHandle() failed. Error 0x%X", error);
|
||||
|
@ -167,11 +167,11 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
for(u32 i = 0; i < param2.nPorts; i++)
|
||||
for (u32 i = 0; i < param2.nPorts; i++)
|
||||
{
|
||||
u32 port = param2.nStartPortNumber + i;
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandPortDisable, port, NULL);
|
||||
if(error != OMX_ErrorNone)
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to do OMX_CommandPortDisable on port %u. Error 0x%X", port, error);
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ public:
|
|||
audio_buffers[i]->nFilledLen = buffer_size;
|
||||
|
||||
error = OMX_EmptyThisBuffer(omx_handle, audio_buffers[i]);
|
||||
if(error != OMX_ErrorNone)
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to empty buffer[%u]. Error 0x%X", i, error);
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,7 @@ public:
|
|||
memset(&ar_dest, 0, sizeof(ar_dest));
|
||||
ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE);
|
||||
ar_dest.nVersion.nVersion = OMX_VERSION;
|
||||
strcpy((char *)ar_dest.sName, output_device);
|
||||
strcpy((char*)ar_dest.sName, output_device);
|
||||
error = OMX_SetConfig(omx_handle, OMX_IndexConfigBrcmAudioDestination, &ar_dest);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
|
@ -271,38 +271,38 @@ public:
|
|||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if(audio_buffers == NULL)
|
||||
if (audio_buffers == NULL)
|
||||
return 1;
|
||||
|
||||
size_t data_size = samples * 4;
|
||||
|
||||
while(data_size > 0)
|
||||
while (data_size > 0)
|
||||
{
|
||||
size_t copy_size = std::min(buffer_size - buffer_length, data_size);
|
||||
|
||||
// Don't have more than maximum audio latency
|
||||
u32 latency = getLatency();
|
||||
if(latency > latency_max)
|
||||
if (latency > latency_max)
|
||||
{
|
||||
usleep((latency - latency_max) * 1000);
|
||||
}
|
||||
else if(latency == 0)
|
||||
else if (latency == 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OMX: underrun occurred");
|
||||
}
|
||||
|
||||
memcpy(audio_buffers[audio_buffer_idx]->pBuffer + buffer_length, frame, copy_size);
|
||||
buffer_length += copy_size;
|
||||
frame = (char *)frame + copy_size;
|
||||
frame = (char*)frame + copy_size;
|
||||
|
||||
// Flush buffer and swap
|
||||
if(buffer_length >= buffer_size)
|
||||
if (buffer_length >= buffer_size)
|
||||
{
|
||||
audio_buffers[audio_buffer_idx]->nOffset = 0;
|
||||
audio_buffers[audio_buffer_idx]->nFilledLen = buffer_size;
|
||||
|
||||
OMX_ERRORTYPE error = OMX_EmptyThisBuffer(omx_handle, audio_buffers[audio_buffer_idx]);
|
||||
if(error != OMX_ErrorNone)
|
||||
if (error != OMX_ErrorNone)
|
||||
INFO_LOG(AUDIO, "OMX: failed to empty buffer[%u]. Error 0x%X", audio_buffer_idx, error);
|
||||
|
||||
audio_buffer_idx = (audio_buffer_idx + 1) % buffer_count;
|
||||
|
@ -322,10 +322,10 @@ public:
|
|||
// Is there anything else that needs to be done for omx?
|
||||
|
||||
error = OMX_Deinit();
|
||||
if(error != OMX_ErrorNone)
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: OMX_Deinit() failed. Error 0x%X", error);
|
||||
|
||||
if(audio_buffers != NULL)
|
||||
if (audio_buffers != NULL)
|
||||
{
|
||||
delete[] audio_buffers;
|
||||
audio_buffers = NULL;
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
{
|
||||
recordFD = openDevice(O_RDONLY);
|
||||
|
||||
if (recordFD < 0 || !setRate(recordFD, sampling_freq) || !setChannels(recordFD, 1) || !setFormat(recordFD, AFMT_S16_NE /* Native 16 bits */ ))
|
||||
if (recordFD < 0 || !setRate(recordFD, sampling_freq) || !setChannels(recordFD, 1) || !setFormat(recordFD, AFMT_S16_NE /* Native 16 bits */))
|
||||
{
|
||||
termRecord();
|
||||
return false;
|
||||
|
@ -130,7 +130,7 @@ public:
|
|||
recordFD = -1;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
u32 record(void* buffer, u32 samples) override
|
||||
{
|
||||
samples *= 2;
|
||||
int l = read(recordFD, buffer, samples);
|
||||
|
@ -141,7 +141,7 @@ public:
|
|||
INFO_LOG(AUDIO, "OSS: Recording error");
|
||||
l = 0;
|
||||
}
|
||||
memset((u8 *)buffer + l, 0, samples - l);
|
||||
memset((u8*)buffer + l, 0, samples - l);
|
||||
}
|
||||
return l / 2;
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
class PulseAudioBackend : public AudioBackend
|
||||
{
|
||||
pa_threaded_mainloop *mainloop = nullptr;
|
||||
pa_context *context = nullptr;
|
||||
pa_stream *stream = nullptr;
|
||||
pa_stream *record_stream = nullptr;
|
||||
pa_threaded_mainloop* mainloop = nullptr;
|
||||
pa_context* context = nullptr;
|
||||
pa_stream* stream = nullptr;
|
||||
pa_stream* record_stream = nullptr;
|
||||
|
||||
static void context_state_cb(pa_context *c, void *userdata)
|
||||
static void context_state_cb(pa_context* c, void* userdata)
|
||||
{
|
||||
PulseAudioBackend *backend = (PulseAudioBackend *)userdata;
|
||||
PulseAudioBackend* backend = (PulseAudioBackend*)userdata;
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
|
@ -25,9 +25,9 @@ class PulseAudioBackend : public AudioBackend
|
|||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
|
||||
static void stream_request_cb(pa_stream* s, size_t length, void* userdata)
|
||||
{
|
||||
PulseAudioBackend *backend = (PulseAudioBackend *)userdata;
|
||||
PulseAudioBackend* backend = (PulseAudioBackend*)userdata;
|
||||
pa_threaded_mainloop_signal(backend->mainloop, 0);
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ public:
|
|||
term();
|
||||
return false;
|
||||
}
|
||||
pa_sample_spec spec{ PA_SAMPLE_S16LE, 44100, 2 };
|
||||
pa_sample_spec spec { PA_SAMPLE_S16LE, 44100, 2 };
|
||||
|
||||
stream = pa_stream_new(context, "audio", &spec, NULL);
|
||||
if (!stream)
|
||||
|
@ -87,11 +87,11 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
//pa_stream_set_state_callback(stream, stream_state_cb, this);
|
||||
// pa_stream_set_state_callback(stream, stream_state_cb, this);
|
||||
pa_stream_set_write_callback(stream, stream_request_cb, this);
|
||||
//pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, this);
|
||||
//pa_stream_set_underflow_callback(stream, underrun_update_cb, this);
|
||||
//pa_stream_set_buffer_attr_callback(stream, buffer_attr_cb, this);
|
||||
// pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, this);
|
||||
// pa_stream_set_underflow_callback(stream, underrun_update_cb, this);
|
||||
// pa_stream_set_buffer_attr_callback(stream, buffer_attr_cb, this);
|
||||
|
||||
pa_buffer_attr buffer_attr;
|
||||
buffer_attr.maxlength = -1;
|
||||
|
@ -118,7 +118,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
const pa_buffer_attr *server_attr = pa_stream_get_buffer_attr(stream);
|
||||
const pa_buffer_attr* server_attr = pa_stream_get_buffer_attr(stream);
|
||||
if (server_attr)
|
||||
DEBUG_LOG(AUDIO, "PulseAudio: requested %d samples buffer, got %d", buffer_attr.tlength / 4, server_attr->tlength / 4);
|
||||
|
||||
|
@ -129,7 +129,7 @@ public:
|
|||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
const u8 *buf = (const u8 *)frame;
|
||||
const u8* buf = (const u8*)frame;
|
||||
size_t size = samples * 4;
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
|
@ -178,7 +178,7 @@ public:
|
|||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
pa_sample_spec spec{ PA_SAMPLE_S16LE, sampling_freq, 1 };
|
||||
pa_sample_spec spec { PA_SAMPLE_S16LE, sampling_freq, 1 };
|
||||
|
||||
record_stream = pa_stream_new(context, "record", &spec, NULL);
|
||||
if (!record_stream)
|
||||
|
@ -218,12 +218,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
u32 record(void* buffer, u32 samples) override
|
||||
{
|
||||
if (record_stream == nullptr)
|
||||
return 0;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
const void *data;
|
||||
const void* data;
|
||||
size_t size;
|
||||
if (pa_stream_peek(record_stream, &data, &size) < 0)
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#if defined(USE_SDL_AUDIO)
|
||||
|
||||
#include <SDL.h>
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "stdclass.h"
|
||||
#include <SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
@ -15,7 +15,7 @@ class SDLAudioBackend : AudioBackend
|
|||
bool needs_resampling = false;
|
||||
cResetEvent read_wait;
|
||||
std::mutex stream_mutex;
|
||||
uint32_t *sample_buffer;
|
||||
uint32_t* sample_buffer;
|
||||
unsigned sample_buffer_size = 0;
|
||||
unsigned sample_count = 0;
|
||||
SDL_AudioCVT audioCvt;
|
||||
|
@ -27,7 +27,7 @@ class SDLAudioBackend : AudioBackend
|
|||
|
||||
static void audioCallback(void* userdata, Uint8* stream, int len)
|
||||
{
|
||||
SDLAudioBackend *backend = (SDLAudioBackend *)userdata;
|
||||
SDLAudioBackend* backend = (SDLAudioBackend*)userdata;
|
||||
|
||||
backend->stream_mutex.lock();
|
||||
// Wait until there's enough samples to feed the kraken
|
||||
|
@ -43,7 +43,8 @@ class SDLAudioBackend : AudioBackend
|
|||
return;
|
||||
}
|
||||
|
||||
if (!backend->needs_resampling) {
|
||||
if (!backend->needs_resampling)
|
||||
{
|
||||
// Just copy bytes for this case.
|
||||
memcpy(stream, &backend->sample_buffer[0], len);
|
||||
}
|
||||
|
@ -72,12 +73,13 @@ public:
|
|||
{
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO))
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "SDL2 error initializing audio subsystem: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sample_buffer_size = std::max<u32>(SAMPLE_COUNT * 2, config::AudioBufferSize);
|
||||
sample_buffer = new uint32_t[sample_buffer_size]();
|
||||
sample_count = 0;
|
||||
|
@ -88,7 +90,7 @@ public:
|
|||
wav_spec.freq = 44100;
|
||||
wav_spec.format = AUDIO_S16;
|
||||
wav_spec.channels = 2;
|
||||
wav_spec.samples = SAMPLE_COUNT * 2; // Must be power of two
|
||||
wav_spec.samples = SAMPLE_COUNT * 2; // Must be power of two
|
||||
wav_spec.callback = audioCallback;
|
||||
wav_spec.userdata = this;
|
||||
needs_resampling = false;
|
||||
|
@ -131,8 +133,10 @@ public:
|
|||
|
||||
// If wait, then wait for the buffer to be smaller than a certain size.
|
||||
stream_mutex.lock();
|
||||
if (wait) {
|
||||
while (sample_count + samples > sample_buffer_size) {
|
||||
if (wait)
|
||||
{
|
||||
while (sample_count + samples > sample_buffer_size)
|
||||
{
|
||||
stream_mutex.unlock();
|
||||
read_wait.Wait();
|
||||
stream_mutex.lock();
|
||||
|
@ -159,18 +163,18 @@ public:
|
|||
SDL_CloseAudioDevice(audiodev);
|
||||
audiodev = SDL_AudioDeviceID();
|
||||
}
|
||||
delete [] sample_buffer;
|
||||
delete[] sample_buffer;
|
||||
sample_buffer = nullptr;
|
||||
if (needs_resampling)
|
||||
{
|
||||
delete [] audioCvt.buf;
|
||||
delete[] audioCvt.buf;
|
||||
audioCvt.buf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void recordCallback(void *userdata, u8 *stream, int len)
|
||||
static void recordCallback(void* userdata, u8* stream, int len)
|
||||
{
|
||||
SDLAudioBackend *backend = (SDLAudioBackend *)userdata;
|
||||
SDLAudioBackend* backend = (SDLAudioBackend*)userdata;
|
||||
DEBUG_LOG(AUDIO, "SDL2: sdl2_record_cb len %d write %zd read %zd", len, (size_t)backend->rec_write, (size_t)backend->rec_read);
|
||||
while (len > 0)
|
||||
{
|
||||
|
@ -192,7 +196,7 @@ public:
|
|||
wav_spec.freq = sampling_freq;
|
||||
wav_spec.format = AUDIO_S16;
|
||||
wav_spec.channels = 1;
|
||||
wav_spec.samples = 256; // Must be power of two
|
||||
wav_spec.samples = 256; // Must be power of two
|
||||
wav_spec.callback = recordCallback;
|
||||
wav_spec.userdata = this;
|
||||
recorddev = SDL_OpenAudioDevice(NULL, 1, &wav_spec, &out_spec, 0);
|
||||
|
@ -227,7 +231,7 @@ public:
|
|||
if (avail == 0)
|
||||
break;
|
||||
avail = std::min(avail, samples);
|
||||
memcpy((u8 *)frame + count, &recordbuf[rec_read], avail);
|
||||
memcpy((u8*)frame + count, &recordbuf[rec_read], avail);
|
||||
rec_read = (rec_read + avail) % sizeof(recordbuf);
|
||||
samples -= avail;
|
||||
count += avail;
|
||||
|
|
|
@ -4,26 +4,30 @@
|
|||
|
||||
static void registerForEvents();
|
||||
|
||||
struct SoundFrame { s16 l; s16 r; };
|
||||
struct SoundFrame
|
||||
{
|
||||
s16 l;
|
||||
s16 r;
|
||||
};
|
||||
|
||||
static SoundFrame Buffer[SAMPLE_COUNT];
|
||||
static u32 writePtr; // next sample index
|
||||
static u32 writePtr; // next sample index
|
||||
|
||||
static AudioBackend *currentBackend;
|
||||
std::vector<AudioBackend *> *AudioBackend::backends;
|
||||
static AudioBackend* currentBackend;
|
||||
std::vector<AudioBackend*>* AudioBackend::backends;
|
||||
|
||||
static bool audio_recording_started;
|
||||
static bool eight_khz;
|
||||
|
||||
AudioBackend *AudioBackend::getBackend(const std::string& slug)
|
||||
AudioBackend* AudioBackend::getBackend(const std::string& slug)
|
||||
{
|
||||
if (backends == nullptr)
|
||||
return nullptr;
|
||||
if (slug == "auto")
|
||||
{
|
||||
// Prefer sdl2 if available and avoid the null driver
|
||||
AudioBackend *sdlBackend = nullptr;
|
||||
AudioBackend *autoBackend = nullptr;
|
||||
AudioBackend* sdlBackend = nullptr;
|
||||
AudioBackend* autoBackend = nullptr;
|
||||
for (auto backend : *backends)
|
||||
{
|
||||
if (backend->slug == "sdl2")
|
||||
|
@ -128,7 +132,7 @@ void StartAudioRecording(bool eight_khz)
|
|||
audio_recording_started = true;
|
||||
}
|
||||
|
||||
u32 RecordAudio(void *buffer, u32 samples)
|
||||
u32 RecordAudio(void* buffer, u32 samples)
|
||||
{
|
||||
if (!audio_recording_started || currentBackend == nullptr)
|
||||
return 0;
|
||||
|
@ -150,11 +154,11 @@ static void registerForEvents()
|
|||
return;
|
||||
done = true;
|
||||
// Empty the audio buffer when loading a state or terminating the game
|
||||
const auto& callback = [](Event, void *) {
|
||||
const auto& callback = [](Event, void*)
|
||||
{
|
||||
writePtr = 0;
|
||||
};
|
||||
EventManager::listen(Event::Terminate, callback);
|
||||
EventManager::listen(Event::LoadState, callback);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,50 +13,59 @@ public:
|
|||
virtual ~AudioBackend() = default;
|
||||
|
||||
virtual bool init() = 0;
|
||||
virtual u32 push(const void *data, u32 frames, bool wait) = 0;
|
||||
virtual u32 push(const void* data, u32 frames, bool wait) = 0;
|
||||
virtual void term() {}
|
||||
|
||||
struct Option {
|
||||
struct Option
|
||||
{
|
||||
std::string name;
|
||||
std::string caption;
|
||||
enum { integer, checkbox, list } type;
|
||||
enum
|
||||
{
|
||||
integer,
|
||||
checkbox,
|
||||
list
|
||||
} type;
|
||||
|
||||
int minValue;
|
||||
int maxValue;
|
||||
std::vector<std::string> values;
|
||||
};
|
||||
virtual const Option *getOptions(int *count) {
|
||||
virtual const Option* getOptions(int* count)
|
||||
{
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool initRecord(u32 sampling_freq) { return false; }
|
||||
virtual u32 record(void *, u32) { return 0; }
|
||||
virtual u32 record(void*, u32) { return 0; }
|
||||
virtual void termRecord() {}
|
||||
|
||||
std::string slug;
|
||||
std::string name;
|
||||
|
||||
static size_t getCount() { return backends == nullptr ? 0 : backends->size(); }
|
||||
static AudioBackend *getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
|
||||
static AudioBackend *getBackend(const std::string& slug);
|
||||
static AudioBackend* getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
|
||||
static AudioBackend* getBackend(const std::string& slug);
|
||||
|
||||
protected:
|
||||
AudioBackend(const std::string& slug, const std::string& name)
|
||||
: slug(slug), name(name) {
|
||||
: slug(slug), name(name)
|
||||
{
|
||||
registerAudioBackend(this);
|
||||
}
|
||||
|
||||
private:
|
||||
static void registerAudioBackend(AudioBackend *backend)
|
||||
static void registerAudioBackend(AudioBackend* backend)
|
||||
{
|
||||
if (backends == nullptr)
|
||||
backends = new std::vector<AudioBackend *>();
|
||||
backends = new std::vector<AudioBackend*>();
|
||||
backends->push_back(backend);
|
||||
std::sort(backends->begin(), backends->end(), [](AudioBackend *b1, AudioBackend *b2) { return b1->slug < b2->slug; });
|
||||
std::sort(backends->begin(), backends->end(), [](AudioBackend* b1, AudioBackend* b2)
|
||||
{ return b1->slug < b2->slug; });
|
||||
}
|
||||
|
||||
static std::vector<AudioBackend *> *backends;
|
||||
static std::vector<AudioBackend*>* backends;
|
||||
};
|
||||
|
||||
void InitAudio();
|
||||
|
@ -64,10 +73,10 @@ void TermAudio();
|
|||
void WriteSample(s16 right, s16 left);
|
||||
|
||||
void StartAudioRecording(bool eight_khz);
|
||||
u32 RecordAudio(void *buffer, u32 samples);
|
||||
u32 RecordAudio(void* buffer, u32 samples);
|
||||
void StopAudioRecording();
|
||||
|
||||
constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with that many frames
|
||||
constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with that many frames
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
|
@ -75,15 +84,17 @@ class RingBuffer
|
|||
std::atomic_int readCursor { 0 };
|
||||
std::atomic_int writeCursor { 0 };
|
||||
|
||||
u32 readSize() {
|
||||
u32 readSize()
|
||||
{
|
||||
return (u32)((writeCursor - readCursor + buffer.size()) % buffer.size());
|
||||
}
|
||||
u32 writeSize() {
|
||||
u32 writeSize()
|
||||
{
|
||||
return (u32)((readCursor - writeCursor + buffer.size() - 1) % buffer.size());
|
||||
}
|
||||
|
||||
public:
|
||||
bool write(const u8 *data, u32 size)
|
||||
bool write(const u8* data, u32 size)
|
||||
{
|
||||
if (size > writeSize())
|
||||
return false;
|
||||
|
@ -102,7 +113,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool read(u8 *data, u32 size)
|
||||
bool read(u8* data, u32 size)
|
||||
{
|
||||
if (size > readSize())
|
||||
return false;
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
#pragma once
|
||||
//#define STRICT_MODE
|
||||
// #define STRICT_MODE
|
||||
#ifndef STRICT_MODE
|
||||
#define FAST_MMU
|
||||
#define USE_WINCE_HACK
|
||||
#endif
|
||||
|
||||
#define DC_PLATFORM_DREAMCAST 0
|
||||
#define DC_PLATFORM_DEV_UNIT 1
|
||||
#define DC_PLATFORM_NAOMI 2
|
||||
#define DC_PLATFORM_NAOMI2 3
|
||||
#define DC_PLATFORM_ATOMISWAVE 4
|
||||
#define DC_PLATFORM_SYSTEMSP 5
|
||||
#define DC_PLATFORM_DREAMCAST 0
|
||||
#define DC_PLATFORM_DEV_UNIT 1
|
||||
#define DC_PLATFORM_NAOMI 2
|
||||
#define DC_PLATFORM_NAOMI2 3
|
||||
#define DC_PLATFORM_ATOMISWAVE 4
|
||||
#define DC_PLATFORM_SYSTEMSP 5
|
||||
|
||||
//HOST_CPU
|
||||
#define CPU_X86 0x20000001
|
||||
#define CPU_ARM 0x20000002
|
||||
#define CPU_ARM64 0x20000003
|
||||
#define CPU_X64 0x20000004
|
||||
// HOST_CPU
|
||||
#define CPU_X86 0x20000001
|
||||
#define CPU_ARM 0x20000002
|
||||
#define CPU_ARM64 0x20000003
|
||||
#define CPU_X64 0x20000004
|
||||
|
||||
//FEAT_SHREC, FEAT_AREC, FEAT_DSPREC
|
||||
#define DYNAREC_NONE 0x40000001
|
||||
#define DYNAREC_JIT 0x40000002
|
||||
// FEAT_SHREC, FEAT_AREC, FEAT_DSPREC
|
||||
#define DYNAREC_NONE 0x40000001
|
||||
#define DYNAREC_JIT 0x40000002
|
||||
|
||||
//automatic
|
||||
// automatic
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define HOST_CPU CPU_X64
|
||||
#define HOST_CPU CPU_X64
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(__arm__) || defined (_M_ARM)
|
||||
#define HOST_CPU CPU_ARM
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define HOST_CPU CPU_ARM64
|
||||
#define HOST_CPU CPU_ARM64
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
@ -48,14 +48,14 @@
|
|||
#endif
|
||||
|
||||
#if defined(TARGET_NO_REC)
|
||||
#define FEAT_SHREC DYNAREC_NONE
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#define FEAT_SHREC DYNAREC_NONE
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_NO_AREC)
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
|
||||
|
@ -63,30 +63,30 @@
|
|||
#define FEAT_NO_RWX_PAGES
|
||||
#endif
|
||||
|
||||
//defaults
|
||||
// defaults
|
||||
|
||||
#ifndef FEAT_SHREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_SHREC DYNAREC_NONE
|
||||
#endif
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_SHREC DYNAREC_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_AREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X64
|
||||
#define FEAT_AREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#endif
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X64
|
||||
#define FEAT_AREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_DSPREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#define FEAT_DSPREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#define FEAT_DSPREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Some restrictions on FEAT_NO_RWX_PAGES
|
||||
|
@ -115,11 +115,11 @@
|
|||
#endif
|
||||
|
||||
// TARGET PLATFORM
|
||||
#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs
|
||||
#define AICA_CORE_CLOCK (GD_CLOCK * 4 / 3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs
|
||||
#define AICA_ARM_CLOCK (AICA_CORE_CLOCK / 2) //[22579200] AICA CORE -> PLL 2:1 -> ARM
|
||||
#define SH4_MAIN_CLOCK (200 * 1000 * 1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200)
|
||||
#define G2_BUS_CLOCK (25 * 1000 * 1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls
|
||||
#define GD_CLOCK 33868800 // GDROM XTAL -- 768fs
|
||||
#define AICA_CORE_CLOCK (GD_CLOCK * 4 / 3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs
|
||||
#define AICA_ARM_CLOCK (AICA_CORE_CLOCK / 2) //[22579200] AICA CORE -> PLL 2:1 -> ARM
|
||||
#define SH4_MAIN_CLOCK (200 * 1000 * 1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200)
|
||||
#define G2_BUS_CLOCK (25 * 1000 * 1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls
|
||||
|
||||
#if defined(GLES) && !defined(GLES3) && !defined(GLES2)
|
||||
// Only use GL ES 2.0 API functions
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// serialize.cpp : save states
|
||||
#include "serialize.h"
|
||||
#include "types.h"
|
||||
#include "achievements/achievements.h"
|
||||
#include "cfg/option.h"
|
||||
#include "hw/aica/aica_if.h"
|
||||
#include "hw/holly/sb.h"
|
||||
#include "hw/bba/bba.h"
|
||||
#include "hw/flashrom/nvmem.h"
|
||||
#include "hw/gdrom/gdrom_if.h"
|
||||
#include "hw/holly/sb.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/modem/modem.h"
|
||||
#include "hw/pvr/pvr.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "hw/sh4/sh4_mmr.h"
|
||||
#include "reios/reios.h"
|
||||
#include "hw/naomi/naomi.h"
|
||||
#include "hw/naomi/naomi_cart.h"
|
||||
#include "hw/bba/bba.h"
|
||||
#include "cfg/option.h"
|
||||
#include "hw/pvr/pvr.h"
|
||||
#include "hw/sh4/sh4_mmr.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "imgread/common.h"
|
||||
#include "achievements/achievements.h"
|
||||
#include "reios/reios.h"
|
||||
#include "types.h"
|
||||
|
||||
void dc_serialize(Serializer& ser)
|
||||
{
|
||||
|
@ -100,18 +100,18 @@ void dc_deserialize(Deserializer& deser)
|
|||
DEBUG_LOG(SAVESTATE, "Loaded %d bytes", (u32)deser.size());
|
||||
}
|
||||
|
||||
Deserializer::Deserializer(const void *data, size_t limit, bool rollback)
|
||||
: SerializeBase(limit, rollback), data((const u8 *)data)
|
||||
Deserializer::Deserializer(const void* data, size_t limit, bool rollback)
|
||||
: SerializeBase(limit, rollback), data((const u8*)data)
|
||||
{
|
||||
if (!memcmp(data, "RASTATE\001", 8))
|
||||
{
|
||||
// RetroArch savestates now have several sections: MEM, ACHV, RPLY, etc.
|
||||
const u8 *p = this->data + 8;
|
||||
const u8* p = this->data + 8;
|
||||
limit -= 8;
|
||||
while (limit > 8)
|
||||
{
|
||||
const u8 *section = p;
|
||||
u32 sectionSize = *(const u32 *)&p[4];
|
||||
const u8* section = p;
|
||||
u32 sectionSize = *(const u32*)&p[4];
|
||||
p += 8;
|
||||
limit -= 8;
|
||||
if (!memcmp(section, "MEM ", 4))
|
||||
|
@ -121,8 +121,9 @@ Deserializer::Deserializer(const void *data, size_t limit, bool rollback)
|
|||
this->limit = sectionSize;
|
||||
break;
|
||||
}
|
||||
sectionSize = (sectionSize + 7) & ~7; // align to 8 bytes
|
||||
if (limit < sectionSize) {
|
||||
sectionSize = (sectionSize + 7) & ~7; // align to 8 bytes
|
||||
if (limit < sectionSize)
|
||||
{
|
||||
limit = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -138,7 +139,7 @@ Deserializer::Deserializer(const void *data, size_t limit, bool rollback)
|
|||
if (_version > Current)
|
||||
throw Exception("Version too recent");
|
||||
|
||||
if(_version >= V42 && settings.platform.isConsole())
|
||||
if (_version >= V42 && settings.platform.isConsole())
|
||||
{
|
||||
u32 ramSize;
|
||||
deserialize(ramSize);
|
||||
|
@ -147,8 +148,8 @@ Deserializer::Deserializer(const void *data, size_t limit, bool rollback)
|
|||
}
|
||||
}
|
||||
|
||||
Serializer::Serializer(void *data, size_t limit, bool rollback)
|
||||
: SerializeBase(limit, rollback), data((u8 *)data)
|
||||
Serializer::Serializer(void* data, size_t limit, bool rollback)
|
||||
: SerializeBase(limit, rollback), data((u8*)data)
|
||||
{
|
||||
Version v = Current;
|
||||
serialize(v);
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
@ -25,7 +25,8 @@
|
|||
class SerializeBase
|
||||
{
|
||||
public:
|
||||
enum Version : int32_t {
|
||||
enum Version : int32_t
|
||||
{
|
||||
V16 = 811,
|
||||
V17,
|
||||
V18,
|
||||
|
@ -88,10 +89,10 @@ public:
|
|||
class Exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
Exception(const char *msg) : std::runtime_error(msg) {}
|
||||
Exception(const char* msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
Deserializer(const void *data, size_t limit, bool rollback = false);
|
||||
Deserializer(const void* data, size_t limit, bool rollback = false);
|
||||
|
||||
template<typename T>
|
||||
void deserialize(T& obj)
|
||||
|
@ -99,7 +100,7 @@ public:
|
|||
doDeserialize(&obj, sizeof(T));
|
||||
}
|
||||
template<typename T>
|
||||
void deserialize(T *obj, size_t count)
|
||||
void deserialize(T* obj, size_t count)
|
||||
{
|
||||
doDeserialize(obj, sizeof(T) * count);
|
||||
}
|
||||
|
@ -125,9 +126,9 @@ public:
|
|||
Version version() const { return _version; }
|
||||
|
||||
private:
|
||||
void doDeserialize(void *dest, size_t size)
|
||||
void doDeserialize(void* dest, size_t size)
|
||||
{
|
||||
if (this->_size + size > limit) // FIXME one more test vs. current
|
||||
if (this->_size + size > limit) // FIXME one more test vs. current
|
||||
{
|
||||
WARN_LOG(SAVESTATE, "Savestate overflow: current %d limit %d sz %d", (int)this->_size, (int)limit, (int)size);
|
||||
throw Exception("Invalid savestate");
|
||||
|
@ -138,7 +139,7 @@ private:
|
|||
}
|
||||
|
||||
Version _version;
|
||||
const u8 *data;
|
||||
const u8* data;
|
||||
};
|
||||
|
||||
class Serializer : public SerializeBase
|
||||
|
@ -147,7 +148,7 @@ public:
|
|||
Serializer()
|
||||
: Serializer(nullptr, std::numeric_limits<size_t>::max(), false) {}
|
||||
|
||||
Serializer(void *data, size_t limit, bool rollback = false);
|
||||
Serializer(void* data, size_t limit, bool rollback = false);
|
||||
|
||||
template<typename T>
|
||||
void serialize(const T& obj)
|
||||
|
@ -155,7 +156,7 @@ public:
|
|||
doSerialize(&obj, sizeof(T));
|
||||
}
|
||||
template<typename T>
|
||||
void serialize(const T *obj, size_t count)
|
||||
void serialize(const T* obj, size_t count)
|
||||
{
|
||||
doSerialize(obj, sizeof(T) * count);
|
||||
}
|
||||
|
@ -174,7 +175,7 @@ public:
|
|||
bool dryrun() const { return data == nullptr; }
|
||||
|
||||
private:
|
||||
void doSerialize(const void *src, size_t size)
|
||||
void doSerialize(const void* src, size_t size)
|
||||
{
|
||||
if (data != nullptr)
|
||||
{
|
||||
|
@ -184,17 +185,19 @@ private:
|
|||
this->_size += size;
|
||||
}
|
||||
|
||||
u8 *data;
|
||||
u8* data;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Serializer& operator<<(Serializer& ctx, const T& obj) {
|
||||
Serializer& operator<<(Serializer& ctx, const T& obj)
|
||||
{
|
||||
ctx.serialize(obj);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer& operator>>(Deserializer& ctx, T& obj) {
|
||||
Deserializer& operator>>(Deserializer& ctx, T& obj)
|
||||
{
|
||||
ctx.deserialize(obj);
|
||||
return ctx;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "types.h"
|
||||
#include "stdclass.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "serialize.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "serialize.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
@ -12,7 +12,7 @@
|
|||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <algorithm>
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
static std::string user_config_dir;
|
||||
|
@ -93,12 +93,16 @@ std::string get_readonly_data_path(const std::string& filename)
|
|||
return filepath;
|
||||
}
|
||||
// Try the game directory
|
||||
try {
|
||||
try
|
||||
{
|
||||
std::string parent = hostfs::storage().getParentPath(settings.content.path);
|
||||
std::string filepath = hostfs::storage().getSubPath(parent, filename);
|
||||
if (hostfs::storage().exists(filepath))
|
||||
return filepath;
|
||||
} catch (const FlycastException&) { }
|
||||
}
|
||||
catch (const FlycastException&)
|
||||
{
|
||||
}
|
||||
|
||||
// Not found, so we return the user variant
|
||||
return user_filepath;
|
||||
|
@ -130,10 +134,10 @@ bool make_directory(const std::string& path)
|
|||
void cThread::Start()
|
||||
{
|
||||
verify(!thread.joinable());
|
||||
thread = std::thread([this]() {
|
||||
thread = std::thread([this]()
|
||||
{
|
||||
ThreadName _(name);
|
||||
entry(param);
|
||||
});
|
||||
entry(param); });
|
||||
}
|
||||
|
||||
void cThread::WaitToEnd()
|
||||
|
@ -144,24 +148,23 @@ void cThread::WaitToEnd()
|
|||
|
||||
cResetEvent::cResetEvent() : state(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cResetEvent::~cResetEvent() = default;
|
||||
|
||||
void cResetEvent::Set()//Signal
|
||||
void cResetEvent::Set() // Signal
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutx);
|
||||
std::lock_guard<std::mutex> lock(mutx);
|
||||
|
||||
state = true;
|
||||
cond.notify_one();
|
||||
state = true;
|
||||
cond.notify_one();
|
||||
}
|
||||
|
||||
void cResetEvent::Reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutx);
|
||||
std::lock_guard<std::mutex> lock(mutx);
|
||||
|
||||
state = false;
|
||||
state = false;
|
||||
}
|
||||
|
||||
bool cResetEvent::Wait(u32 msec)
|
||||
|
@ -172,35 +175,37 @@ bool cResetEvent::Wait(u32 msec)
|
|||
cond.wait_for(lock, std::chrono::milliseconds(msec));
|
||||
|
||||
bool rc = state;
|
||||
state = false;
|
||||
state = false;
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void cResetEvent::Wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutx);
|
||||
std::unique_lock<std::mutex> lock(mutx);
|
||||
|
||||
if (!state) {
|
||||
cond.wait(lock);
|
||||
}
|
||||
if (!state)
|
||||
{
|
||||
cond.wait(lock);
|
||||
}
|
||||
|
||||
state = false;
|
||||
state = false;
|
||||
}
|
||||
|
||||
void RamRegion::serialize(Serializer &ser) const {
|
||||
void RamRegion::serialize(Serializer& ser) const
|
||||
{
|
||||
ser.serialize(data, size);
|
||||
}
|
||||
|
||||
void RamRegion::deserialize(Deserializer &deser) {
|
||||
void RamRegion::deserialize(Deserializer& deser)
|
||||
{
|
||||
deser.deserialize(data, size);
|
||||
}
|
||||
|
||||
|
||||
void RamRegion::alloc(size_t size)
|
||||
{
|
||||
this->size = size;
|
||||
data = (u8 *)allocAligned(PAGE_SIZE, size);
|
||||
data = (u8*)allocAligned(PAGE_SIZE, size);
|
||||
ownsMemory = true;
|
||||
}
|
||||
|
||||
|
@ -222,7 +227,7 @@ u64 getTimeMs()
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static struct tm *localtime_r(const time_t *_clock, struct tm *_result)
|
||||
static struct tm* localtime_r(const time_t* _clock, struct tm* _result)
|
||||
{
|
||||
return localtime_s(_result, _clock) ? nullptr : _result;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
#include "md5/md5.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/mman.h>
|
||||
|
@ -22,7 +22,7 @@
|
|||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
#ifndef PAGE_MASK
|
||||
#define PAGE_MASK (PAGE_SIZE-1)
|
||||
#define PAGE_MASK (PAGE_SIZE - 1)
|
||||
#endif
|
||||
|
||||
class cThread
|
||||
|
@ -31,13 +31,13 @@ private:
|
|||
typedef void* ThreadEntryFP(void* param);
|
||||
ThreadEntryFP* entry;
|
||||
void* param;
|
||||
const char *name;
|
||||
const char* name;
|
||||
|
||||
public:
|
||||
std::thread thread;
|
||||
|
||||
cThread(ThreadEntryFP* function, void* param, const char *name)
|
||||
:entry(function), param(param), name(name) {}
|
||||
cThread(ThreadEntryFP* function, void* param, const char* name)
|
||||
: entry(function), param(param), name(name) {}
|
||||
~cThread() { WaitToEnd(); }
|
||||
void Start();
|
||||
void WaitToEnd();
|
||||
|
@ -50,13 +50,13 @@ private:
|
|||
std::condition_variable cond;
|
||||
bool state;
|
||||
|
||||
public :
|
||||
public:
|
||||
cResetEvent();
|
||||
~cResetEvent();
|
||||
void Set(); //Set state to signaled
|
||||
void Reset(); //Set state to non signaled
|
||||
bool Wait(u32 msec);//Wait for signal , then reset[if auto]. Returns false if timed out
|
||||
void Wait(); //Wait for signal , then reset[if auto]
|
||||
void Set(); // Set state to signaled
|
||||
void Reset(); // Set state to non signaled
|
||||
bool Wait(u32 msec); // Wait for signal , then reset[if auto]. Returns false if timed out
|
||||
void Wait(); // Wait for signal , then reset[if auto]
|
||||
};
|
||||
|
||||
void set_user_config_dir(const std::string& dir);
|
||||
|
@ -78,7 +78,7 @@ size_t get_last_slash_pos(const std::string& path);
|
|||
|
||||
class RamRegion
|
||||
{
|
||||
u8 *data;
|
||||
u8* data;
|
||||
size_t size;
|
||||
bool ownsMemory = false;
|
||||
|
||||
|
@ -86,33 +86,37 @@ public:
|
|||
void alloc(size_t size);
|
||||
void free();
|
||||
|
||||
void setRegion(u8 *data, size_t size)
|
||||
void setRegion(u8* data, size_t size)
|
||||
{
|
||||
this->data = data;
|
||||
this->size = size;
|
||||
ownsMemory = false;
|
||||
}
|
||||
|
||||
void zero() {
|
||||
void zero()
|
||||
{
|
||||
std::memset(data, 0, size);
|
||||
}
|
||||
|
||||
u8& operator [](size_t i) {
|
||||
u8& operator[](size_t i)
|
||||
{
|
||||
return data[i];
|
||||
}
|
||||
}
|
||||
|
||||
void serialize(Serializer &ser) const;
|
||||
void deserialize(Deserializer &deser);
|
||||
void serialize(Serializer& ser) const;
|
||||
void deserialize(Deserializer& deser);
|
||||
};
|
||||
|
||||
static inline void string_tolower(std::string& s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c)
|
||||
{ return std::tolower(c); });
|
||||
}
|
||||
|
||||
static inline void string_toupper(std::string& s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::toupper(c); });
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c)
|
||||
{ return std::toupper(c); });
|
||||
}
|
||||
|
||||
static inline std::string get_file_extension(const std::string& s)
|
||||
|
@ -136,23 +140,23 @@ static inline std::string get_file_basename(const std::string& s)
|
|||
extern const std::string defaultWs;
|
||||
|
||||
static inline std::string trim_trailing_ws(const std::string& str,
|
||||
const std::string& whitespace = defaultWs)
|
||||
const std::string& whitespace = defaultWs)
|
||||
{
|
||||
const auto strEnd = str.find_last_not_of(whitespace);
|
||||
const auto strEnd = str.find_last_not_of(whitespace);
|
||||
if (strEnd == std::string::npos)
|
||||
return "";
|
||||
|
||||
return str.substr(0, strEnd + 1);
|
||||
return str.substr(0, strEnd + 1);
|
||||
}
|
||||
|
||||
static inline std::string trim_ws(const std::string& str,
|
||||
const std::string& whitespace = defaultWs)
|
||||
const std::string& whitespace = defaultWs)
|
||||
{
|
||||
const auto strStart = str.find_first_not_of(whitespace);
|
||||
const auto strStart = str.find_first_not_of(whitespace);
|
||||
if (strStart == std::string::npos)
|
||||
return "";
|
||||
|
||||
return str.substr(strStart, str.find_last_not_of(whitespace) + 1 - strStart);
|
||||
return str.substr(strStart, str.find_last_not_of(whitespace) + 1 - strStart);
|
||||
}
|
||||
|
||||
class MD5Sum
|
||||
|
@ -160,16 +164,19 @@ class MD5Sum
|
|||
MD5_CTX ctx;
|
||||
|
||||
public:
|
||||
MD5Sum() {
|
||||
MD5Sum()
|
||||
{
|
||||
MD5_Init(&ctx);
|
||||
}
|
||||
|
||||
MD5Sum& add(const void *data, unsigned long len) {
|
||||
MD5Sum& add(const void* data, unsigned long len)
|
||||
{
|
||||
MD5_Update(&ctx, data, len);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MD5Sum& add(std::FILE *file) {
|
||||
MD5Sum& add(std::FILE* file)
|
||||
{
|
||||
std::fseek(file, 0, SEEK_SET);
|
||||
char buf[4096];
|
||||
unsigned long len = 0;
|
||||
|
@ -179,22 +186,26 @@ public:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
MD5Sum& add(const T& v) {
|
||||
MD5Sum& add(const T& v)
|
||||
{
|
||||
MD5_Update(&ctx, &v, (unsigned long)sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MD5Sum& add(const std::vector<T>& v) {
|
||||
MD5Sum& add(const std::vector<T>& v)
|
||||
{
|
||||
MD5_Update(&ctx, v.data(), (unsigned long)(v.size() * sizeof(T)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void getDigest(u8 digest[16]) {
|
||||
void getDigest(u8 digest[16])
|
||||
{
|
||||
MD5_Final(digest, &ctx);
|
||||
}
|
||||
|
||||
std::vector<u8> getDigest() {
|
||||
std::vector<u8> getDigest()
|
||||
{
|
||||
std::vector<u8> v(16);
|
||||
MD5_Final(v.data(), &ctx);
|
||||
return v;
|
||||
|
@ -207,15 +218,18 @@ std::string timeToISO8601(time_t time);
|
|||
class ThreadRunner
|
||||
{
|
||||
public:
|
||||
void init() {
|
||||
void init()
|
||||
{
|
||||
threadId = std::this_thread::get_id();
|
||||
}
|
||||
void runOnThread(std::function<void()> func)
|
||||
{
|
||||
if (threadId == std::this_thread::get_id()) {
|
||||
if (threadId == std::this_thread::get_id())
|
||||
{
|
||||
func();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LockGuard _(mutex);
|
||||
tasks.push_back(func);
|
||||
}
|
||||
|
|
110
core/types.h
110
core/types.h
|
@ -2,32 +2,32 @@
|
|||
#include "build.h"
|
||||
|
||||
#if HOST_CPU == CPU_X86
|
||||
#ifdef _MSC_VER
|
||||
#define DYNACALL __fastcall
|
||||
#else
|
||||
//android defines fastcall as regparm(3), it doesn't work for us
|
||||
#undef fastcall
|
||||
#define DYNACALL __attribute__((fastcall))
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#define DYNACALL __fastcall
|
||||
#else
|
||||
#define DYNACALL
|
||||
// android defines fastcall as regparm(3), it doesn't work for us
|
||||
#undef fastcall
|
||||
#define DYNACALL __attribute__((fastcall))
|
||||
#endif
|
||||
#else
|
||||
#define DYNACALL
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
// conversion from 't1' to 't2', possible loss of data
|
||||
#pragma warning(disable: 4267)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable : 4267)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
//basic types
|
||||
typedef int8_t s8;
|
||||
// basic types
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
@ -36,8 +36,8 @@ typedef float f32;
|
|||
typedef double f64;
|
||||
|
||||
#ifndef TARGET_UWP
|
||||
#include "nowide/cstdlib.hpp"
|
||||
#include "nowide/cstdio.hpp"
|
||||
#include "nowide/cstdlib.hpp"
|
||||
#else
|
||||
#include "nowide/config.hpp"
|
||||
#include "nowide/convert.hpp"
|
||||
|
@ -45,40 +45,43 @@ typedef double f64;
|
|||
#include "nowide/stackstring.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
namespace nowide {
|
||||
FILE *fopen(char const *file_name, char const *mode);
|
||||
int remove(const char *pathname);
|
||||
namespace nowide
|
||||
{
|
||||
FILE* fopen(char const* file_name, char const* mode);
|
||||
int remove(const char* pathname);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
int darw_printf(const char* Text,...);
|
||||
int darw_printf(const char* Text, ...);
|
||||
#endif
|
||||
|
||||
#ifndef TARGET_IPHONE
|
||||
#if defined(TARGET_ARM_MAC)
|
||||
#include <pthread.h>
|
||||
inline static void JITWriteProtect(bool enabled) {
|
||||
inline static void JITWriteProtect(bool enabled)
|
||||
{
|
||||
if (__builtin_available(macOS 11.0, *))
|
||||
pthread_jit_write_protect_np(enabled);
|
||||
}
|
||||
#else
|
||||
inline static void JITWriteProtect(bool enabled) {
|
||||
inline static void JITWriteProtect(bool enabled)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//includes from c++rt
|
||||
#include <string>
|
||||
// includes from c++rt
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define likely(x) x
|
||||
#define unlikely(x) x
|
||||
#define likely(x) x
|
||||
#define unlikely(x) x
|
||||
#define expected(x, y) x
|
||||
#else
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define expected(x, y) __builtin_expect((x), (y))
|
||||
#endif
|
||||
|
||||
|
@ -91,10 +94,24 @@ inline static void JITWriteProtect(bool enabled) {
|
|||
[[noreturn]] void os_DebugBreak();
|
||||
void fatal_error(const char* text, ...);
|
||||
|
||||
#define verify(x) do { if ((x) == false){ fatal_error("Verify Failed : " #x "\n in %s -> %s : %d", (__FUNCTION__), (__FILE__), __LINE__); os_DebugBreak();}} while (false)
|
||||
#define die(reason) do { fatal_error("Fatal error : %s\n in %s -> %s : %d", (reason), (__FUNCTION__), (__FILE__), __LINE__); os_DebugBreak();} while (false)
|
||||
#define verify(x) \
|
||||
do \
|
||||
{ \
|
||||
if ((x) == false) \
|
||||
{ \
|
||||
fatal_error("Verify Failed : " #x "\n in %s -> %s : %d", (__FUNCTION__), (__FILE__), __LINE__); \
|
||||
os_DebugBreak(); \
|
||||
} \
|
||||
} while (false)
|
||||
#define die(reason) \
|
||||
do \
|
||||
{ \
|
||||
fatal_error("Fatal error : %s\n in %s -> %s : %d", (reason), (__FUNCTION__), (__FILE__), __LINE__); \
|
||||
os_DebugBreak(); \
|
||||
} while (false)
|
||||
|
||||
enum class RenderType {
|
||||
enum class RenderType
|
||||
{
|
||||
OpenGL = 0,
|
||||
OpenGL_OIT = 3,
|
||||
Vulkan = 4,
|
||||
|
@ -104,17 +121,21 @@ enum class RenderType {
|
|||
DirectX11_OIT = 6,
|
||||
};
|
||||
|
||||
static inline bool isOpenGL(RenderType renderType) {
|
||||
static inline bool isOpenGL(RenderType renderType)
|
||||
{
|
||||
return renderType == RenderType::OpenGL || renderType == RenderType::OpenGL_OIT;
|
||||
}
|
||||
static inline bool isVulkan(RenderType renderType) {
|
||||
static inline bool isVulkan(RenderType renderType)
|
||||
{
|
||||
return renderType == RenderType::Vulkan || renderType == RenderType::Vulkan_OIT;
|
||||
}
|
||||
static inline bool isDirectX(RenderType renderType) {
|
||||
static inline bool isDirectX(RenderType renderType)
|
||||
{
|
||||
return renderType == RenderType::DirectX9 || renderType == RenderType::DirectX11 || renderType == RenderType::DirectX11_OIT;
|
||||
}
|
||||
|
||||
enum class KeyboardLayout {
|
||||
enum class KeyboardLayout
|
||||
{
|
||||
JP = 1,
|
||||
US,
|
||||
UK,
|
||||
|
@ -135,7 +156,8 @@ enum class KeyboardLayout {
|
|||
|
||||
struct settings_t
|
||||
{
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
int system;
|
||||
u32 ram_size;
|
||||
u32 ram_mask;
|
||||
|
@ -155,7 +177,8 @@ struct settings_t
|
|||
bool isSystemSP() const { return system == DC_PLATFORM_SYSTEMSP; }
|
||||
} platform;
|
||||
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
int width = 640;
|
||||
int height = 480;
|
||||
float pointScale = 1.f;
|
||||
|
@ -177,13 +200,14 @@ struct settings_t
|
|||
std::string title;
|
||||
} content;
|
||||
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
KeyboardLayout keyboardLangId = KeyboardLayout::US;
|
||||
bool fastForwardMode;
|
||||
// The following flags are only set for arcade games
|
||||
bool lightgunGame; // or touchscreen
|
||||
bool keyboardGame;
|
||||
bool mouseGame; // or rotary encoders
|
||||
bool mouseGame; // or rotary encoders
|
||||
bool fourPlayerGames;
|
||||
} input;
|
||||
|
||||
|
@ -214,8 +238,8 @@ struct settings_t
|
|||
|
||||
extern settings_t settings;
|
||||
|
||||
#define RAM_SIZE settings.platform.ram_size
|
||||
#define RAM_MASK settings.platform.ram_mask
|
||||
#define RAM_SIZE settings.platform.ram_size
|
||||
#define RAM_MASK settings.platform.ram_mask
|
||||
#define ARAM_SIZE settings.platform.aram_size
|
||||
#define ARAM_MASK settings.platform.aram_mask
|
||||
#define VRAM_SIZE settings.platform.vram_size
|
||||
|
@ -243,17 +267,17 @@ public:
|
|||
class Serializer;
|
||||
class Deserializer;
|
||||
|
||||
constexpr size_t operator""_KB(unsigned long long x)
|
||||
constexpr size_t operator""_KB(unsigned long long x)
|
||||
{
|
||||
return 1024 * x;
|
||||
}
|
||||
|
||||
constexpr size_t operator""_MB(unsigned long long x)
|
||||
constexpr size_t operator""_MB(unsigned long long x)
|
||||
{
|
||||
return 1024 * 1024 * x;
|
||||
}
|
||||
|
||||
constexpr size_t operator""_GB(unsigned long long x)
|
||||
constexpr size_t operator""_GB(unsigned long long x)
|
||||
{
|
||||
return 1024 * 1024 * 1024 * x;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "types.h"
|
||||
|
||||
#if FEAT_AREC != DYNAREC_NONE
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "hw/arm7/arm7.h"
|
||||
#include "hw/aica/aica_if.h"
|
||||
#include "hw/arm7/arm7_rec.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/aica/aica_if.h"
|
||||
#include "hw/arm7/arm7.h"
|
||||
#include "hw/arm7/arm7_rec.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
@ -15,12 +15,13 @@ static const u32 C_FLAG = 1 << 29;
|
|||
static const u32 V_FLAG = 1 << 28;
|
||||
static const u32 NZCV_MASK = N_FLAG | Z_FLAG | C_FLAG | V_FLAG;
|
||||
|
||||
|
||||
namespace aica::arm::recompiler {
|
||||
namespace aica::arm::recompiler
|
||||
{
|
||||
|
||||
extern void (*EntryPoints[])();
|
||||
|
||||
class AicaArmTest : public ::testing::Test {
|
||||
class AicaArmTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
@ -36,12 +37,12 @@ protected:
|
|||
PrepareOps(1, &op);
|
||||
}
|
||||
|
||||
void PrepareOps(int count, u32 *ops)
|
||||
void PrepareOps(int count, u32* ops)
|
||||
{
|
||||
arm_Reg[R15_ARM_NEXT].I = 0x1000;
|
||||
for (int i = 0; i < count; i++)
|
||||
*(u32*)&aica_ram[0x1000 + i * 4] = ops[i];
|
||||
*(u32*)&aica_ram[0x1000 + count * 4] = 0xea000000 | ((u32)(-count * 4 - 8) << 2); // b pc+8-12
|
||||
*(u32*)&aica_ram[0x1000 + count * 4] = 0xea000000 | ((u32)(-count * 4 - 8) << 2); // b pc+8-12
|
||||
flush();
|
||||
compile();
|
||||
}
|
||||
|
@ -57,11 +58,11 @@ protected:
|
|||
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
|
||||
}
|
||||
};
|
||||
#define ASSERT_NZCV_EQ(expected) ASSERT_EQ(arm_Reg[RN_PSR_FLAGS].I & NZCV_MASK, (expected));
|
||||
#define ASSERT_NZCV_EQ(expected) ASSERT_EQ(arm_Reg[RN_PSR_FLAGS].I& NZCV_MASK, (expected));
|
||||
|
||||
TEST_F(AicaArmTest, ArithmeticOpsTest)
|
||||
{
|
||||
PrepareOp(0xe0810002); // add r0, r1, r2
|
||||
PrepareOp(0xe0810002); // add r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xbaad0000;
|
||||
arm_Reg[2].I = 0x0000cafe;
|
||||
|
@ -70,13 +71,13 @@ TEST_F(AicaArmTest, ArithmeticOpsTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
ASSERT_NZCV_EQ(NZCV_MASK);
|
||||
|
||||
PrepareOp(0xe0810000); // add r0, r1, r0
|
||||
PrepareOp(0xe0810000); // add r0, r1, r0
|
||||
arm_Reg[0].I = 11;
|
||||
arm_Reg[1].I = 22;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 33);
|
||||
|
||||
PrepareOp(0xe0410002); // sub r0, r1, r2
|
||||
PrepareOp(0xe0410002); // sub r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xbaadcafe;
|
||||
arm_Reg[2].I = 0x0000cafe;
|
||||
|
@ -84,85 +85,85 @@ TEST_F(AicaArmTest, ArithmeticOpsTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0xbaad0000);
|
||||
ASSERT_NZCV_EQ(NZCV_MASK);
|
||||
|
||||
PrepareOp(0xe0410000); // sub r0, r1, r0
|
||||
PrepareOp(0xe0410000); // sub r0, r1, r0
|
||||
arm_Reg[0].I = 11;
|
||||
arm_Reg[1].I = 22;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 11);
|
||||
|
||||
PrepareOp(0xe0910002); // adds r0, r1, r2
|
||||
PrepareOp(0xe0910002); // adds r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x80000000;
|
||||
arm_Reg[2].I = 0x80000000;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
|
||||
|
||||
PrepareOp(0xe0510002); // subs r0, r1, r2
|
||||
PrepareOp(0xe0510002); // subs r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xbaadcafe;
|
||||
arm_Reg[2].I = 0x0000cafe;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xbaad0000);
|
||||
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
|
||||
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xbaadcafe;
|
||||
arm_Reg[2].I = 0xbaadcafe;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x80000000;
|
||||
arm_Reg[2].I = 1;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x7fffffff);
|
||||
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
|
||||
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xffffffff;
|
||||
arm_Reg[2].I = 0xffffffff;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
|
||||
PrepareOp(0xe0b10002); // adcs r0, r1, r2
|
||||
PrepareOp(0xe0b10002); // adcs r0, r1, r2
|
||||
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 3);
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[0].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 4);
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
|
||||
// from armwrestler
|
||||
PrepareOp(0xe0d22002); // sbcs r2,r2,r2
|
||||
PrepareOp(0xe0d22002); // sbcs r2,r2,r2
|
||||
ResetNZCV();
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[2].I = 0xFFFFFFFF;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[2].I, 0);
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
|
||||
|
||||
// from armwrestler
|
||||
PrepareOp(0xe2d22000); // sbcs r2,r2,#0
|
||||
PrepareOp(0xe2d22000); // sbcs r2,r2,#0
|
||||
ResetNZCV();
|
||||
arm_Reg[2].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[2].I, -1);
|
||||
ASSERT_NZCV_EQ(N_FLAG);
|
||||
|
||||
PrepareOp(0xe0d10002); // sbcs r0, r1, r2
|
||||
PrepareOp(0xe0d10002); // sbcs r0, r1, r2
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, -2);
|
||||
ASSERT_NZCV_EQ(N_FLAG); // N
|
||||
ASSERT_NZCV_EQ(N_FLAG); // N
|
||||
|
||||
PrepareOp(0xe0710002); // rsbs r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
|
@ -170,7 +171,7 @@ TEST_F(AicaArmTest, ArithmeticOpsTest)
|
|||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C, confirmed by interpreter and online arm sim
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C, confirmed by interpreter and online arm sim
|
||||
|
||||
PrepareOp(0xe0f10002); // rscs r0, r1, r2
|
||||
ResetNZCV();
|
||||
|
@ -179,58 +180,58 @@ TEST_F(AicaArmTest, ArithmeticOpsTest)
|
|||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // C set
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
|
||||
PrepareOp(0xe1500001); // cmp r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 2;
|
||||
arm_Reg[1].I = 1;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
|
||||
PrepareOp(0xe1700001); // cmn r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 2;
|
||||
arm_Reg[1].I = -1;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
}
|
||||
|
||||
TEST_F(AicaArmTest, LogicOpsTest)
|
||||
{
|
||||
PrepareOp(0xe1a00001); // mov r0, r1
|
||||
PrepareOp(0xe1a00001); // mov r0, r1
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xbaadcafe;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
|
||||
PrepareOp(0xe3c100ff); // bic r0, r1, 0xff
|
||||
PrepareOp(0xe3c100ff); // bic r0, r1, 0xff
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xffff;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xff00);
|
||||
|
||||
PrepareOp(0xe0010002); // and r0, r1, r2
|
||||
PrepareOp(0xe0010002); // and r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xff0f;
|
||||
arm_Reg[2].I = 0xf0f0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xf000);
|
||||
|
||||
PrepareOp(0xe0010000); // and r0, r1, r0
|
||||
PrepareOp(0xe0010000); // and r0, r1, r0
|
||||
arm_Reg[0].I = 0xf0f0f0f0;
|
||||
arm_Reg[1].I = 0xffffffff;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xf0f0f0f0);
|
||||
|
||||
PrepareOp(0xe1a00251); // asr r0, r1, r2
|
||||
PrepareOp(0xe1a00251); // asr r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xfffffff8;
|
||||
arm_Reg[2].I = 2;
|
||||
|
@ -243,149 +244,149 @@ TEST_F(AicaArmTest, LogicOpsTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x00ffffff);
|
||||
|
||||
PrepareOp(0xe0210002); // eor r0, r1, r2
|
||||
PrepareOp(0xe0210002); // eor r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0xffff0000;
|
||||
arm_Reg[2].I = 0xf0f0f0f0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x0f0ff0f0);
|
||||
|
||||
PrepareOp(0xe0210000); // eor r0, r1, r0
|
||||
PrepareOp(0xe0210000); // eor r0, r1, r0
|
||||
arm_Reg[0].I = 0xf0f0f0f0;
|
||||
arm_Reg[1].I = 0xffffffff;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x0f0f0f0f);
|
||||
|
||||
PrepareOp(0xe1810000); // orr r0, r1, r0
|
||||
PrepareOp(0xe1810000); // orr r0, r1, r0
|
||||
arm_Reg[0].I = 0xf0f0f0f0;
|
||||
arm_Reg[1].I = 0x0f0f0f0f;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
|
||||
|
||||
PrepareOp(0xe1a00211); // lsl r0, r1, r2
|
||||
PrepareOp(0xe1a00211); // lsl r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 8;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x100);
|
||||
|
||||
PrepareOp(0xe1a00231); // lsr r0, r1, r2
|
||||
PrepareOp(0xe1a00231); // lsr r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x100;
|
||||
arm_Reg[2].I = 4;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x10);
|
||||
|
||||
PrepareOp(0xe1a00271); // ror r0, r1, r2
|
||||
PrepareOp(0xe1a00271); // ror r0, r1, r2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x12345678;
|
||||
arm_Reg[2].I = 16;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x56781234);
|
||||
|
||||
PrepareOp(0xe1300001); // teq r0, r1
|
||||
PrepareOp(0xe1300001); // teq r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(0);
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
|
||||
arm_Reg[0].I = 1;
|
||||
arm_Reg[1].I = 1;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
|
||||
|
||||
PrepareOp(0xe1100001); // tst r0, r1
|
||||
PrepareOp(0xe1100001); // tst r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 1;
|
||||
arm_Reg[1].I = 2;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(0x40000000); // Z
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
|
||||
ASSERT_NZCV_EQ(0x40000000); // Z
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
|
||||
arm_Reg[0].I = 3;
|
||||
arm_Reg[1].I = 2;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
|
||||
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
|
||||
}
|
||||
|
||||
TEST_F(AicaArmTest, Operand2ImmTest)
|
||||
{
|
||||
PrepareOp(0xe2810003); // add r0, r1, #3
|
||||
PrepareOp(0xe2810003); // add r0, r1, #3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 4);
|
||||
|
||||
PrepareOp(0xe2a10003); // adc r0, r1, #3
|
||||
PrepareOp(0xe2a10003); // adc r0, r1, #3
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 5);
|
||||
|
||||
PrepareOp(0xe2410003); // sub r0, r1, #3
|
||||
PrepareOp(0xe2410003); // sub r0, r1, #3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 7;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 4);
|
||||
|
||||
PrepareOp(0xe2610003); // rsb r0, r1, #3
|
||||
PrepareOp(0xe2610003); // rsb r0, r1, #3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 2);
|
||||
|
||||
PrepareOp(0xe2c10003); // sbc r0, r1, #3
|
||||
PrepareOp(0xe2c10003); // sbc r0, r1, #3
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 10;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 6);
|
||||
|
||||
PrepareOp(0xe2e10010); // rsc r0, r1, #16
|
||||
PrepareOp(0xe2e10010); // rsc r0, r1, #16
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 6;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 9);
|
||||
|
||||
PrepareOp(0xe3500010); // cmp r0, #16
|
||||
PrepareOp(0xe3500010); // cmp r0, #16
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 16;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
|
||||
|
||||
PrepareOp(0xe3700001); // cmn r0, #1
|
||||
PrepareOp(0xe3700001); // cmn r0, #1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0xffffffff;
|
||||
RunOp();
|
||||
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
|
||||
|
||||
PrepareOp(0xe2010001); // and r0, r1, #1
|
||||
PrepareOp(0xe2010001); // and r0, r1, #1
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 3;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0xe3810001); // orr r0, r1, #1
|
||||
PrepareOp(0xe3810001); // orr r0, r1, #1
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 3);
|
||||
|
||||
PrepareOp(0xe2210001); // eor r0, r1, #1
|
||||
PrepareOp(0xe2210001); // eor r0, r1, #1
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 3;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 2);
|
||||
|
||||
PrepareOp(0xe3c10001); // bic r0, r1, #1
|
||||
PrepareOp(0xe3c10001); // bic r0, r1, #1
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 3;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 2);
|
||||
|
||||
PrepareOp(0xe3100001); // tst r0, #1
|
||||
PrepareOp(0xe3100001); // tst r0, #1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 2;
|
||||
RunOp();
|
||||
|
@ -395,7 +396,7 @@ TEST_F(AicaArmTest, Operand2ImmTest)
|
|||
RunOp();
|
||||
ASSERT_NZCV_EQ(0);
|
||||
|
||||
PrepareOp(0xe3300001); // teq r0, #1
|
||||
PrepareOp(0xe3300001); // teq r0, #1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 1;
|
||||
RunOp();
|
||||
|
@ -405,7 +406,7 @@ TEST_F(AicaArmTest, Operand2ImmTest)
|
|||
RunOp();
|
||||
ASSERT_NZCV_EQ(0);
|
||||
|
||||
PrepareOp(0xe2522001); // subs r2, #1
|
||||
PrepareOp(0xe2522001); // subs r2, #1
|
||||
ResetNZCV();
|
||||
arm_Reg[2].I = 1;
|
||||
RunOp();
|
||||
|
@ -422,7 +423,7 @@ TEST_F(AicaArmTest, Operand2ImmTest)
|
|||
|
||||
TEST_F(AicaArmTest, Operand2ShiftImmTest)
|
||||
{
|
||||
PrepareOp(0xe0810202); // add r0, r1, r2, LSL #4
|
||||
PrepareOp(0xe0810202); // add r0, r1, r2, LSL #4
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 2;
|
||||
|
@ -450,17 +451,17 @@ TEST_F(AicaArmTest, Operand2ShiftImmTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x12345678);
|
||||
|
||||
PrepareOp(0xe0810062); // add r0, r1, r2, RRX
|
||||
PrepareOp(0xe0810062); // add r0, r1, r2, RRX
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x22222221;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x11111111);
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
|
||||
PrepareOp(0xe1910062); // orrs r0, r1, r2, RRX
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
PrepareOp(0xe1910062); // orrs r0, r1, r2, RRX
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x22222221;
|
||||
|
@ -470,14 +471,14 @@ TEST_F(AicaArmTest, Operand2ShiftImmTest)
|
|||
|
||||
// When an Operand2 constant is used with the instructions MOVS, MVNS, ANDS, ORRS, ORNS, EORS, BICS, TEQ or TST, the carry flag is updated to bit[31] of the constant,
|
||||
// if the constant is greater than 255 and can be produced by shifting an 8-bit value.
|
||||
PrepareOp(0xe3b00102); // movs r0, #0x80000000
|
||||
PrepareOp(0xe3b00102); // movs r0, #0x80000000
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x80000000);
|
||||
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
|
||||
|
||||
PrepareOp(0xe3f00000); // mvns r0, #0
|
||||
PrepareOp(0xe3f00000); // mvns r0, #0
|
||||
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
|
||||
arm_Reg[0].I = 0;
|
||||
RunOp();
|
||||
|
@ -487,7 +488,7 @@ TEST_F(AicaArmTest, Operand2ShiftImmTest)
|
|||
|
||||
TEST_F(AicaArmTest, Operand2RegShiftTest)
|
||||
{
|
||||
PrepareOp(0xe1810312); // orr r0, r1, r2, LSL r3
|
||||
PrepareOp(0xe1810312); // orr r0, r1, r2, LSL r3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x10;
|
||||
|
@ -495,7 +496,7 @@ TEST_F(AicaArmTest, Operand2RegShiftTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x1001);
|
||||
|
||||
PrepareOp(0xe1810352); // orr r0, r1, r2, ASR r3
|
||||
PrepareOp(0xe1810352); // orr r0, r1, r2, ASR r3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x80000000;
|
||||
|
@ -503,7 +504,7 @@ TEST_F(AicaArmTest, Operand2RegShiftTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
|
||||
|
||||
PrepareOp(0xe1810332); // orr r0, r1, r2, LSR r3
|
||||
PrepareOp(0xe1810332); // orr r0, r1, r2, LSR r3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x80000000;
|
||||
|
@ -511,7 +512,7 @@ TEST_F(AicaArmTest, Operand2RegShiftTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 3);
|
||||
|
||||
PrepareOp(0xe1810372); // orr r0, r1, r2, ROR r3
|
||||
PrepareOp(0xe1810372); // orr r0, r1, r2, ROR r3
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
arm_Reg[2].I = 0x43208765;
|
||||
|
@ -519,7 +520,7 @@ TEST_F(AicaArmTest, Operand2RegShiftTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x87654321);
|
||||
|
||||
PrepareOp(0xe1b0431f); // movs r4, r15, lsl r3
|
||||
PrepareOp(0xe1b0431f); // movs r4, r15, lsl r3
|
||||
ResetNZCV();
|
||||
arm_Reg[3].I = 0;
|
||||
arm_Reg[4].I = 0;
|
||||
|
@ -527,7 +528,7 @@ TEST_F(AicaArmTest, Operand2RegShiftTest)
|
|||
ASSERT_EQ(arm_Reg[4].I, 0x1000 + 12);
|
||||
ASSERT_NZCV_EQ(0);
|
||||
|
||||
PrepareOp(0xe1b0400f); // movs r4, r15, lsl #0
|
||||
PrepareOp(0xe1b0400f); // movs r4, r15, lsl #0
|
||||
ResetNZCV();
|
||||
arm_Reg[3].I = 0;
|
||||
arm_Reg[4].I = 0;
|
||||
|
@ -547,7 +548,7 @@ TEST_F(AicaArmTest, CarryTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
|
||||
PrepareOp(0xe1910822); // orrs r0, r1, r2, LSR #16
|
||||
PrepareOp(0xe1910822); // orrs r0, r1, r2, LSR #16
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -556,7 +557,7 @@ TEST_F(AicaArmTest, CarryTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0x8001);
|
||||
ASSERT_NZCV_EQ(C_FLAG); // C
|
||||
|
||||
PrepareOp(0xe1910042); // orrs r0, r1, r2, ASR #32
|
||||
PrepareOp(0xe1910042); // orrs r0, r1, r2, ASR #32
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -565,7 +566,7 @@ TEST_F(AicaArmTest, CarryTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
|
||||
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
|
||||
|
||||
PrepareOp(0xe1910842); // orrs r0, r1, r2, ASR #16
|
||||
PrepareOp(0xe1910842); // orrs r0, r1, r2, ASR #16
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -579,7 +580,7 @@ TEST_F(AicaArmTest, CarryTest)
|
|||
arm_Reg[2].I = 0x00004000;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
ASSERT_NZCV_EQ(0); //
|
||||
|
||||
PrepareOp(0xe1910802); // orrs r0, r1, r2, LSL #16
|
||||
ResetNZCV();
|
||||
|
@ -607,7 +608,7 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(*(u32*)&aica_ram[0x10000], 0xbaadcafe);
|
||||
|
||||
PrepareOp(0xe5b10004); // ldr r0, [r1, #4]!
|
||||
PrepareOp(0xe5b10004); // ldr r0, [r1, #4]!
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x10000;
|
||||
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
|
||||
|
@ -615,7 +616,7 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
ASSERT_EQ(arm_Reg[1].I, 0x10004);
|
||||
|
||||
PrepareOp(0xe4110004); // ldr r0, [r1], #-4
|
||||
PrepareOp(0xe4110004); // ldr r0, [r1], #-4
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x10004;
|
||||
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
|
||||
|
@ -623,59 +624,59 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
ASSERT_EQ(arm_Reg[1].I, 0x10000);
|
||||
|
||||
PrepareOp(0xe4900004); // ldr r0, [r0], #4
|
||||
PrepareOp(0xe4900004); // ldr r0, [r0], #4
|
||||
arm_Reg[0].I = 0x10004;
|
||||
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
|
||||
PrepareOp(0xe5b00004); // ldr r0, [r0, #4]!
|
||||
PrepareOp(0xe5b00004); // ldr r0, [r0, #4]!
|
||||
arm_Reg[0].I = 0x10000;
|
||||
*(u32*)&aica_ram[0x10000] = 0xbaadcafe;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
|
||||
|
||||
PrepareOp(0xe51f0004); // ldr r0, [r15, #-4]
|
||||
PrepareOp(0xe51f0004); // ldr r0, [r15, #-4]
|
||||
arm_Reg[0].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
|
||||
PrepareOp(0xe79000a4); // ldr r0, [r0, r4, LSR #1]
|
||||
PrepareOp(0xe79000a4); // ldr r0, [r0, r4, LSR #1]
|
||||
arm_Reg[0].I = 0x1003;
|
||||
arm_Reg[4].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
|
||||
PrepareOp(0xe7900084); // ldr r0, [r0, r4, LSL #1]
|
||||
PrepareOp(0xe7900084); // ldr r0, [r0, r4, LSL #1]
|
||||
arm_Reg[0].I = 0x1002;
|
||||
arm_Reg[4].I = 1;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
|
||||
PrepareOp(0xe7900024); // ldr r0, [r0, r4, LSR #32]
|
||||
PrepareOp(0xe7900024); // ldr r0, [r0, r4, LSR #32]
|
||||
arm_Reg[0].I = 0x1004;
|
||||
arm_Reg[4].I = 0x12345678;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
ASSERT_EQ(arm_Reg[4].I, 0x12345678);
|
||||
|
||||
PrepareOp(0xe7900044); // ldr r0, [r0, r4, ASR #32]
|
||||
PrepareOp(0xe7900044); // ldr r0, [r0, r4, ASR #32]
|
||||
arm_Reg[0].I = 0x1005;
|
||||
arm_Reg[4].I = 0x80000000;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
ASSERT_EQ(arm_Reg[4].I, 0x80000000);
|
||||
|
||||
PrepareOp(0xe7920002); // ldr r0,[r2,r2]
|
||||
PrepareOp(0xe7920002); // ldr r0,[r2,r2]
|
||||
arm_Reg[0].I = 5;
|
||||
arm_Reg[2].I = 0x1004 / 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
ASSERT_EQ(arm_Reg[2].I, 0x1004 / 2);
|
||||
|
||||
PrepareOp(0xe7920063); // ldr r0,[r2,r3, rrx]
|
||||
PrepareOp(0xe7920063); // ldr r0,[r2,r3, rrx]
|
||||
ResetNZCV();
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[0].I = 5;
|
||||
arm_Reg[2].I = 0x1006;
|
||||
arm_Reg[3].I = -4;
|
||||
|
@ -685,21 +686,21 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[3].I, -4);
|
||||
|
||||
// unaligned read
|
||||
PrepareOp(0xe7900002); // ldr r0,[r0,r2]
|
||||
PrepareOp(0xe7900002); // ldr r0,[r0,r2]
|
||||
arm_Reg[0].I = 0x1004;
|
||||
arm_Reg[2].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, (*(u32*)&aica_ram[0x1004]) >> 16 | (*(u32*)&aica_ram[0x1004]) << 16);
|
||||
ASSERT_EQ(arm_Reg[2].I, 2);
|
||||
|
||||
PrepareOp(0xe69000a4); // ldr r0,[r0],r4, lsr #1
|
||||
PrepareOp(0xe69000a4); // ldr r0,[r0],r4, lsr #1
|
||||
arm_Reg[0].I = 0x1004;
|
||||
arm_Reg[4].I = 2;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
ASSERT_EQ(arm_Reg[4].I, 2);
|
||||
|
||||
PrepareOp(0xe6920104); // ldr r0,[r2],r4, lsl #2
|
||||
PrepareOp(0xe6920104); // ldr r0,[r2],r4, lsl #2
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[2].I = 0x1004;
|
||||
arm_Reg[4].I = 2;
|
||||
|
@ -708,14 +709,14 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[2].I, 0x1004 + 8);
|
||||
ASSERT_EQ(arm_Reg[4].I, 2);
|
||||
|
||||
PrepareOp(0xe6920000); // ldr r0,[r2],r0
|
||||
PrepareOp(0xe6920000); // ldr r0,[r2],r0
|
||||
arm_Reg[0].I = 123;
|
||||
arm_Reg[2].I = 0x1004;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
|
||||
ASSERT_EQ(arm_Reg[2].I, 0x1004 + 123);
|
||||
|
||||
PrepareOp(0xe6920043); // ldr r0,[r2],r3, asr #32
|
||||
PrepareOp(0xe6920043); // ldr r0,[r2],r3, asr #32
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[2].I = 0x1004;
|
||||
arm_Reg[3].I = 0xc0000000;
|
||||
|
@ -724,9 +725,9 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[2].I, 0x1004 - 1);
|
||||
ASSERT_EQ(arm_Reg[3].I, 0xc0000000);
|
||||
|
||||
PrepareOp(0xe6920063); // ldr r0,[r2],r3, rrx
|
||||
PrepareOp(0xe6920063); // ldr r0,[r2],r3, rrx
|
||||
ResetNZCV();
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
|
||||
arm_Reg[0].I = 5;
|
||||
arm_Reg[2].I = 0x1004;
|
||||
arm_Reg[3].I = 0xfffffffc;
|
||||
|
@ -737,7 +738,7 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[3].I, 0xfffffffc);
|
||||
|
||||
// unaligned read
|
||||
PrepareOp(0xe6900002); // ldr r0,[r0],r2
|
||||
PrepareOp(0xe6900002); // ldr r0,[r0],r2
|
||||
arm_Reg[0].I = 0x1006;
|
||||
arm_Reg[2].I = 1;
|
||||
RunOp();
|
||||
|
@ -745,19 +746,18 @@ TEST_F(AicaArmTest, MemoryTest)
|
|||
ASSERT_EQ(arm_Reg[2].I, 1);
|
||||
|
||||
// conditional with write-back, false condition
|
||||
PrepareOp(0x04910004); // ldreq r0, [r1], #4
|
||||
PrepareOp(0x04910004); // ldreq r0, [r1], #4
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 0x1004;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
ASSERT_EQ(arm_Reg[1].I, 0x1004);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(AicaArmTest, PcRelativeTest)
|
||||
{
|
||||
PrepareOp(0xe38f0010); // orr r0, r15, #16
|
||||
PrepareOp(0xe38f0010); // orr r0, r15, #16
|
||||
arm_Reg[0].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0x1008 + 16);
|
||||
|
@ -776,7 +776,7 @@ TEST_F(AicaArmTest, PcRelativeTest)
|
|||
|
||||
TEST_F(AicaArmTest, ConditionalTest)
|
||||
{
|
||||
PrepareOp(0x01a00001); // moveq r0, r1
|
||||
PrepareOp(0x01a00001); // moveq r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -786,7 +786,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0x11a00001); // movne r0, r1
|
||||
PrepareOp(0x11a00001); // movne r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -797,7 +797,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0x21a00001); // movcs r0, r1
|
||||
PrepareOp(0x21a00001); // movcs r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -807,7 +807,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0x31a00001); // movcc r0, r1
|
||||
PrepareOp(0x31a00001); // movcc r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -818,7 +818,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0x41a00001); // movmi r0, r1
|
||||
PrepareOp(0x41a00001); // movmi r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -828,7 +828,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0x51a00001); // movpl r0, r1
|
||||
PrepareOp(0x51a00001); // movpl r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -839,7 +839,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0x61a00001); // movvs r0, r1
|
||||
PrepareOp(0x61a00001); // movvs r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -849,7 +849,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0x71a00001); // movvc r0, r1
|
||||
PrepareOp(0x71a00001); // movvc r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -860,7 +860,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0x81a00001); // movhi r0, r1
|
||||
PrepareOp(0x81a00001); // movhi r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -874,7 +874,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0x91a00001); // movls r0, r1
|
||||
PrepareOp(0x91a00001); // movls r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -889,7 +889,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0xa1a00001); // movge r0, r1
|
||||
PrepareOp(0xa1a00001); // movge r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -903,7 +903,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0xb1a00001); // movlt r0, r1
|
||||
PrepareOp(0xb1a00001); // movlt r0, r1
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
arm_Reg[1].I = 1;
|
||||
|
@ -917,7 +917,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 0);
|
||||
|
||||
PrepareOp(0xc1a00001); // movgt r0, r1
|
||||
PrepareOp(0xc1a00001); // movgt r0, r1
|
||||
// Z==0 && N==V
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
|
@ -937,7 +937,7 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[0].I, 1);
|
||||
|
||||
PrepareOp(0xd1a00001); // movle r0, r1
|
||||
PrepareOp(0xd1a00001); // movle r0, r1
|
||||
// Z==1 || N!=V
|
||||
ResetNZCV();
|
||||
arm_Reg[0].I = 0;
|
||||
|
@ -960,22 +960,22 @@ TEST_F(AicaArmTest, ConditionalTest)
|
|||
|
||||
TEST_F(AicaArmTest, JumpTest)
|
||||
{
|
||||
PrepareOp(0xea00003e); // b +248
|
||||
PrepareOp(0xea00003e); // b +248
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1100);
|
||||
|
||||
PrepareOp(0xeb00003e); // bl +248
|
||||
PrepareOp(0xeb00003e); // bl +248
|
||||
arm_Reg[14].I = 0;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1100);
|
||||
ASSERT_EQ(arm_Reg[14].I, 0x1004);
|
||||
|
||||
PrepareOp(0xe1a0f000); // mov pc, r0
|
||||
PrepareOp(0xe1a0f000); // mov pc, r0
|
||||
arm_Reg[0].I = 0x100;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x100);
|
||||
|
||||
PrepareOp(0xc1a0f000); // movgt pc, r0
|
||||
PrepareOp(0xc1a0f000); // movgt pc, r0
|
||||
ResetNZCV();
|
||||
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
|
||||
arm_Reg[0].I = 0x100;
|
||||
|
@ -991,7 +991,7 @@ TEST_F(AicaArmTest, JumpTest)
|
|||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0xbaadcafc);
|
||||
|
||||
PrepareOp(0x1b00003e); // blne +248
|
||||
PrepareOp(0x1b00003e); // blne +248
|
||||
ResetNZCV();
|
||||
arm_Reg[14].I = 0;
|
||||
RunOp();
|
||||
|
@ -1007,13 +1007,13 @@ TEST_F(AicaArmTest, JumpTest)
|
|||
|
||||
TEST_F(AicaArmTest, LdmStmTest)
|
||||
{
|
||||
PrepareOp(0xe8bd8000); // ldm sp!, {pc}
|
||||
PrepareOp(0xe8bd8000); // ldm sp!, {pc}
|
||||
arm_Reg[13].I = 0x1100;
|
||||
*(u32*)&aica_ram[0x1100] = 0x1234;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1234);
|
||||
|
||||
PrepareOp(0xe92d8000); // stmdb sp!, {pc}
|
||||
PrepareOp(0xe92d8000); // stmdb sp!, {pc}
|
||||
arm_Reg[13].I = 0x1104;
|
||||
RunOp();
|
||||
ASSERT_EQ(arm_Reg[13].I, 0x1100);
|
||||
|
@ -1023,20 +1023,20 @@ TEST_F(AicaArmTest, LdmStmTest)
|
|||
TEST_F(AicaArmTest, RegAllocTest)
|
||||
{
|
||||
u32 ops[] = {
|
||||
0xe3a00000, // mov r0, #0
|
||||
0xe3a01001, // mov r1, #1
|
||||
0xe3a02002, // mov r2, #2
|
||||
0xe3a03003, // mov r3, #3
|
||||
0xe3a04004, // mov r4, #4
|
||||
0xe3a05005, // mov r5, #5
|
||||
0xe3a06006, // mov r6, #6
|
||||
0xe0800001, // add r0, r0, r1
|
||||
0xe0811002, // add r1, r1, r2
|
||||
0xe0822003, // add r2, r2, r3
|
||||
0xe0833004, // add r3, r3, r4
|
||||
0xe0844005, // add r4, r4, r5
|
||||
0xe0855006, // add r5, r5, r6
|
||||
0xe0866000, // add r6, r6, r0
|
||||
0xe3a00000, // mov r0, #0
|
||||
0xe3a01001, // mov r1, #1
|
||||
0xe3a02002, // mov r2, #2
|
||||
0xe3a03003, // mov r3, #3
|
||||
0xe3a04004, // mov r4, #4
|
||||
0xe3a05005, // mov r5, #5
|
||||
0xe3a06006, // mov r6, #6
|
||||
0xe0800001, // add r0, r0, r1
|
||||
0xe0811002, // add r1, r1, r2
|
||||
0xe0822003, // add r2, r2, r3
|
||||
0xe0833004, // add r3, r3, r4
|
||||
0xe0844005, // add r4, r4, r5
|
||||
0xe0855006, // add r5, r5, r6
|
||||
0xe0866000, // add r6, r6, r0
|
||||
};
|
||||
PrepareOps(std::size(ops), ops);
|
||||
for (int i = 0; i < 15; i++)
|
||||
|
@ -1056,8 +1056,8 @@ TEST_F(AicaArmTest, RegAllocTest)
|
|||
TEST_F(AicaArmTest, ConditionRegAllocTest)
|
||||
{
|
||||
u32 ops1[] = {
|
||||
0x03a0004d, // moveq r0, #77
|
||||
0xe1a01000 // mov r1, r0
|
||||
0x03a0004d, // moveq r0, #77
|
||||
0xe1a01000 // mov r1, r0
|
||||
};
|
||||
PrepareOps(std::size(ops1), ops1);
|
||||
arm_Reg[0].I = 22;
|
||||
|
@ -1070,8 +1070,8 @@ TEST_F(AicaArmTest, ConditionRegAllocTest)
|
|||
ASSERT_EQ(arm_Reg[1].I, 22);
|
||||
|
||||
u32 ops2[] = {
|
||||
0x01a01000, // moveq r1, r0
|
||||
0xe1a02000 // mov r2, r0
|
||||
0x01a01000, // moveq r1, r0
|
||||
0xe1a02000 // mov r2, r0
|
||||
};
|
||||
PrepareOps(std::size(ops2), ops2);
|
||||
arm_Reg[0].I = 22;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "types.h"
|
||||
#include "cfg/ini.h"
|
||||
#include "cfg/cfg.h"
|
||||
#include "cfg/ini.h"
|
||||
#include "cheats.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "types.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class CheatManagerTest : public ::testing::Test {
|
||||
class CheatManagerTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
@ -18,8 +19,8 @@ protected:
|
|||
|
||||
TEST_F(CheatManagerTest, TestLoad)
|
||||
{
|
||||
FILE *fp = fopen("test.cht", "w");
|
||||
const char *s = R"(
|
||||
FILE* fp = fopen("test.cht", "w");
|
||||
const char* s = R"(
|
||||
cheat0_address = "1234"
|
||||
cheat0_address_bit_position = "0"
|
||||
cheat0_big_endian = "false"
|
||||
|
@ -150,7 +151,8 @@ TEST_F(CheatManagerTest, TestGameShark)
|
|||
ASSERT_EQ(1u, mgr.cheats[0].repeatCount);
|
||||
|
||||
mgr.reset("TESTGS10");
|
||||
mgr.addGameSharkCheat("cheat10", "0d654321" "00031984");
|
||||
mgr.addGameSharkCheat("cheat10", "0d654321"
|
||||
"00031984");
|
||||
ASSERT_EQ(Cheat::Type::runNextIfGt, mgr.cheats[0].type);
|
||||
ASSERT_EQ(0x654321u, mgr.cheats[0].address);
|
||||
ASSERT_EQ(0x1984u, mgr.cheats[0].value);
|
||||
|
@ -176,7 +178,6 @@ TEST_F(CheatManagerTest, TestGameShark)
|
|||
ASSERT_EQ(1u, mgr.cheats[2].repeatCount);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CheatManagerTest, TestGameSharkError)
|
||||
{
|
||||
CheatManager mgr;
|
||||
|
@ -258,8 +259,8 @@ TEST_F(CheatManagerTest, TestSave)
|
|||
|
||||
TEST_F(CheatManagerTest, TestSubBytePatch)
|
||||
{
|
||||
FILE *fp = fopen("test.cht", "w");
|
||||
const char *s = R"(
|
||||
FILE* fp = fopen("test.cht", "w");
|
||||
const char* s = R"(
|
||||
cheat0_address = "0x10000"
|
||||
cheat0_address_bit_position = "0xF0"
|
||||
cheat0_big_endian = "false"
|
||||
|
@ -304,5 +305,4 @@ cheats = "2"
|
|||
WriteMem8_nommu(0x8c010001, 0);
|
||||
mgr.apply();
|
||||
ASSERT_EQ(2, ReadMem8_nommu(0x8c010001));
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "types.h"
|
||||
#include "cfg/ini.h"
|
||||
#include "types.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class ConfigFileTest : public ::testing::Test {
|
||||
class ConfigFileTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(ConfigFileTest, TestLoadSave)
|
||||
|
@ -16,7 +17,7 @@ TEST_F(ConfigFileTest, TestLoadSave)
|
|||
ASSERT_EQ(2, file.get_int("", "prop2", 0));
|
||||
ASSERT_TRUE(file.get_bool("", "prop3", false));
|
||||
|
||||
FILE *fp = fopen("test.cfg", "w");
|
||||
FILE* fp = fopen("test.cfg", "w");
|
||||
file.save(fp);
|
||||
fclose(fp);
|
||||
fp = fopen("test.cfg", "r");
|
||||
|
@ -38,7 +39,7 @@ TEST_F(ConfigFileTest, TestLoadSave)
|
|||
TEST_F(ConfigFileTest, TestQuotes)
|
||||
{
|
||||
using namespace emucfg;
|
||||
FILE *fp = fopen("test.cfg", "w");
|
||||
FILE* fp = fopen("test.cfg", "w");
|
||||
fprintf(fp, "propWithQuotes=\"value with quotes\"\n");
|
||||
fprintf(fp, "propWithQuotes2=\"42\"\n");
|
||||
fprintf(fp, "propWithQuotes3=\"true\"\n");
|
||||
|
@ -55,7 +56,7 @@ TEST_F(ConfigFileTest, TestQuotes)
|
|||
TEST_F(ConfigFileTest, TestTrim)
|
||||
{
|
||||
using namespace emucfg;
|
||||
FILE *fp = fopen("test.cfg", "w");
|
||||
FILE* fp = fopen("test.cfg", "w");
|
||||
fprintf(fp, " prop = \"value 1 \" \n\n\n");
|
||||
fprintf(fp, " prop2 = 42 \n");
|
||||
fprintf(fp, " prop3 = yes \r\n\n");
|
||||
|
@ -80,7 +81,7 @@ TEST_F(ConfigFileTest, TestLoadSaveSection)
|
|||
ASSERT_EQ(2, file.get_int("sect2", "prop2", 0));
|
||||
ASSERT_TRUE(file.get_bool("sect2", "prop3", false));
|
||||
|
||||
FILE *fp = fopen("test.cfg", "w");
|
||||
FILE* fp = fopen("test.cfg", "w");
|
||||
file.save(fp);
|
||||
fclose(fp);
|
||||
fp = fopen("test.cfg", "r");
|
||||
|
|
|
@ -3,27 +3,28 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "gtest/gtest.h"
|
||||
#include "types.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/modules/mmu.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "hw/sh4/sh4_core.h"
|
||||
#include "types.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class MmuTest : public ::testing::Test {
|
||||
class MmuTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
|
|
@ -3,24 +3,25 @@
|
|||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "sh4_ops.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "sh4_ops.h"
|
||||
|
||||
class Sh4InterpreterTest : public Sh4OpTest {
|
||||
class Sh4InterpreterTest : public Sh4OpTest
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "types.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "types.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "hw/sh4/sh4_core.h"
|
||||
#include "hw/sh4/dyna/shil.h"
|
||||
#include "hw/sh4/sh4_core.h"
|
||||
#define SHIL_MODE 2
|
||||
#include "hw/sh4/dyna/shil_canonical.h"
|
||||
|
||||
|
@ -109,7 +109,8 @@ static void div32u_slow(u32& r1, u32 r2, u32& r3)
|
|||
}
|
||||
}
|
||||
|
||||
class Div32Test : public ::testing::Test {
|
||||
class Div32Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
@ -122,7 +123,7 @@ protected:
|
|||
void div32s(u32 n1, u32 n2, u32 n3)
|
||||
{
|
||||
const long long int dividend = (long long)n3 << 32 | n1;
|
||||
//printf("%lld / %d = ", dividend, n2);
|
||||
// printf("%lld / %d = ", dividend, n2);
|
||||
int r1s = n1;
|
||||
int r2 = n2;
|
||||
int r3s = n3;
|
||||
|
@ -130,7 +131,7 @@ protected:
|
|||
int r3f = r3s;
|
||||
div32s_slow((u32&)r1s, r2, (u32&)r3s);
|
||||
div32s_fast((u32&)r1f, r2, (u32&)r3f);
|
||||
//printf("%d %% %d\n", (r1s << 1) | sr.T, r3s);
|
||||
// printf("%d %% %d\n", (r1s << 1) | sr.T, r3s);
|
||||
ASSERT_EQ(r1s, r1f);
|
||||
ASSERT_EQ(r3s, r3f);
|
||||
}
|
||||
|
@ -138,7 +139,7 @@ protected:
|
|||
void div32u(u32 n1, u32 n2, u32 n3)
|
||||
{
|
||||
const long long int dividend = (long long)n3 << 32 | n1;
|
||||
//printf("%lld / %d = ", dividend, n2);
|
||||
// printf("%lld / %d = ", dividend, n2);
|
||||
int r1s = n1;
|
||||
int r2 = n2;
|
||||
int r3s = n3;
|
||||
|
@ -146,7 +147,7 @@ protected:
|
|||
int r3f = r3s;
|
||||
div32u_slow((u32&)r1s, r2, (u32&)r3s);
|
||||
div32u_fast((u32&)r1f, r2, (u32&)r3f);
|
||||
//printf("%d %% %d\n", (r1s << 1) | sr.T, r3s);
|
||||
// printf("%d %% %d\n", (r1s << 1) | sr.T, r3s);
|
||||
ASSERT_EQ(r3s, r3f);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "types.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "emulator.h"
|
||||
#include "cfg/option.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "types.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class SerializeTest : public ::testing::Test {
|
||||
class SerializeTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,12 +10,12 @@ void os_DoEvents()
|
|||
{
|
||||
}
|
||||
|
||||
void os_RunInstance(int argc, const char *argv[])
|
||||
void os_RunInstance(int argc, const char* argv[])
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void os_SetThreadName(const char *name)
|
||||
void os_SetThreadName(const char* name)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "util/periodic_thread.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <atomic>
|
||||
|
||||
class PeriodicThreadTest : public ::testing::Test
|
||||
|
@ -9,9 +9,8 @@ class PeriodicThreadTest : public ::testing::Test
|
|||
TEST_F(PeriodicThreadTest, Basic)
|
||||
{
|
||||
std::atomic<int> counter = 0;
|
||||
PeriodicThread thread = PeriodicThread("Test", [&]() {
|
||||
counter++;
|
||||
});
|
||||
PeriodicThread thread = PeriodicThread("Test", [&]()
|
||||
{ counter++; });
|
||||
thread.setPeriod(10);
|
||||
thread.start();
|
||||
usleep(15'000);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "util/tsqueue.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
|
@ -31,11 +31,11 @@ TEST_F(TsQueueTest, MultiThread)
|
|||
{
|
||||
TsQueue<bool> queue;
|
||||
std::atomic<bool> gotResult = false;
|
||||
std::future<bool> future = std::async(std::launch::async, [&]() {
|
||||
std::future<bool> future = std::async(std::launch::async, [&]()
|
||||
{
|
||||
bool res = queue.pop();
|
||||
gotResult = true;
|
||||
return res;
|
||||
});
|
||||
return res; });
|
||||
usleep(500'000);
|
||||
ASSERT_FALSE(gotResult);
|
||||
ASSERT_EQ(std::future_status::timeout, future.wait_for(std::chrono::seconds(0)));
|
||||
|
@ -45,7 +45,8 @@ TEST_F(TsQueueTest, MultiThread)
|
|||
|
||||
TEST_F(TsQueueTest, Class)
|
||||
{
|
||||
struct T1 {
|
||||
struct T1
|
||||
{
|
||||
float f;
|
||||
};
|
||||
TsQueue<T1> q1;
|
||||
|
@ -67,18 +68,20 @@ TEST_F(TsQueueTest, Class)
|
|||
class T3
|
||||
{
|
||||
public:
|
||||
T3(const char *s) : s(s) {}
|
||||
T3(const char* s) : s(s) {}
|
||||
T3(const T3&) = delete;
|
||||
T3(T3&& other) {
|
||||
T3(T3&& other)
|
||||
{
|
||||
std::swap(s, other.s);
|
||||
}
|
||||
T3& operator=(const T3& other) = delete;
|
||||
T3& operator=(T3&& other) {
|
||||
T3& operator=(T3&& other)
|
||||
{
|
||||
std::swap(s, other.s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char *s;
|
||||
const char* s;
|
||||
};
|
||||
TsQueue<T3> q3;
|
||||
q3.push(T3("pi"));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "util/worker_thread.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
|
@ -9,9 +9,10 @@ class WorkerThreadTest : public ::testing::Test
|
|||
|
||||
TEST_F(WorkerThreadTest, Basic)
|
||||
{
|
||||
WorkerThread worker{"Test"};
|
||||
WorkerThread worker { "Test" };
|
||||
std::atomic<bool> done = false;
|
||||
const auto& task = [&]() {
|
||||
const auto& task = [&]()
|
||||
{
|
||||
done = true;
|
||||
};
|
||||
worker.run(task);
|
||||
|
@ -28,12 +29,14 @@ TEST_F(WorkerThreadTest, Basic)
|
|||
|
||||
TEST_F(WorkerThreadTest, MultiThread)
|
||||
{
|
||||
WorkerThread worker{"Test"};
|
||||
WorkerThread worker { "Test" };
|
||||
std::atomic<int> counter = 0;
|
||||
const auto& task = [&]() {
|
||||
const auto& task = [&]()
|
||||
{
|
||||
++counter;
|
||||
};
|
||||
const auto& consumer = [&]() {
|
||||
const auto& consumer = [&]()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
worker.run(task);
|
||||
};
|
||||
|
@ -50,30 +53,33 @@ TEST_F(WorkerThreadTest, MultiThread)
|
|||
// be executed. But it shouldn't crash.
|
||||
TEST_F(WorkerThreadTest, StartStop)
|
||||
{
|
||||
WorkerThread worker{"Test"};
|
||||
WorkerThread worker { "Test" };
|
||||
std::atomic<int> counter = 0;
|
||||
const auto& task = [&]() {
|
||||
const auto& task = [&]()
|
||||
{
|
||||
++counter;
|
||||
};
|
||||
const auto& consumer = [&]() {
|
||||
const auto& consumer = [&]()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
worker.run(task);
|
||||
};
|
||||
std::future<void> future = std::async(std::launch::async, consumer);
|
||||
std::future<void> future2 = std::async(std::launch::async, [&]() {
|
||||
std::future<void> future2 = std::async(std::launch::async, [&]()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
worker.stop();
|
||||
});
|
||||
worker.stop(); });
|
||||
future.get();
|
||||
future2.get();
|
||||
worker.stop();
|
||||
//ASSERT_EQ(100, counter);
|
||||
// ASSERT_EQ(100, counter);
|
||||
}
|
||||
|
||||
TEST_F(WorkerThreadTest, Future)
|
||||
{
|
||||
WorkerThread worker{"Test"};
|
||||
const auto& task = [](u32 v) -> u32 {
|
||||
WorkerThread worker { "Test" };
|
||||
const auto& task = [](u32 v) -> u32
|
||||
{
|
||||
return v;
|
||||
};
|
||||
std::future<u32> f = worker.runFuture(task, 42);
|
||||
|
|
Loading…
Reference in New Issue