From 36c476505461227ac68a785f8d33865f2cb08465 Mon Sep 17 00:00:00 2001
From: FearlessTobi <thm.frey@gmail.com>
Date: Tue, 1 May 2018 19:57:01 +0200
Subject: [PATCH] citra_qt: Add a game region column

---
 src/citra_qt/game_list.cpp |  2 ++
 src/citra_qt/game_list.h   |  1 +
 src/citra_qt/game_list_p.h | 48 ++++++++++++++++++++++++++++++++++++++
 src/core/loader/smdh.cpp   | 16 +++++++++++++
 src/core/loader/smdh.h     | 14 +++++++++++
 5 files changed, 81 insertions(+)

diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 1bd387b75..3043db2c1 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -231,6 +231,7 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
     item_model->insertColumns(0, COLUMN_COUNT);
     item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
     item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
+    item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, "Region");
     item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
     item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
 
@@ -492,6 +493,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
             emit EntryReady({
                 new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),
                 new GameListItemCompat(compatibility),
+                new GameListItemRegion(smdh),
                 new GameListItem(
                     QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
                 new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 376dc8474..3fabd5088 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -31,6 +31,7 @@ public:
     enum {
         COLUMN_NAME,
         COLUMN_COMPATIBILITY,
+        COLUMN_REGION,
         COLUMN_FILE_TYPE,
         COLUMN_SIZE,
         COLUMN_COUNT, // Number of columns
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index 208d3bd53..5e2134ae5 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -73,6 +73,38 @@ static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh,
     return QString::fromUtf16(smdh.GetShortTitle(language).data());
 }
 
+/**
+ * Gets the game region from SMDH data.
+ * @param smdh SMDH data
+ * @return QString region
+ */
+static QString GetRegionFromSMDH(const Loader::SMDH& smdh) {
+    const Loader::SMDH::GameRegion region = smdh.GetRegion();
+
+    switch (region) {
+    case Loader::SMDH::GameRegion::Invalid:
+        return QObject::tr("Invalid region");
+    case Loader::SMDH::GameRegion::Japan:
+        return QObject::tr("Japan");
+    case Loader::SMDH::GameRegion::NorthAmerica:
+        return QObject::tr("North America");
+    case Loader::SMDH::GameRegion::Europe:
+        return QObject::tr("Europe");
+    case Loader::SMDH::GameRegion::Australia:
+        return QObject::tr("Australia");
+    case Loader::SMDH::GameRegion::China:
+        return QObject::tr("China");
+    case Loader::SMDH::GameRegion::Korea:
+        return QObject::tr("Korea");
+    case Loader::SMDH::GameRegion::Taiwan:
+        return QObject::tr("Taiwan");
+    case Loader::SMDH::GameRegion::RegionFree:
+        return QObject::tr("Region free");
+    default:
+        return QObject::tr("Invalid Region");
+    }
+}
+
 struct CompatStatus {
     QString color;
     const char* text;
@@ -168,6 +200,22 @@ public:
     }
 };
 
+class GameListItemRegion : public GameListItem {
+public:
+    GameListItemRegion() = default;
+    explicit GameListItemRegion(const std::vector<u8>& smdh_data) {
+        if (!Loader::IsValidSMDH(smdh_data)) {
+            setText(QObject::tr("Invalid region"));
+            return;
+        }
+
+        Loader::SMDH smdh;
+        memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
+
+        setText(GetRegionFromSMDH(smdh));
+    }
+};
+
 /**
  * A specialization of GameListItem for size values.
  * This class ensures that for every numerical size value it holds (in bytes), a correct
diff --git a/src/core/loader/smdh.cpp b/src/core/loader/smdh.cpp
index f53dab770..3392739c5 100644
--- a/src/core/loader/smdh.cpp
+++ b/src/core/loader/smdh.cpp
@@ -48,4 +48,20 @@ std::array<u16, 0x40> SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language)
     return titles[static_cast<int>(language)].short_title;
 }
 
+SMDH::GameRegion SMDH::GetRegion() const {
+    if (region_lockout == 0x7fffffff) {
+        return GameRegion::RegionFree;
+    }
+
+    constexpr u32 REGION_COUNT = 7;
+    u32 region = 0;
+    for (; region < REGION_COUNT; ++region) {
+        if (region_lockout & (1 << region)) {
+            return static_cast<GameRegion>(region);
+        }
+    }
+
+    return GameRegion::Invalid;
+}
+
 } // namespace Loader
diff --git a/src/core/loader/smdh.h b/src/core/loader/smdh.h
index 8e0b658a0..0da1cf855 100644
--- a/src/core/loader/smdh.h
+++ b/src/core/loader/smdh.h
@@ -62,6 +62,18 @@ struct SMDH {
         TraditionalChinese = 11
     };
 
+    enum class GameRegion {
+        Invalid = -1,
+        Japan = 0,
+        NorthAmerica = 1,
+        Europe = 2,
+        Australia = 3,
+        China = 4,
+        Korea = 5,
+        Taiwan = 6,
+        RegionFree = 7,
+    };
+
     /**
      * Gets game icon from SMDH
      * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
@@ -75,6 +87,8 @@ struct SMDH {
      * @return UTF-16 array of the short title
      */
     std::array<u16, 0x40> GetShortTitle(Loader::SMDH::TitleLanguage language) const;
+
+    GameRegion GetRegion() const;
 };
 static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong");