summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/install_dialog.cpp72
-rw-r--r--src/yuzu/install_dialog.h35
-rw-r--r--src/yuzu/main.cpp295
-rw-r--r--src/yuzu/main.h4
-rw-r--r--src/yuzu/main.ui2
6 files changed, 290 insertions, 124 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 742b72856..f430525f8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -98,11 +98,13 @@ add_executable(yuzu
98 game_list_p.h 98 game_list_p.h
99 game_list_worker.cpp 99 game_list_worker.cpp
100 game_list_worker.h 100 game_list_worker.h
101 hotkeys.cpp
102 hotkeys.h
103 install_dialog.cpp
104 install_dialog.h
101 loading_screen.cpp 105 loading_screen.cpp
102 loading_screen.h 106 loading_screen.h
103 loading_screen.ui 107 loading_screen.ui
104 hotkeys.cpp
105 hotkeys.h
106 main.cpp 108 main.cpp
107 main.h 109 main.h
108 main.ui 110 main.ui
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
new file mode 100644
index 000000000..fac158c25
--- /dev/null
+++ b/src/yuzu/install_dialog.cpp
@@ -0,0 +1,72 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QCheckBox>
6#include <QDialogButtonBox>
7#include <QFileInfo>
8#include <QHBoxLayout>
9#include <QLabel>
10#include <QListWidget>
11#include <QVBoxLayout>
12#include "yuzu/install_dialog.h"
13#include "yuzu/uisettings.h"
14
15InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) {
16 file_list = new QListWidget(this);
17
18 for (const QString& file : files) {
19 QListWidgetItem* item = new QListWidgetItem(QFileInfo(file).fileName(), file_list);
20 item->setData(Qt::UserRole, file);
21 item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
22 item->setCheckState(Qt::Checked);
23 }
24
25 file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 6) / 5);
26
27 vbox_layout = new QVBoxLayout;
28
29 hbox_layout = new QHBoxLayout;
30
31 description = new QLabel(tr("Please confirm these are the files you wish to install."));
32
33 overwrite_files = new QCheckBox(tr("Overwrite Existing Files"));
34 overwrite_files->setCheckState(Qt::Unchecked);
35
36 buttons = new QDialogButtonBox;
37 buttons->addButton(QDialogButtonBox::Cancel);
38 buttons->addButton(tr("Install"), QDialogButtonBox::AcceptRole);
39
40 connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept);
41 connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject);
42
43 hbox_layout->addWidget(overwrite_files);
44 hbox_layout->addWidget(buttons);
45
46 vbox_layout->addWidget(description);
47 vbox_layout->addWidget(file_list);
48 vbox_layout->addLayout(hbox_layout);
49
50 setLayout(vbox_layout);
51 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
52 setWindowTitle(tr("Install Files to NAND"));
53}
54
55InstallDialog::~InstallDialog() = default;
56
57QStringList InstallDialog::GetFilenames() const {
58 QStringList filenames;
59
60 for (int i = 0; i < file_list->count(); ++i) {
61 const QListWidgetItem* item = file_list->item(i);
62 if (item->checkState() == Qt::Checked) {
63 filenames.append(item->data(Qt::UserRole).toString());
64 }
65 }
66
67 return filenames;
68}
69
70bool InstallDialog::ShouldOverwriteFiles() const {
71 return overwrite_files->isChecked();
72} \ No newline at end of file
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h
new file mode 100644
index 000000000..3eaa9e60a
--- /dev/null
+++ b/src/yuzu/install_dialog.h
@@ -0,0 +1,35 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8
9class QCheckBox;
10class QDialogButtonBox;
11class QHBoxLayout;
12class QLabel;
13class QListWidget;
14class QVBoxLayout;
15
16class InstallDialog : public QDialog {
17 Q_OBJECT
18
19public:
20 explicit InstallDialog(QWidget* parent, const QStringList& files);
21 ~InstallDialog() override;
22
23 QStringList GetFilenames() const;
24 bool ShouldOverwriteFiles() const;
25
26private:
27 QListWidget* file_list;
28
29 QVBoxLayout* vbox_layout;
30 QHBoxLayout* hbox_layout;
31
32 QLabel* description;
33 QCheckBox* overwrite_files;
34 QDialogButtonBox* buttons;
35}; \ No newline at end of file
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 4d501a8f9..45ddc3baf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -107,6 +107,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
107#include "yuzu/game_list.h" 107#include "yuzu/game_list.h"
108#include "yuzu/game_list_p.h" 108#include "yuzu/game_list_p.h"
109#include "yuzu/hotkeys.h" 109#include "yuzu/hotkeys.h"
110#include "yuzu/install_dialog.h"
110#include "yuzu/loading_screen.h" 111#include "yuzu/loading_screen.h"
111#include "yuzu/main.h" 112#include "yuzu/main.h"
112#include "yuzu/uisettings.h" 113#include "yuzu/uisettings.h"
@@ -1596,38 +1597,67 @@ void GMainWindow::OnMenuLoadFolder() {
1596void GMainWindow::OnMenuInstallToNAND() { 1597void GMainWindow::OnMenuInstallToNAND() {
1597 const QString file_filter = 1598 const QString file_filter =
1598 tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " 1599 tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
1599 "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " 1600 "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge "
1600 "Image (*.xci)"); 1601 "Image (*.xci)");
1601 QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), 1602 QStringList files = QFileDialog::getOpenFileNames(this, tr("Install Files"),
1602 UISettings::values.roms_path, file_filter); 1603 UISettings::values.roms_path, file_filter);
1603 1604
1604 if (filename.isEmpty()) { 1605 if (files.isEmpty()) {
1606 return;
1607 }
1608
1609 InstallDialog installDialog(this, files);
1610 if (installDialog.exec() == QDialog::Rejected) {
1605 return; 1611 return;
1606 } 1612 }
1607 1613
1608 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, 1614 const QStringList filenames = installDialog.GetFilenames();
1609 const FileSys::VirtualFile& dest, std::size_t block_size) { 1615 const bool overwrite_files = installDialog.ShouldOverwriteFiles();
1610 if (src == nullptr || dest == nullptr) 1616
1617 int count = 0;
1618 int total_count = filenames.size();
1619 bool is_progressdialog_created = false;
1620
1621 const auto qt_raw_copy = [this, &count, &total_count, &is_progressdialog_created](
1622 const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
1623 std::size_t block_size) {
1624 if (src == nullptr || dest == nullptr) {
1611 return false; 1625 return false;
1612 if (!dest->Resize(src->GetSize())) 1626 }
1627 if (!dest->Resize(src->GetSize())) {
1613 return false; 1628 return false;
1629 }
1614 1630
1615 std::array<u8, 0x1000> buffer{}; 1631 std::array<u8, 0x1000> buffer{};
1616 const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); 1632 const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size());
1617 1633
1618 QProgressDialog progress( 1634 if (!is_progressdialog_created) {
1619 tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())), 1635 ui.action_Install_File_NAND->setEnabled(false);
1620 tr("Cancel"), 0, progress_maximum, this); 1636 install_progress = new QProgressDialog(
1621 progress.setWindowModality(Qt::WindowModal); 1637 tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())),
1638 tr("Cancel"), 0, progress_maximum, this);
1639 install_progress->setWindowTitle(
1640 tr("%n file(s) remaining", "", total_count - count - 1));
1641 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
1642 ~Qt::WindowMaximizeButtonHint);
1643 install_progress->setAutoClose(false);
1644 is_progressdialog_created = true;
1645 } else {
1646 install_progress->setWindowTitle(
1647 tr("%n file(s) remaining", "", total_count - count - 1));
1648 install_progress->setLabelText(
1649 tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())));
1650 install_progress->setMaximum(progress_maximum);
1651 }
1622 1652
1623 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { 1653 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
1624 if (progress.wasCanceled()) { 1654 if (install_progress->wasCanceled()) {
1625 dest->Resize(0); 1655 dest->Resize(0);
1626 return false; 1656 return false;
1627 } 1657 }
1628 1658
1629 const int progress_value = static_cast<int>(i / buffer.size()); 1659 const int progress_value = static_cast<int>(i / buffer.size());
1630 progress.setValue(progress_value); 1660 install_progress->setValue(progress_value);
1631 1661
1632 const auto read = src->Read(buffer.data(), buffer.size(), i); 1662 const auto read = src->Read(buffer.data(), buffer.size(), i);
1633 dest->Write(buffer.data(), read, i); 1663 dest->Write(buffer.data(), read, i);
@@ -1636,143 +1666,166 @@ void GMainWindow::OnMenuInstallToNAND() {
1636 return true; 1666 return true;
1637 }; 1667 };
1638 1668
1639 const auto success = [this]() { 1669 const auto success = [this, &count, &is_progressdialog_created]() {
1670 if (is_progressdialog_created) {
1671 install_progress->close();
1672 }
1640 QMessageBox::information(this, tr("Successfully Installed"), 1673 QMessageBox::information(this, tr("Successfully Installed"),
1641 tr("The file was successfully installed.")); 1674 tr("%n file(s) successfully installed", "", count));
1642 game_list->PopulateAsync(UISettings::values.game_dirs); 1675 game_list->PopulateAsync(UISettings::values.game_dirs);
1643 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + 1676 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
1644 DIR_SEP + "game_list"); 1677 DIR_SEP + "game_list");
1678 ui.action_Install_File_NAND->setEnabled(true);
1645 }; 1679 };
1646 1680
1647 const auto failed = [this]() { 1681 const auto failed = [this, &is_progressdialog_created](const QString& file) {
1682 if (is_progressdialog_created) {
1683 install_progress->close();
1684 }
1648 QMessageBox::warning( 1685 QMessageBox::warning(
1649 this, tr("Failed to Install"), 1686 this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()),
1650 tr("There was an error while attempting to install the provided file. It " 1687 tr("There was an error while attempting to install the provided file. It "
1651 "could have an incorrect format or be missing metadata. Please " 1688 "could have an incorrect format or be missing metadata. Please "
1652 "double-check your file and try again.")); 1689 "double-check your file and try again."));
1690 game_list->PopulateAsync(UISettings::values.game_dirs);
1691 ui.action_Install_File_NAND->setEnabled(true);
1653 }; 1692 };
1654 1693
1655 const auto overwrite = [this]() { 1694 const auto overwrite = [this](const QString& file) {
1656 return QMessageBox::question(this, tr("Failed to Install"), 1695 return QMessageBox::question(
1657 tr("The file you are attempting to install already exists " 1696 this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()),
1658 "in the cache. Would you like to overwrite it?")) == 1697 tr("The file you are attempting to install already exists "
1659 QMessageBox::Yes; 1698 "in the cache. Would you like to overwrite it?")) == QMessageBox::Yes;
1660 }; 1699 };
1661 1700
1662 if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || 1701 for (const QString& filename : filenames) {
1663 filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { 1702 if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) ||
1664 std::shared_ptr<FileSys::NSP> nsp; 1703 filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
1665 if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { 1704 std::shared_ptr<FileSys::NSP> nsp;
1666 nsp = std::make_shared<FileSys::NSP>( 1705 if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
1667 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 1706 nsp = std::make_shared<FileSys::NSP>(
1668 if (nsp->IsExtractedType()) 1707 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
1669 failed(); 1708 if (nsp->IsExtractedType()) {
1670 } else { 1709 failed(filename);
1671 const auto xci = std::make_shared<FileSys::XCI>( 1710 break;
1672 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 1711 }
1673 nsp = xci->GetSecurePartitionNSP(); 1712 } else {
1674 } 1713 const auto xci = std::make_shared<FileSys::XCI>(
1714 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
1715 nsp = xci->GetSecurePartitionNSP();
1716 }
1675 1717
1676 if (nsp->GetStatus() != Loader::ResultStatus::Success) { 1718 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
1677 failed(); 1719 failed(filename);
1678 return; 1720 break;
1679 } 1721 }
1680 const auto res = Core::System::GetInstance() 1722 const auto res = Core::System::GetInstance()
1681 .GetFileSystemController() 1723 .GetFileSystemController()
1682 .GetUserNANDContents() 1724 .GetUserNANDContents()
1683 ->InstallEntry(*nsp, false, qt_raw_copy); 1725 ->InstallEntry(*nsp, false, qt_raw_copy);
1684 if (res == FileSys::InstallResult::Success) { 1726 if (res == FileSys::InstallResult::Success) {
1685 success(); 1727 ++count;
1686 } else { 1728 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1687 if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1729 if (overwrite_files && overwrite(filename)) {
1688 if (overwrite()) {
1689 const auto res2 = Core::System::GetInstance() 1730 const auto res2 = Core::System::GetInstance()
1690 .GetFileSystemController() 1731 .GetFileSystemController()
1691 .GetUserNANDContents() 1732 .GetUserNANDContents()
1692 ->InstallEntry(*nsp, true, qt_raw_copy); 1733 ->InstallEntry(*nsp, true, qt_raw_copy);
1693 if (res2 == FileSys::InstallResult::Success) { 1734 if (res2 != FileSys::InstallResult::Success) {
1694 success(); 1735 failed(filename);
1695 } else { 1736 break;
1696 failed();
1697 } 1737 }
1738 ++count;
1739 } else {
1740 --total_count;
1698 } 1741 }
1699 } else { 1742 } else {
1700 failed(); 1743 failed(filename);
1744 break;
1701 } 1745 }
1702 } 1746 } else {
1703 } else { 1747 const auto nca = std::make_shared<FileSys::NCA>(
1704 const auto nca = std::make_shared<FileSys::NCA>( 1748 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
1705 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 1749 const auto id = nca->GetStatus();
1706 const auto id = nca->GetStatus();
1707
1708 // Game updates necessary are missing base RomFS
1709 if (id != Loader::ResultStatus::Success &&
1710 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
1711 failed();
1712 return;
1713 }
1714 1750
1715 const QStringList tt_options{tr("System Application"), 1751 // Game updates necessary are missing base RomFS
1716 tr("System Archive"), 1752 if (id != Loader::ResultStatus::Success &&
1717 tr("System Application Update"), 1753 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
1718 tr("Firmware Package (Type A)"), 1754 failed(filename);
1719 tr("Firmware Package (Type B)"), 1755 break;
1720 tr("Game"), 1756 }
1721 tr("Game Update"),
1722 tr("Game DLC"),
1723 tr("Delta Title")};
1724 bool ok;
1725 const auto item = QInputDialog::getItem(
1726 this, tr("Select NCA Install Type..."),
1727 tr("Please select the type of title you would like to install this NCA as:\n(In "
1728 "most instances, the default 'Game' is fine.)"),
1729 tt_options, 5, false, &ok);
1730
1731 auto index = tt_options.indexOf(item);
1732 if (!ok || index == -1) {
1733 QMessageBox::warning(this, tr("Failed to Install"),
1734 tr("The title type you selected for the NCA is invalid."));
1735 return;
1736 }
1737 1757
1738 // If index is equal to or past Game, add the jump in TitleType. 1758 const QStringList tt_options{tr("System Application"),
1739 if (index >= 5) { 1759 tr("System Archive"),
1740 index += static_cast<size_t>(FileSys::TitleType::Application) - 1760 tr("System Application Update"),
1741 static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); 1761 tr("Firmware Package (Type A)"),
1742 } 1762 tr("Firmware Package (Type B)"),
1763 tr("Game"),
1764 tr("Game Update"),
1765 tr("Game DLC"),
1766 tr("Delta Title")};
1767 bool ok;
1768 const auto item = QInputDialog::getItem(
1769 this, tr("Select NCA Install Type..."),
1770 tr("Please select the type of title you would like to install this NCA as:\n(In "
1771 "most instances, the default 'Game' is fine.)"),
1772 tt_options, 5, false, &ok);
1773
1774 auto index = tt_options.indexOf(item);
1775 if (!ok || index == -1) {
1776 QMessageBox::warning(this, tr("Failed to Install"),
1777 tr("The title type you selected for the NCA is invalid."));
1778 break;
1779 }
1743 1780
1744 FileSys::InstallResult res; 1781 // If index is equal to or past Game, add the jump in TitleType.
1745 if (index >= static_cast<s32>(FileSys::TitleType::Application)) { 1782 if (index >= 5) {
1746 res = Core::System::GetInstance() 1783 index += static_cast<size_t>(FileSys::TitleType::Application) -
1747 .GetFileSystemController() 1784 static_cast<size_t>(FileSys::TitleType::FirmwarePackageB);
1748 .GetUserNANDContents() 1785 }
1749 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1750 qt_raw_copy);
1751 } else {
1752 res = Core::System::GetInstance()
1753 .GetFileSystemController()
1754 .GetSystemNANDContents()
1755 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1756 qt_raw_copy);
1757 }
1758 1786
1759 if (res == FileSys::InstallResult::Success) { 1787 FileSys::InstallResult res;
1760 success(); 1788 if (index >= static_cast<s32>(FileSys::TitleType::Application)) {
1761 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1789 res = Core::System::GetInstance()
1762 if (overwrite()) { 1790 .GetFileSystemController()
1763 const auto res2 = Core::System::GetInstance() 1791 .GetUserNANDContents()
1764 .GetFileSystemController() 1792 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1765 .GetUserNANDContents() 1793 qt_raw_copy);
1766 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), 1794 } else {
1767 true, qt_raw_copy); 1795 res = Core::System::GetInstance()
1768 if (res2 == FileSys::InstallResult::Success) { 1796 .GetFileSystemController()
1769 success(); 1797 .GetSystemNANDContents()
1798 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1799 qt_raw_copy);
1800 }
1801
1802 if (res == FileSys::InstallResult::Success) {
1803 ++count;
1804 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1805 if (overwrite_files && overwrite(filename)) {
1806 const auto res2 =
1807 Core::System::GetInstance()
1808 .GetFileSystemController()
1809 .GetUserNANDContents()
1810 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true,
1811 qt_raw_copy);
1812 if (res2 != FileSys::InstallResult::Success) {
1813 failed(filename);
1814 break;
1815 }
1816 ++count;
1770 } else { 1817 } else {
1771 failed(); 1818 --total_count;
1772 } 1819 }
1820 } else {
1821 failed(filename);
1822 break;
1773 } 1823 }
1774 } else { 1824 }
1775 failed(); 1825
1826 // Return success only on the last file
1827 if (filename == filenames.last()) {
1828 success();
1776 } 1829 }
1777 } 1830 }
1778} 1831}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 8e3d39c38..55d072e96 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -28,6 +28,7 @@ class MicroProfileDialog;
28class ProfilerWidget; 28class ProfilerWidget;
29class QLabel; 29class QLabel;
30class QPushButton; 30class QPushButton;
31class QProgressDialog;
31class WaitTreeWidget; 32class WaitTreeWidget;
32enum class GameListOpenTarget; 33enum class GameListOpenTarget;
33class GameListPlaceholder; 34class GameListPlaceholder;
@@ -272,6 +273,9 @@ private:
272 273
273 HotkeyRegistry hotkey_registry; 274 HotkeyRegistry hotkey_registry;
274 275
276 // Install to NAND progress dialog
277 QProgressDialog* install_progress;
278
275protected: 279protected:
276 void dropEvent(QDropEvent* event) override; 280 void dropEvent(QDropEvent* event) override;
277 void dragEnterEvent(QDragEnterEvent* event) override; 281 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index bee6e107e..c3a1d715e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -130,7 +130,7 @@
130 <bool>true</bool> 130 <bool>true</bool>
131 </property> 131 </property>
132 <property name="text"> 132 <property name="text">
133 <string>Install File to NAND...</string> 133 <string>Install Files to NAND...</string>
134 </property> 134 </property>
135 </action> 135 </action>
136 <action name="action_Load_File"> 136 <action name="action_Load_File">