From c1e8600e94d581347f15a20f63ef1d0a34710ab6 Mon Sep 17 00:00:00 2001
From: R-YaTian <47445484+R-YaTian@users.noreply.github.com>
Date: Sun, 29 Sep 2024 18:06:20 +0800
Subject: [PATCH 1/2] am: Fix too much file handle cause memory leak

---
 src/core/hle/service/am/am.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aee587734..d896082a7 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -194,6 +194,8 @@ Result CIAFile::WriteTitleMetadata() {
             // TODO: Correct error code.
             return FileSys::ResultFileNotFound;
         }
+        if (content_count > 255)
+            file.Close();
     }
 
     if (container.GetTitleMetadata().HasEncryptedContent()) {
@@ -224,6 +226,8 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
     // has been written since we might get a written buffer which contains multiple .app
     // contents or only part of a larger .app's contents.
     const u64 offset_max = offset + length;
+    if (content_written.size() > 255)
+        content_files.clear();
     for (std::size_t i = 0; i < content_written.size(); i++) {
         if (content_written[i] < container.GetContentSize(i)) {
             // The size, minimum unwritten offset, and maximum unwritten offset of this content
@@ -243,7 +247,9 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
             // Since the incoming TMD has already been written, we can use GetTitleContentPath
             // to get the content paths to write to.
             FileSys::TitleMetadata tmd = container.GetTitleMetadata();
-            auto& file = content_files[i];
+            auto path = GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update);
+            auto& file = content_written.size() > 255 ? content_files.emplace_back(path, "ab+")
+                                                      : content_files[i];
 
             std::vector<u8> temp(buffer + (range_min - offset),
                                  buffer + (range_min - offset) + available_to_write);
@@ -259,6 +265,8 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
             content_written[i] += available_to_write;
             LOG_DEBUG(Service_AM, "Wrote {:x} to content {}, total {:x}", available_to_write, i,
                       content_written[i]);
+            if (content_written.size() > 255)
+                file.Close();
         }
     }
 

From 6f18e6a39fdb450c7e1ba6b9d9de78096e636d61 Mon Sep 17 00:00:00 2001
From: R-YaTian <47445484+R-YaTian@users.noreply.github.com>
Date: Sat, 12 Oct 2024 12:40:00 +0800
Subject: [PATCH 2/2] am: Improve code logic

---
 src/core/hle/service/am/am.cpp | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d896082a7..c0e28c2b8 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -45,6 +45,7 @@ constexpr u16 CATEGORY_DLP = 0x0001;
 constexpr u8 VARIATION_SYSTEM = 0x02;
 constexpr u32 TID_HIGH_UPDATE = 0x0004000E;
 constexpr u32 TID_HIGH_DLC = 0x0004008C;
+constexpr u16 MAX_CONTENT_COUNT = 255;
 
 struct TitleInfo {
     u64_le tid;
@@ -194,7 +195,7 @@ Result CIAFile::WriteTitleMetadata() {
             // TODO: Correct error code.
             return FileSys::ResultFileNotFound;
         }
-        if (content_count > 255)
+        if (content_count > MAX_CONTENT_COUNT)
             file.Close();
     }
 
@@ -226,8 +227,6 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
     // has been written since we might get a written buffer which contains multiple .app
     // contents or only part of a larger .app's contents.
     const u64 offset_max = offset + length;
-    if (content_written.size() > 255)
-        content_files.clear();
     for (std::size_t i = 0; i < content_written.size(); i++) {
         if (content_written[i] < container.GetContentSize(i)) {
             // The size, minimum unwritten offset, and maximum unwritten offset of this content
@@ -247,9 +246,12 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
             // Since the incoming TMD has already been written, we can use GetTitleContentPath
             // to get the content paths to write to.
             FileSys::TitleMetadata tmd = container.GetTitleMetadata();
-            auto path = GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update);
-            auto& file = content_written.size() > 255 ? content_files.emplace_back(path, "ab+")
-                                                      : content_files[i];
+            auto& file = content_files[i];
+            if (content_written.size() > MAX_CONTENT_COUNT) {
+                auto path = GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update);
+                FileUtil::IOFile fp(path, "ab+");
+                file = std::move(fp);
+            }
 
             std::vector<u8> temp(buffer + (range_min - offset),
                                  buffer + (range_min - offset) + available_to_write);
@@ -265,8 +267,9 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
             content_written[i] += available_to_write;
             LOG_DEBUG(Service_AM, "Wrote {:x} to content {}, total {:x}", available_to_write, i,
                       content_written[i]);
-            if (content_written.size() > 255)
+            if (content_written.size() > MAX_CONTENT_COUNT) {
                 file.Close();
+            }
         }
     }