This commit is contained in:
Isaac Marovitz 2025-07-18 15:28:22 +08:00 committed by GitHub
commit e0d495266e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 1365 additions and 1172 deletions

46
.clang-format Normal file
View File

@ -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

1
.clang-format-ignore Normal file
View File

@ -0,0 +1 @@
core/deps/*

View File

@ -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
{

View File

@ -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;
}

View File

@ -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);

View 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;
};

View 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/>.
*/
#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;

View 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
@ -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;
};

View 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/>.
*/
#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);

View 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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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(&params);
/* 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;

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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, &param);
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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

94
core/build.h Executable file → Normal file
View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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");

View File

@ -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
{

View File

@ -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
{

View File

@ -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);
}
};

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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"));

View File

@ -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);