summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2019-08-08 16:28:30 +0200
committerAdmiral H. Curtiss <pikachu025@gmail.com>2019-09-23 01:55:46 +0200
commitff90a5855dbfb5fa1652ce391f2fa2c1ffc32ee3 (patch)
tree95ad9fbecb0df5d8ff240db63425fa3c19f1f1a6 /Source/Core
parent8f573cf5f2a93d535d21cdb4233ea8b2f17b1409 (diff)
Qt/GCMemcardManager: Fix animation frame order and speed.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcard.cpp9
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcard.h1
-rw-r--r--Source/Core/DolphinQt/GCMemcardManager.cpp75
-rw-r--r--Source/Core/DolphinQt/GCMemcardManager.h11
4 files changed, 71 insertions, 25 deletions
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
index 45a2141353..8bdb8262f0 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
@@ -473,6 +473,15 @@ std::string GCMemcard::DEntry_BIFlags(u8 index) const
return flags;
}
+bool GCMemcard::DEntry_IsPingPong(u8 index) const
+{
+ if (!m_valid || index >= DIRLEN)
+ return false;
+
+ const int flags = GetActiveDirectory().m_dir_entries[index].m_banner_and_icon_flags;
+ return (flags & 0b0000'0100) != 0;
+}
+
std::string GCMemcard::DEntry_FileName(u8 index) const
{
if (!m_valid || index >= DIRLEN)
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.h b/Source/Core/Core/HW/GCMemcard/GCMemcard.h
index c173138028..e676977293 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcard.h
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.h
@@ -438,6 +438,7 @@ public:
std::string DEntry_GameCode(u8 index) const;
std::string DEntry_Makercode(u8 index) const;
std::string DEntry_BIFlags(u8 index) const;
+ bool DEntry_IsPingPong(u8 index) const;
std::string DEntry_FileName(u8 index) const;
u32 DEntry_ModTime(u8 index) const;
u32 DEntry_ImageOffset(u8 index) const;
diff --git a/Source/Core/DolphinQt/GCMemcardManager.cpp b/Source/Core/DolphinQt/GCMemcardManager.cpp
index 7bf63bec96..455d479a9e 100644
--- a/Source/Core/DolphinQt/GCMemcardManager.cpp
+++ b/Source/Core/DolphinQt/GCMemcardManager.cpp
@@ -37,6 +37,16 @@ constexpr u32 ICON_HEIGHT = 32;
constexpr u32 ANIM_MAX_FRAMES = 8;
constexpr float ROW_HEIGHT = 28;
+struct GCMemcardManager::IconAnimationData
+{
+ // the individual frames
+ std::vector<QPixmap> m_frames;
+
+ // vector containing a list of frame indices that indicate, for each time unit,
+ // the frame that should be displayed when at that time unit
+ std::vector<u8> m_frame_timing;
+};
+
GCMemcardManager::GCMemcardManager(QWidget* parent) : QDialog(parent)
{
CreateWidgets();
@@ -48,7 +58,9 @@ GCMemcardManager::GCMemcardManager(QWidget* parent) : QDialog(parent)
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &GCMemcardManager::DrawIcons);
- m_timer->start(1000 / 8);
+ // individual frames of icon animations can stay on screen for 4, 8, or 12 frames at 60 FPS,
+ // which means the fastest animation and common denominator is 15 FPS or 66 milliseconds per frame
+ m_timer->start(1000 / 15);
// Make the dimensions more reasonable on startup
resize(650, 500);
@@ -182,7 +194,9 @@ void GCMemcardManager::UpdateSlotTable(int slot)
return s.substr(0, offset);
};
- for (int i = 0; i < memcard->GetNumFiles(); i++)
+ const u8 num_files = memcard->GetNumFiles();
+ m_slot_active_icons[slot].reserve(num_files);
+ for (int i = 0; i < num_files; i++)
{
int file_index = memcard->GetFileIndex(i);
table->setRowCount(i + 1);
@@ -200,18 +214,13 @@ void GCMemcardManager::UpdateSlotTable(int slot)
banner->setData(Qt::DecorationRole, GetBannerFromSaveFile(file_index, slot));
banner->setFlags(banner->flags() ^ Qt::ItemIsEditable);
- auto frames = GetIconFromSaveFile(file_index, slot);
+ auto icon_data = GetIconFromSaveFile(file_index, slot);
auto* icon = new QTableWidgetItem;
- icon->setData(Qt::DecorationRole, frames[0]);
+ icon->setData(Qt::DecorationRole, icon_data.m_frames[0]);
std::optional<DEntry> entry = memcard->GetDEntry(file_index);
- // TODO: This is wrong, the animation speed is not static and is already correctly calculated in
- // GetIconFromSaveFile(), just not returned
- const u16 animation_speed = entry ? entry->m_animation_speed : 1;
- const auto speed = (((animation_speed >> 8) & 1) << 2) + (animation_speed & 1);
-
- m_slot_active_icons[slot].push_back({speed, frames});
+ m_slot_active_icons[slot].emplace_back(std::move(icon_data));
table->setItem(i, 0, banner);
table->setItem(i, 1, create_item(title));
@@ -442,25 +451,25 @@ void GCMemcardManager::FixChecksums()
void GCMemcardManager::DrawIcons()
{
- m_current_frame++;
- m_current_frame %= 15;
-
const auto column = 3;
for (int slot = 0; slot < SLOT_COUNT; slot++)
{
int row = 0;
- for (auto& icon : m_slot_active_icons[slot])
+ for (const auto& icon : m_slot_active_icons[slot])
{
- int frame = (m_current_frame / 3 - icon.first) % icon.second.size();
+ const u64 current_time_in_animation = m_current_frame % icon.m_frame_timing.size();
+ const u8 current_frame = icon.m_frame_timing[current_time_in_animation];
auto* item = new QTableWidgetItem;
- item->setData(Qt::DecorationRole, icon.second[frame]);
+ item->setData(Qt::DecorationRole, icon.m_frames[current_frame]);
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
m_slot_table[slot]->setItem(row, column, item);
row++;
}
}
+
+ ++m_current_frame;
}
QPixmap GCMemcardManager::GetBannerFromSaveFile(int file_index, int slot)
@@ -479,35 +488,57 @@ QPixmap GCMemcardManager::GetBannerFromSaveFile(int file_index, int slot)
return QPixmap::fromImage(image);
}
-std::vector<QPixmap> GCMemcardManager::GetIconFromSaveFile(int file_index, int slot)
+GCMemcardManager::IconAnimationData GCMemcardManager::GetIconFromSaveFile(int file_index, int slot)
{
auto& memcard = m_slot_memcard[slot];
std::vector<u8> anim_delay(ANIM_MAX_FRAMES);
std::vector<u32> anim_data(ICON_WIDTH * ICON_HEIGHT * ANIM_MAX_FRAMES);
- std::vector<QPixmap> frame_pixmaps;
+ IconAnimationData frame_data;
- u32 num_frames = memcard->ReadAnimRGBA8(file_index, anim_data.data(), anim_delay.data());
+ const u32 num_frames = memcard->ReadAnimRGBA8(file_index, anim_data.data(), anim_delay.data());
// Decode Save File Animation
if (num_frames > 0)
{
+ frame_data.m_frames.reserve(num_frames);
const u32 per_frame_offset = ICON_WIDTH * ICON_HEIGHT;
for (u32 f = 0; f < num_frames; ++f)
{
QImage img(reinterpret_cast<u8*>(&anim_data[f * per_frame_offset]), ICON_WIDTH, ICON_HEIGHT,
QImage::Format_ARGB32);
- frame_pixmaps.push_back(QPixmap::fromImage(img));
+ frame_data.m_frames.push_back(QPixmap::fromImage(img));
+ for (int i = 0; i < anim_delay[f]; ++i)
+ {
+ frame_data.m_frame_timing.push_back(static_cast<u8>(f));
+ }
+ }
+
+ const bool is_pingpong = memcard->DEntry_IsPingPong(file_index);
+ if (is_pingpong && num_frames >= 3)
+ {
+ // if the animation 'ping-pongs' between start and end then the animation frame order is
+ // something like 'abcdcbabcdcba' instead of the usual 'abcdabcdabcd'
+ // to display that correctly just append all except the first and last frame in reverse order
+ // at the end of the animation
+ for (u32 f = num_frames - 2; f > 0; --f)
+ {
+ for (int i = 0; i < anim_delay[f]; ++i)
+ {
+ frame_data.m_frame_timing.push_back(static_cast<u8>(f));
+ }
+ }
}
}
else
{
// No Animation found, use an empty placeholder instead.
- frame_pixmaps.push_back(QPixmap());
+ frame_data.m_frames.emplace_back();
+ frame_data.m_frame_timing.push_back(1);
}
- return frame_pixmaps;
+ return frame_data;
}
QString GCMemcardManager::GetErrorMessagesForErrorCode(const GCMemcardErrorCode& code)
diff --git a/Source/Core/DolphinQt/GCMemcardManager.h b/Source/Core/DolphinQt/GCMemcardManager.h
index 5e25989221..f3431ed929 100644
--- a/Source/Core/DolphinQt/GCMemcardManager.h
+++ b/Source/Core/DolphinQt/GCMemcardManager.h
@@ -11,6 +11,8 @@
#include <QDialog>
+#include "Common/CommonTypes.h"
+
class GCMemcard;
class GCMemcardErrorCode;
@@ -34,6 +36,8 @@ public:
static QString GetErrorMessagesForErrorCode(const GCMemcardErrorCode& code);
private:
+ struct IconAnimationData;
+
void CreateWidgets();
void ConnectWidgets();
@@ -52,7 +56,8 @@ private:
void DrawIcons();
QPixmap GetBannerFromSaveFile(int file_index, int slot);
- std::vector<QPixmap> GetIconFromSaveFile(int file_index, int slot);
+
+ IconAnimationData GetIconFromSaveFile(int file_index, int slot);
// Actions
QPushButton* m_select_button;
@@ -65,7 +70,7 @@ private:
// Slots
static constexpr int SLOT_COUNT = 2;
- std::array<std::vector<std::pair<int, std::vector<QPixmap>>>, SLOT_COUNT> m_slot_active_icons;
+ std::array<std::vector<IconAnimationData>, SLOT_COUNT> m_slot_active_icons;
std::array<std::unique_ptr<GCMemcard>, SLOT_COUNT> m_slot_memcard;
std::array<QGroupBox*, SLOT_COUNT> m_slot_group;
std::array<QLineEdit*, SLOT_COUNT> m_slot_file_edit;
@@ -74,7 +79,7 @@ private:
std::array<QLabel*, SLOT_COUNT> m_slot_stat_label;
int m_active_slot;
- int m_current_frame;
+ u64 m_current_frame = 0;
QDialogButtonBox* m_button_box;
QTimer* m_timer;