mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 22:35:39 +00:00
CDImagePBP: Add multi-disc support
This commit is contained in:
parent
50d712c3fe
commit
e83f312928
|
@ -1662,19 +1662,28 @@ DEFINE_JNI_ARGS_METHOD(jstring, AndroidHostInterface_importBIOSImage, jobject ob
|
||||||
return env->NewStringUTF(hash.ToString().c_str());
|
return env->NewStringUTF(hash.ToString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(jobjectArray, AndroidHostInterface_getMediaPlaylistPaths, jobject obj)
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_hasMediaSubImages, jobject obj)
|
||||||
|
{
|
||||||
|
if (!System::IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return System::HasMediaSubImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jobjectArray, AndroidHostInterface_getMediaSubImageTitles, jobject obj)
|
||||||
{
|
{
|
||||||
if (!System::IsValid())
|
if (!System::IsValid())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const u32 count = System::GetMediaPlaylistCount();
|
const u32 count = System::GetMediaSubImageCount();
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
jobjectArray arr = env->NewObjectArray(static_cast<jsize>(count), s_String_class, nullptr);
|
jobjectArray arr = env->NewObjectArray(static_cast<jsize>(count), s_String_class, nullptr);
|
||||||
for (u32 i = 0; i < count; i++)
|
for (u32 i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
jstring str = env->NewStringUTF(System::GetMediaPlaylistPath(i).c_str());
|
jstring str = env->NewStringUTF(System::GetMediaSubImageTitle(i).c_str());
|
||||||
env->SetObjectArrayElement(arr, static_cast<jsize>(i), str);
|
env->SetObjectArrayElement(arr, static_cast<jsize>(i), str);
|
||||||
env->DeleteLocalRef(str);
|
env->DeleteLocalRef(str);
|
||||||
}
|
}
|
||||||
|
@ -1682,24 +1691,24 @@ DEFINE_JNI_ARGS_METHOD(jobjectArray, AndroidHostInterface_getMediaPlaylistPaths,
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getMediaPlaylistIndex, jobject obj)
|
DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getMediaSubImageIndex, jobject obj)
|
||||||
{
|
{
|
||||||
if (!System::IsValid())
|
if (!System::IsValid())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return System::GetMediaPlaylistIndex();
|
return System::GetMediaSubImageIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_setMediaPlaylistIndex, jobject obj, jint index)
|
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_switchMediaSubImage, jobject obj, jint index)
|
||||||
{
|
{
|
||||||
if (!System::IsValid() || index < 0 || static_cast<u32>(index) >= System::GetMediaPlaylistCount())
|
if (!System::IsValid() || index < 0 || static_cast<u32>(index) >= System::GetMediaSubImageCount())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
hi->RunOnEmulationThread([index, hi]() {
|
hi->RunOnEmulationThread([index, hi]() {
|
||||||
if (System::IsValid())
|
if (System::IsValid())
|
||||||
{
|
{
|
||||||
if (!System::SwitchMediaFromPlaylist(index))
|
if (!System::SwitchMediaSubImage(static_cast<u32>(index)))
|
||||||
hi->AddOSDMessage("Disc switch failed. Please make sure the file exists.");
|
hi->AddOSDMessage("Disc switch failed. Please make sure the file exists.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -139,11 +139,13 @@ public class AndroidHostInterface {
|
||||||
|
|
||||||
public native void setFastForwardEnabled(boolean enabled);
|
public native void setFastForwardEnabled(boolean enabled);
|
||||||
|
|
||||||
public native String[] getMediaPlaylistPaths();
|
public native boolean hasMediaSubImages();
|
||||||
|
|
||||||
public native int getMediaPlaylistIndex();
|
public native String[] getMediaSubImageTitles();
|
||||||
|
|
||||||
public native boolean setMediaPlaylistIndex(int index);
|
public native int getMediaSubImageIndex();
|
||||||
|
|
||||||
|
public native boolean switchMediaSubImage(int index);
|
||||||
|
|
||||||
public native boolean setMediaFilename(String filename);
|
public native boolean setMediaFilename(String filename);
|
||||||
|
|
||||||
|
|
|
@ -690,9 +690,23 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startDiscChangeFromFile() {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.setType("*/*");
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_CHANGE_DISC_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
private void showDiscChangeMenu() {
|
private void showDiscChangeMenu() {
|
||||||
final String[] paths = AndroidHostInterface.getInstance().getMediaPlaylistPaths();
|
final AndroidHostInterface hi = AndroidHostInterface.getInstance();
|
||||||
final int currentPath = AndroidHostInterface.getInstance().getMediaPlaylistIndex();
|
|
||||||
|
if (!hi.hasMediaSubImages()) {
|
||||||
|
startDiscChangeFromFile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] paths = AndroidHostInterface.getInstance().getMediaSubImageTitles();
|
||||||
|
final int currentPath = AndroidHostInterface.getInstance().getMediaSubImageIndex();
|
||||||
final int numPaths = (paths != null) ? paths.length : 0;
|
final int numPaths = (paths != null) ? paths.length : 0;
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
@ -707,12 +721,9 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
onMenuClosed();
|
onMenuClosed();
|
||||||
|
|
||||||
if (i < numPaths) {
|
if (i < numPaths) {
|
||||||
AndroidHostInterface.getInstance().setMediaPlaylistIndex(i);
|
AndroidHostInterface.getInstance().switchMediaSubImage(i);
|
||||||
} else {
|
} else {
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
startDiscChangeFromFile();
|
||||||
intent.setType("*/*");
|
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_CHANGE_DISC_FILE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||||
|
|
|
@ -307,6 +307,17 @@ std::string CDImage::GetSubImageMetadata(u32 index, const std::string_view& type
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDImage::ClearTOC()
|
||||||
|
{
|
||||||
|
m_lba_count = 0;
|
||||||
|
m_indices.clear();
|
||||||
|
m_tracks.clear();
|
||||||
|
m_current_index = nullptr;
|
||||||
|
m_position_in_index = 0;
|
||||||
|
m_position_in_track = 0;
|
||||||
|
m_position_on_disc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void CDImage::CopyTOC(const CDImage* image)
|
void CDImage::CopyTOC(const CDImage* image)
|
||||||
{
|
{
|
||||||
m_lba_count = image->m_lba_count;
|
m_lba_count = image->m_lba_count;
|
||||||
|
|
|
@ -280,6 +280,7 @@ public:
|
||||||
virtual std::string GetSubImageMetadata(u32 index, const std::string_view& type) const;
|
virtual std::string GetSubImageMetadata(u32 index, const std::string_view& type) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void ClearTOC();
|
||||||
void CopyTOC(const CDImage* image);
|
void CopyTOC(const CDImage* image);
|
||||||
|
|
||||||
const Index* GetIndexForDiscPosition(LBA pos);
|
const Index* GetIndexForDiscPosition(LBA pos);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "assert.h"
|
||||||
#include "cd_image.h"
|
#include "cd_image.h"
|
||||||
#include "cd_subchannel_replacement.h"
|
#include "cd_subchannel_replacement.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
@ -25,6 +26,13 @@ public:
|
||||||
bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
|
bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
|
||||||
bool HasNonStandardSubchannel() const override;
|
bool HasNonStandardSubchannel() const override;
|
||||||
|
|
||||||
|
bool HasSubImages() const override;
|
||||||
|
u32 GetSubImageCount() const override;
|
||||||
|
u32 GetCurrentSubImage() const override;
|
||||||
|
bool SwitchSubImage(u32 index, Common::Error* error) override;
|
||||||
|
std::string GetMetadata(const std::string_view& type) const override;
|
||||||
|
std::string GetSubImageMetadata(u32 index, const std::string_view& type) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
|
bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
|
||||||
|
|
||||||
|
@ -54,6 +62,8 @@ private:
|
||||||
|
|
||||||
bool OpenDisc(u32 index, Common::Error* error);
|
bool OpenDisc(u32 index, Common::Error* error);
|
||||||
|
|
||||||
|
static const std::string* LookupStringSFOTableEntry(const char* key, const SFOTable& table);
|
||||||
|
|
||||||
FILE* m_file = nullptr;
|
FILE* m_file = nullptr;
|
||||||
|
|
||||||
PBPHeader m_pbp_header;
|
PBPHeader m_pbp_header;
|
||||||
|
@ -63,6 +73,7 @@ private:
|
||||||
|
|
||||||
// Absolute offsets to ISO headers, size is the number of discs in the file
|
// Absolute offsets to ISO headers, size is the number of discs in the file
|
||||||
std::vector<u32> m_disc_offsets;
|
std::vector<u32> m_disc_offsets;
|
||||||
|
u32 m_current_disc = 0;
|
||||||
|
|
||||||
// Absolute offsets and sizes of blocks in m_file
|
// Absolute offsets and sizes of blocks in m_file
|
||||||
std::array<BlockInfo, BLOCK_TABLE_NUM_ENTRIES> m_blockinfo_table;
|
std::array<BlockInfo, BLOCK_TABLE_NUM_ENTRIES> m_blockinfo_table;
|
||||||
|
@ -537,6 +548,7 @@ bool CDImagePBP::OpenDisc(u32 index, Common::Error* error)
|
||||||
// that isn't 2 seconds long. We don't have a good way to validate this, and have to assume the TOC is giving us
|
// that isn't 2 seconds long. We don't have a good way to validate this, and have to assume the TOC is giving us
|
||||||
// correct pregap lengths...
|
// correct pregap lengths...
|
||||||
|
|
||||||
|
ClearTOC();
|
||||||
m_lba_count = sectors_on_file;
|
m_lba_count = sectors_on_file;
|
||||||
LBA track1_pregap_frames = 0;
|
LBA track1_pregap_frames = 0;
|
||||||
for (u32 curr_track = 1; curr_track <= last_track; curr_track++)
|
for (u32 curr_track = 1; curr_track <= last_track; curr_track++)
|
||||||
|
@ -654,9 +666,23 @@ bool CDImagePBP::OpenDisc(u32 index, Common::Error* error)
|
||||||
else
|
else
|
||||||
m_sbi.LoadSBI(FileSystem::ReplaceExtension(m_filename, "sbi").c_str());
|
m_sbi.LoadSBI(FileSystem::ReplaceExtension(m_filename, "sbi").c_str());
|
||||||
|
|
||||||
|
m_current_disc = index;
|
||||||
return Seek(1, Position{0, 0, 0});
|
return Seek(1, Position{0, 0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string* CDImagePBP::LookupStringSFOTableEntry(const char* key, const SFOTable& table)
|
||||||
|
{
|
||||||
|
auto iter = table.find(key);
|
||||||
|
if (iter == table.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const SFOTableDataValue& data_value = iter->second;
|
||||||
|
if (!std::holds_alternative<std::string>(data_value))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &std::get<std::string>(data_value);
|
||||||
|
}
|
||||||
|
|
||||||
bool CDImagePBP::InitDecompressionStream()
|
bool CDImagePBP::InitDecompressionStream()
|
||||||
{
|
{
|
||||||
m_inflate_stream = {};
|
m_inflate_stream = {};
|
||||||
|
@ -788,6 +814,61 @@ void CDImagePBP::PrintSFOTable(const SFOTable& sfo_table)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool CDImagePBP::HasSubImages() const
|
||||||
|
{
|
||||||
|
return m_disc_offsets.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CDImagePBP::GetMetadata(const std::string_view& type) const
|
||||||
|
{
|
||||||
|
if (type == "title")
|
||||||
|
{
|
||||||
|
const std::string* title = LookupStringSFOTableEntry("TITLE", m_sfo_table);
|
||||||
|
if (title && !title->empty())
|
||||||
|
return *title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CDImage::GetMetadata(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CDImagePBP::GetSubImageCount() const
|
||||||
|
{
|
||||||
|
return static_cast<u32>(m_disc_offsets.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CDImagePBP::GetCurrentSubImage() const
|
||||||
|
{
|
||||||
|
return m_current_disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImagePBP::SwitchSubImage(u32 index, Common::Error* error)
|
||||||
|
{
|
||||||
|
if (index >= m_disc_offsets.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const u32 old_disc = m_current_disc;
|
||||||
|
if (!OpenDisc(index, error))
|
||||||
|
{
|
||||||
|
// return to old disc, this should never fail... in theory.
|
||||||
|
if (!OpenDisc(old_disc, nullptr))
|
||||||
|
Panic("Failed to reopen old disc after switch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CDImagePBP::GetSubImageMetadata(u32 index, const std::string_view& type) const
|
||||||
|
{
|
||||||
|
if (type == "title")
|
||||||
|
{
|
||||||
|
const std::string* title = LookupStringSFOTableEntry("TITLE", m_sfo_table);
|
||||||
|
if (title && !title->empty())
|
||||||
|
return StringUtil::StdStringFromFormat("%s (Disc %u)", title->c_str(), index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CDImage::GetSubImageMetadata(index, type);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CDImage> CDImage::OpenPBPImage(const char* filename, Common::Error* error)
|
std::unique_ptr<CDImage> CDImage::OpenPBPImage(const char* filename, Common::Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CDImagePBP> image = std::make_unique<CDImagePBP>();
|
std::unique_ptr<CDImagePBP> image = std::make_unique<CDImagePBP>();
|
||||||
|
|
Loading…
Reference in a new issue