Files
86Box/src/qt/qt_mediahistorymanager.cpp

343 lines
11 KiB
C++
Raw Normal View History

/*
2023-01-06 15:36:05 -05:00
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
2022-11-19 08:49:04 -05:00
*
2023-01-06 15:36:05 -05:00
* This file is part of the 86Box distribution.
2022-11-19 08:49:04 -05:00
*
2023-01-06 15:36:05 -05:00
* Media history management module
2022-11-19 08:49:04 -05:00
*
*
*
2023-01-06 15:36:05 -05:00
* Authors: cold-brewed
2022-11-19 08:49:04 -05:00
*
2023-01-06 15:36:05 -05:00
* Copyright 2022 The 86Box development team
2022-11-19 08:49:04 -05:00
*/
#include <QApplication>
#include <QFileInfo>
#include <QMetaEnum>
#include <QStringBuilder>
#include <utility>
#include "qt_mediahistorymanager.hpp"
2022-11-19 08:49:04 -05:00
extern "C" {
#include <86box/timer.h>
#include <86box/cdrom.h>
#include <86box/fdd.h>
}
namespace ui {
2022-11-19 08:49:04 -05:00
MediaHistoryManager::MediaHistoryManager()
{
initializeImageHistory();
deserializeAllImageHistory();
initialDeduplication();
}
MediaHistoryManager::~MediaHistoryManager()
2022-11-19 08:49:04 -05:00
= default;
master_list_t &
MediaHistoryManager::blankImageHistory(master_list_t &initialized_master_list) const
{
2022-11-19 08:49:04 -05:00
for (const auto device_type : ui::AllSupportedMediaHistoryTypes) {
device_media_history_t device_media_history;
// Loop for all possible media devices
2022-11-19 08:49:04 -05:00
for (int device_index = 0; device_index < maxDevicesSupported(device_type); device_index++) {
device_index_list_t indexing_list;
device_media_history[device_index] = indexing_list;
// Loop for each history slot
for (int slot_index = 0; slot_index < max_images; slot_index++) {
device_media_history[device_index].append(QString());
}
}
initialized_master_list.insert(device_type, device_media_history);
}
return initialized_master_list;
}
2022-11-19 08:49:04 -05:00
const device_index_list_t &
MediaHistoryManager::getHistoryListForDeviceIndex(int index, ui::MediaType type)
{
if (master_list.contains(type)) {
2022-11-19 08:49:04 -05:00
if ((index >= 0) && (index < master_list[type].size())) {
return master_list[type][index];
} else {
2023-06-11 14:27:14 -04:00
qWarning("Media device index %i for device type %s was requested but index %i is out of range (valid range: >= 0 && < %lli)",
index, mediaTypeToString(type).toUtf8().constData(), index, static_cast<long long>(master_list[type].size()));
}
}
// Failure gets an empty list
return empty_device_index_list;
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::setHistoryListForDeviceIndex(int index, ui::MediaType type, device_index_list_t history_list)
{
master_list[type][index] = std::move(history_list);
}
QString
MediaHistoryManager::getImageForSlot(int index, int slot, ui::MediaType type)
{
2022-11-19 08:49:04 -05:00
QString image_name;
device_index_list_t device_history = getHistoryListForDeviceIndex(index, type);
if ((slot >= 0) && (slot < device_history.size())) {
image_name = device_history[slot];
} else {
2023-06-11 14:27:14 -04:00
qWarning("Media history slot %i, index %i for device type %s was requested but slot %i is out of range (valid range: >= 0 && < %i, device_history.size() is %lli)",
slot, index, mediaTypeToString(type).toUtf8().constData(), slot, maxDevicesSupported(type), static_cast<long long>(device_history.size()));
}
return image_name;
}
// These are hardcoded since we can't include the various
// header files where they are defined (e.g., fdd.h, mo.h).
// However, all in ui::MediaType support 4 except cassette.
2022-11-19 08:49:04 -05:00
int
MediaHistoryManager::maxDevicesSupported(ui::MediaType type)
{
return type == ui::MediaType::Cassette ? 1 : 4;
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::deserializeImageHistoryType(ui::MediaType type)
{
for (int device = 0; device < maxDevicesSupported(type); device++) {
char **device_history_ptr = getEmuHistoryVarForType(type, device);
2022-11-19 08:49:04 -05:00
if (device_history_ptr == nullptr) {
// Device not supported, return and do not deserialize.
// This will leave the image listing at the default initialization state
// from the ui side (this class)
continue;
}
2022-11-19 08:49:04 -05:00
for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
master_list[type][device][slot] = device_history_ptr[slot];
}
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::deserializeAllImageHistory()
{
2022-11-19 08:49:04 -05:00
for (const auto device_type : ui::AllSupportedMediaHistoryTypes) {
deserializeImageHistoryType(device_type);
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::serializeImageHistoryType(ui::MediaType type)
{
for (int device = 0; device < maxDevicesSupported(type); device++) {
char **device_history_ptr = getEmuHistoryVarForType(type, device);
2022-11-19 08:49:04 -05:00
if (device_history_ptr == nullptr) {
// Device not supported, return and do not serialize.
// This will leave the image listing at the current state,
// and it will not be saved on the emu side
continue;
}
2022-11-19 08:49:04 -05:00
for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
if (device_history_ptr[slot] != nullptr) {
strncpy(device_history_ptr[slot], master_list[type][device][slot].toUtf8().constData(), MAX_IMAGE_PATH_LEN);
}
}
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::serializeAllImageHistory()
{
2022-11-19 08:49:04 -05:00
for (const auto device_type : ui::AllSupportedMediaHistoryTypes) {
serializeImageHistoryType(device_type);
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::initialDeduplication()
{
QString current_image;
// Perform initial dedup if an image is loaded
2022-11-19 08:49:04 -05:00
for (const auto device_type : ui::AllSupportedMediaHistoryTypes) {
for (int device_index = 0; device_index < maxDevicesSupported(device_type); device_index++) {
device_index_list_t device_history = getHistoryListForDeviceIndex(device_index, device_type);
switch (device_type) {
case ui::MediaType::Optical:
current_image = cdrom[device_index].image_path;
break;
case ui::MediaType::Floppy:
current_image = floppyfns[device_index];
break;
default:
continue;
break;
}
2022-11-19 08:49:04 -05:00
deduplicateList(device_history, QVector<QString>(1, current_image));
// Fill in missing, if any
int missing = MAX_PREV_IMAGES - device_history.size();
2022-11-19 08:49:04 -05:00
if (missing) {
for (int i = 0; i < missing; i++) {
device_history.push_back(QString());
}
}
setHistoryListForDeviceIndex(device_index, device_type, device_history);
}
}
}
2022-11-19 08:49:04 -05:00
char **
MediaHistoryManager::getEmuHistoryVarForType(ui::MediaType type, int index)
{
switch (type) {
case ui::MediaType::Optical:
return &cdrom[index].image_history[0];
case ui::MediaType::Floppy:
return &fdd_image_history[index][0];
default:
return nullptr;
}
}
device_index_list_t &
2022-11-19 08:49:04 -05:00
MediaHistoryManager::deduplicateList(device_index_list_t &device_history, const QVector<QString> &filenames)
{
QVector<QString> items_to_delete;
for (auto &list_item_path : device_history) {
2022-11-19 08:49:04 -05:00
if (list_item_path.isEmpty()) {
continue;
}
2022-11-19 08:49:04 -05:00
for (const auto &path_to_check : filenames) {
if (path_to_check.isEmpty()) {
continue;
}
QString adjusted_path = pathAdjustSingle(path_to_check);
2022-11-19 08:49:04 -05:00
int match = QString::localeAwareCompare(list_item_path, adjusted_path);
if (match == 0) {
items_to_delete.append(list_item_path);
}
}
}
// Remove by name rather than index because the index would change
// after each removal
2022-11-19 08:49:04 -05:00
for (const auto &path : items_to_delete) {
device_history.removeAll(path);
}
return device_history;
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::addImageToHistory(int index, ui::MediaType type, const QString &image_name, const QString &new_image_name)
{
device_index_list_t device_history = getHistoryListForDeviceIndex(index, type);
2022-11-19 08:49:04 -05:00
QVector<QString> files_to_check;
files_to_check.append(image_name);
files_to_check.append(new_image_name);
device_history = deduplicateList(device_history, files_to_check);
if (!image_name.isEmpty()) {
device_history.push_front(image_name);
}
// Pop any extras
2023-05-29 01:30:51 -04:00
if (device_history.size() > MAX_PREV_IMAGES) {
device_history.pop_back();
}
// Fill in missing, if any
int missing = MAX_PREV_IMAGES - device_history.size();
2022-11-19 08:49:04 -05:00
if (missing) {
for (int i = 0; i < missing; i++) {
device_history.push_back(QString());
}
}
device_history = removeMissingImages(device_history);
device_history = pathAdjustFull(device_history);
setHistoryListForDeviceIndex(index, type, device_history);
serializeImageHistoryType(type);
}
2022-11-19 08:49:04 -05:00
QString
MediaHistoryManager::mediaTypeToString(ui::MediaType type)
{
QMetaEnum qme = QMetaEnum::fromType<ui::MediaType>();
return qme.valueToKey(static_cast<int>(type));
}
QString
MediaHistoryManager::pathAdjustSingle(QString checked_path)
{
2022-11-19 08:49:04 -05:00
QString current_usr_path = getUsrPath();
QFileInfo file_info(checked_path);
if (file_info.filePath().isEmpty() || current_usr_path.isEmpty() || file_info.isRelative()) {
return checked_path;
}
if (file_info.filePath().startsWith(current_usr_path)) {
checked_path = file_info.filePath().remove(current_usr_path);
}
return checked_path;
}
device_index_list_t &
MediaHistoryManager::pathAdjustFull(device_index_list_t &device_history)
{
for (auto &checked_path : device_history) {
checked_path = pathAdjustSingle(checked_path);
}
return device_history;
}
2022-11-19 08:49:04 -05:00
QString
MediaHistoryManager::getUsrPath()
{
QString current_usr_path(usr_path);
// Ensure `usr_path` has a trailing slash
return current_usr_path.endsWith("/") ? current_usr_path : current_usr_path.append("/");
}
device_index_list_t &
MediaHistoryManager::removeMissingImages(device_index_list_t &device_history)
{
for (auto &checked_path : device_history) {
QFileInfo file_info(checked_path);
if (file_info.filePath().isEmpty()) {
continue;
}
// For this check, explicitly prepend `usr_path` to relative paths to account for $CWD platform variances
QFileInfo absolute_path = file_info.isRelative() ? QFileInfo(getUsrPath().append(file_info.filePath())) : file_info;
2024-05-19 21:17:57 +02:00
if ((file_info.filePath().left(8) != "ioctl://") && !absolute_path.exists()) {
qWarning("Image file %s does not exist - removing from history", qPrintable(file_info.filePath()));
checked_path = "";
}
}
return device_history;
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager::initializeImageHistory()
{
auto initial_master_list = getMasterList();
setMasterList(blankImageHistory(initial_master_list));
}
const master_list_t &
MediaHistoryManager::getMasterList() const
{
return master_list;
}
void
MediaHistoryManager::setMasterList(const master_list_t &masterList)
{
master_list = masterList;
}
void
MediaHistoryManager::clearImageHistory()
{
initializeImageHistory();
serializeAllImageHistory();
}
} // ui