2017-11-20 10:03:26 +00:00
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "ui_mainwindow.h"
|
2018-01-12 03:18:50 +00:00
|
|
|
#include "PsfLoader.h"
|
2017-11-20 10:03:26 +00:00
|
|
|
#include "PsfTags.h"
|
|
|
|
#include "AppConfig.h"
|
|
|
|
|
2023-05-23 12:58:02 -04:00
|
|
|
#ifdef WIN32
|
|
|
|
#include "ui_win32/SH_WaveOut.h"
|
|
|
|
#else
|
|
|
|
#include "SH_OpenAL.h"
|
|
|
|
#endif
|
|
|
|
|
2017-11-20 10:03:26 +00:00
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
#define PREFERENCE_UI_LASTFOLDER "ui.lastfolder"
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
MainWindow::MainWindow(QWidget* parent)
|
|
|
|
: QMainWindow(parent)
|
|
|
|
, ui(new Ui::MainWindow)
|
|
|
|
, model(this)
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
m_virtualMachine = new CPsfVm();
|
2023-05-23 12:58:02 -04:00
|
|
|
#ifdef WIN32
|
|
|
|
m_virtualMachine->SetSpuHandler(&CSH_WaveOut::HandlerFactory);
|
|
|
|
#else
|
2017-11-20 10:03:26 +00:00
|
|
|
m_virtualMachine->SetSpuHandler(&CSH_OpenAL::HandlerFactory);
|
2023-05-23 12:58:02 -04:00
|
|
|
#endif
|
2019-07-01 13:11:23 +01:00
|
|
|
m_OnNewFrameConnection = m_virtualMachine->OnNewFrame.Connect([&]() { OnNewFrame(); });
|
2017-11-20 10:03:26 +00:00
|
|
|
|
2023-05-23 16:40:11 -04:00
|
|
|
m_debugger = std::make_unique<DebuggerWindow>(*m_virtualMachine);
|
|
|
|
|
2017-11-20 10:03:26 +00:00
|
|
|
model.setHeaderData(0, Qt::Orientation::Horizontal, QVariant("Game"), Qt::DisplayRole);
|
|
|
|
model.setHeaderData(1, Qt::Orientation::Horizontal, QVariant("Title"), Qt::DisplayRole);
|
|
|
|
model.setHeaderData(2, Qt::Orientation::Horizontal, QVariant("Length"), Qt::DisplayRole);
|
|
|
|
ui->tableView->setModel(&model);
|
|
|
|
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
2018-04-30 21:01:23 +01:00
|
|
|
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
2017-11-20 10:03:26 +00:00
|
|
|
|
|
|
|
CAppConfig::GetInstance().RegisterPreferenceString(PREFERENCE_UI_LASTFOLDER, QDir::homePath().toStdString().c_str());
|
|
|
|
m_path = CAppConfig::GetInstance().GetPreferenceString(PREFERENCE_UI_LASTFOLDER);
|
|
|
|
|
|
|
|
// used as workaround to avoid direct ui access from a thread
|
|
|
|
connect(this, SIGNAL(ChangeRow(int)), ui->tableView, SLOT(selectRow(int)));
|
|
|
|
|
|
|
|
m_running = true;
|
|
|
|
m_thread = std::thread(&MainWindow::UiUpdateLoop, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::UiUpdateLoop()
|
|
|
|
{
|
|
|
|
while(m_running)
|
|
|
|
{
|
|
|
|
auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(16);
|
|
|
|
if(m_frames > m_trackLength)
|
|
|
|
{
|
|
|
|
m_frames = 0;
|
|
|
|
on_nextButton_clicked();
|
|
|
|
}
|
2023-06-01 17:00:43 -04:00
|
|
|
else if((m_frames >= m_fadePosition) && (m_trackLength != m_fadePosition))
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
|
|
|
float currentRatio = static_cast<float>(m_trackLength - m_fadePosition) / static_cast<float>(m_trackLength - m_frames);
|
2018-04-30 21:01:23 +01:00
|
|
|
float currentVolume = (1 / currentRatio) * m_volumeAdjust;
|
2017-11-20 10:03:26 +00:00
|
|
|
m_virtualMachine->SetVolumeAdjust(currentVolume);
|
|
|
|
}
|
|
|
|
else if(m_trackLength > 0 && m_frames > 0 && m_fadePosition > 0)
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
float currentRatio = 1 / (static_cast<float>(m_trackLength - m_fadePosition) / static_cast<float>(m_frames));
|
2017-11-20 10:03:26 +00:00
|
|
|
if(currentRatio < 1.0f)
|
|
|
|
{
|
|
|
|
float currentVolume = currentRatio * m_volumeAdjust;
|
|
|
|
m_virtualMachine->SetVolumeAdjust(currentVolume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::this_thread::sleep_until(end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
{
|
|
|
|
m_running = false;
|
|
|
|
if(m_thread.joinable()) m_thread.join();
|
2023-05-23 16:40:11 -04:00
|
|
|
m_debugger.reset();
|
2017-11-20 10:03:26 +00:00
|
|
|
if(m_virtualMachine != nullptr)
|
|
|
|
{
|
|
|
|
m_virtualMachine->Pause();
|
|
|
|
delete m_virtualMachine;
|
|
|
|
m_virtualMachine = nullptr;
|
|
|
|
}
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_actionOpen_triggered()
|
|
|
|
{
|
|
|
|
QFileDialog dialog(this);
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFiles);
|
|
|
|
dialog.setDirectory(QString(m_path.c_str()));
|
2023-05-23 11:48:40 -04:00
|
|
|
dialog.setNameFilter(tr("PSF files (*.psf *.psf2 *.psfp *.minipsf *.minipsf2 *.minipsfp)"));
|
2017-11-20 10:03:26 +00:00
|
|
|
if(dialog.exec())
|
|
|
|
{
|
|
|
|
if(m_virtualMachine != nullptr)
|
|
|
|
{
|
|
|
|
m_virtualMachine->Pause();
|
|
|
|
m_virtualMachine->Reset();
|
|
|
|
int index = model.rowCount();
|
2018-04-30 21:01:23 +01:00
|
|
|
foreach(QString file, dialog.selectedFiles())
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
2023-05-23 11:54:14 -04:00
|
|
|
AddFileToPlaylist(file.toStdString());
|
2017-11-20 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_path = QFileInfo(model.at(index)->path.c_str()).absolutePath().toStdString();
|
|
|
|
CAppConfig::GetInstance().SetPreferenceString(PREFERENCE_UI_LASTFOLDER, m_path.c_str());
|
|
|
|
PlayTrackIndex(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-23 11:54:14 -04:00
|
|
|
void MainWindow::AddFileToPlaylist(const fs::path& filePath)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2023-05-23 09:43:10 -04:00
|
|
|
//TODO: Use PlaylistDiscoveryService
|
|
|
|
auto streamProvider = CreatePsfStreamProvider(fs::path());
|
|
|
|
std::unique_ptr<Framework::CStream> inputStream(streamProvider->GetStreamForPath(filePath.native()));
|
|
|
|
CPsfBase psfFile(*inputStream);
|
|
|
|
auto tags = CPsfTags::TagMap(psfFile.GetTagsBegin(), psfFile.GetTagsEnd());
|
2023-05-23 11:54:14 -04:00
|
|
|
model.addPlaylistItem(filePath.string(), tags);
|
|
|
|
}
|
|
|
|
catch(const std::exception& e)
|
|
|
|
{
|
|
|
|
QMessageBox messageBox;
|
|
|
|
messageBox.critical(0, "Error", e.what());
|
|
|
|
messageBox.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-30 11:22:57 -04:00
|
|
|
void MainWindow::ShowDebugger()
|
|
|
|
{
|
|
|
|
m_debugger->showMaximized();
|
|
|
|
m_debugger->raise();
|
|
|
|
m_debugger->activateWindow();
|
|
|
|
}
|
|
|
|
|
2017-11-20 10:03:26 +00:00
|
|
|
void MainWindow::UpdateTrackDetails(CPsfBase::TagMap& tags)
|
|
|
|
{
|
|
|
|
auto tag = CPsfTags(tags);
|
|
|
|
ui->current_game->setText(QString::fromWCharArray(tag.GetTagValue("game").c_str()));
|
|
|
|
ui->current_total_length->setText(QString::fromWCharArray(tag.GetTagValue("length").c_str()));
|
|
|
|
ui->current_title->setText(QString::fromWCharArray(tag.GetTagValue("title").c_str()));
|
|
|
|
ui->current_artist->setText(QString::fromWCharArray(tag.GetTagValue("artist").c_str()));
|
|
|
|
ui->current_cp->setText(QString::fromWCharArray(tag.GetTagValue("copyright").c_str()));
|
|
|
|
ui->current_year->setText(QString::fromWCharArray(tag.GetTagValue("year").c_str()));
|
|
|
|
|
|
|
|
m_volumeAdjust = 1.0f;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
m_volumeAdjust = stof(tag.GetTagValue("volume"));
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
double dlength = CPsfTags::ConvertTimeString(tag.GetTagValue("length").c_str());
|
|
|
|
double dfade = CPsfTags::ConvertTimeString(tag.GetTagValue("fade").c_str());
|
|
|
|
m_trackLength = static_cast<uint64>(dlength * 60.0);
|
|
|
|
m_fadePosition = m_trackLength - static_cast<uint64>(dfade * 60.0);
|
|
|
|
m_frames = 0;
|
|
|
|
|
|
|
|
m_virtualMachine->SetVolumeAdjust((dfade > 0) ? 0 : m_volumeAdjust);
|
|
|
|
|
|
|
|
ChangeRow(m_currentindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::OnNewFrame()
|
|
|
|
{
|
|
|
|
m_frames++;
|
2018-04-30 21:01:23 +01:00
|
|
|
ui->current_time->setText(QDateTime::fromTime_t(m_frames / 60).toUTC().toString("mm:ss"));
|
2017-11-20 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
void MainWindow::on_tableView_customContextMenuRequested(const QPoint& pos)
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
|
|
|
auto item = ui->tableView->indexAt(pos);
|
|
|
|
if(item.isValid())
|
|
|
|
{
|
|
|
|
QMenu* menu = new QMenu(this);
|
|
|
|
auto playAct = new QAction("Play", this);
|
|
|
|
auto delAct = new QAction("Remove", this);
|
|
|
|
|
|
|
|
menu->addAction(playAct);
|
|
|
|
menu->addAction(delAct);
|
|
|
|
menu->popup(ui->tableView->viewport()->mapToGlobal(pos));
|
|
|
|
|
|
|
|
auto index = item.row();
|
|
|
|
connect(playAct, &QAction::triggered, std::bind(&MainWindow::PlayTrackIndex, this, index));
|
|
|
|
connect(delAct, &QAction::triggered, std::bind(&MainWindow::DeleteTrackIndex, this, index));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-23 16:40:11 -04:00
|
|
|
void MainWindow::UnloadCurrentTrack()
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
2023-05-23 16:40:11 -04:00
|
|
|
if(m_currentindex == -1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-11-20 10:03:26 +00:00
|
|
|
m_virtualMachine->Pause();
|
2023-05-23 16:40:11 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
auto item = model.at(m_currentindex);
|
|
|
|
auto itemPath = fs::path(item->path);
|
|
|
|
auto tagPackageName = itemPath.stem().string();
|
|
|
|
m_virtualMachine->SaveDebugTags(tagPackageName.c_str());
|
|
|
|
#endif
|
|
|
|
m_currentindex = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::PlayTrackIndex(int index)
|
|
|
|
{
|
|
|
|
UnloadCurrentTrack();
|
2017-11-20 10:03:26 +00:00
|
|
|
m_virtualMachine->Reset();
|
|
|
|
m_currentindex = index;
|
2023-05-23 16:40:11 -04:00
|
|
|
|
2017-11-20 10:03:26 +00:00
|
|
|
CPsfBase::TagMap tags;
|
2023-05-23 16:40:11 -04:00
|
|
|
auto item = model.at(m_currentindex);
|
|
|
|
auto itemPath = fs::path(item->path);
|
|
|
|
CPsfLoader::LoadPsf(*m_virtualMachine, itemPath.native(), "", &tags);
|
2017-11-20 10:03:26 +00:00
|
|
|
UpdateTrackDetails(tags);
|
2023-05-23 16:40:11 -04:00
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
auto tagPackageName = itemPath.stem().string();
|
|
|
|
m_virtualMachine->LoadDebugTags(tagPackageName.c_str());
|
|
|
|
m_debugger->Reset();
|
|
|
|
#else
|
2017-11-20 10:03:26 +00:00
|
|
|
m_virtualMachine->Resume();
|
2023-05-23 16:40:11 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::closeEvent(QCloseEvent* event)
|
|
|
|
{
|
|
|
|
UnloadCurrentTrack();
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
m_debugger->close();
|
|
|
|
#endif
|
2017-11-20 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
void MainWindow::on_tableView_doubleClicked(const QModelIndex& index)
|
2017-11-20 10:03:26 +00:00
|
|
|
{
|
|
|
|
PlayTrackIndex(index.row());
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::DeleteTrackIndex(int index)
|
|
|
|
{
|
|
|
|
auto playlistsize = model.removePlaylistItem(index);
|
|
|
|
if(index == m_currentindex)
|
|
|
|
{
|
|
|
|
if(playlistsize >= m_currentindex)
|
|
|
|
{
|
|
|
|
PlayTrackIndex(m_currentindex);
|
|
|
|
}
|
|
|
|
else if(m_currentindex > 0 && playlistsize == m_currentindex - 1)
|
|
|
|
{
|
|
|
|
PlayTrackIndex(--m_currentindex);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(m_virtualMachine->GetStatus() == CVirtualMachine::STATUS::RUNNING)
|
|
|
|
{
|
|
|
|
m_virtualMachine->Pause();
|
|
|
|
m_virtualMachine->Reset();
|
|
|
|
}
|
|
|
|
m_currentindex--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(index < m_currentindex)
|
|
|
|
{
|
|
|
|
m_currentindex--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_playpauseButton_clicked()
|
|
|
|
{
|
|
|
|
if(m_currentindex > -1)
|
|
|
|
{
|
|
|
|
if(m_virtualMachine != nullptr)
|
|
|
|
{
|
|
|
|
if(m_virtualMachine->GetStatus() == CVirtualMachine::STATUS::RUNNING)
|
|
|
|
{
|
|
|
|
m_virtualMachine->Pause();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_virtualMachine->Resume();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
on_actionOpen_triggered();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_nextButton_clicked()
|
|
|
|
{
|
|
|
|
if(model.rowCount() < 1) return;
|
|
|
|
|
|
|
|
int currentindex = m_currentindex;
|
|
|
|
if(m_currentindex < model.rowCount() - 1)
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentindex += 1;
|
2017-11-20 10:03:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_currentindex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(currentindex == m_currentindex) return;
|
|
|
|
|
|
|
|
PlayTrackIndex(m_currentindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_prevButton_clicked()
|
|
|
|
{
|
|
|
|
if(model.rowCount() < 1) return;
|
|
|
|
|
|
|
|
if(m_currentindex > 0)
|
|
|
|
{
|
|
|
|
m_currentindex -= 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_currentindex = model.rowCount() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlayTrackIndex(m_currentindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_actionPlayPause_triggered()
|
|
|
|
{
|
|
|
|
on_playpauseButton_clicked();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_actionPrev_triggered()
|
|
|
|
{
|
|
|
|
on_prevButton_clicked();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_actionNext_triggered()
|
|
|
|
{
|
|
|
|
on_nextButton_clicked();
|
|
|
|
}
|