2022-08-30 17:18:51 -04:00
/*
2023-01-06 15:36:05 -05:00
* 86 Box 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 86 Box 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 86 Box development team
2022-11-19 08:49:04 -05:00
*/
2022-08-30 17:18:51 -04: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 " {
2022-10-01 17:39:03 -04:00
# include <86box/timer.h>
# include <86box/cdrom.h>
# include <86box/fdd.h>
}
2022-08-30 17:18:51 -04:00
namespace ui {
2022-11-19 08:49:04 -05:00
MediaHistoryManager : : MediaHistoryManager ( )
{
2022-08-30 17:18:51 -04:00
initializeImageHistory ( ) ;
deserializeAllImageHistory ( ) ;
initialDeduplication ( ) ;
}
MediaHistoryManager : : ~ MediaHistoryManager ( )
2022-11-19 08:49:04 -05:00
= default ;
2022-08-30 17:18:51 -04:00
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 ) {
2022-08-30 17:18:51 -04:00
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 + + ) {
2022-08-30 17:18:51 -04:00
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 &
2022-08-30 17:18:51 -04:00
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 ( ) ) ) {
2022-08-30 17:18:51 -04:00
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 ( ) ) ) ;
2022-08-30 17:18:51 -04:00
}
}
// 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 )
2022-08-30 17:18:51 -04:00
{
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 ;
2022-08-30 17:18:51 -04:00
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 ( ) ) ) ;
2022-08-30 17:18:51 -04:00
}
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 )
2022-08-30 17:18:51 -04:00
{
return type = = ui : : MediaType : : Cassette ? 1 : 4 ;
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager : : deserializeImageHistoryType ( ui : : MediaType type )
2022-08-30 17:18:51 -04:00
{
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 ) {
2022-08-30 17:18:51 -04:00
// 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 + + ) {
2022-08-30 17:18:51 -04:00
master_list [ type ] [ device ] [ slot ] = device_history_ptr [ slot ] ;
}
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager : : deserializeAllImageHistory ( )
2022-08-30 17:18:51 -04:00
{
2022-11-19 08:49:04 -05:00
for ( const auto device_type : ui : : AllSupportedMediaHistoryTypes ) {
2022-08-30 17:18:51 -04:00
deserializeImageHistoryType ( device_type ) ;
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager : : serializeImageHistoryType ( ui : : MediaType type )
2022-08-30 17:18:51 -04:00
{
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 ) {
2022-08-30 17:18:51 -04:00
// 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 + + ) {
2022-09-12 09:48:14 -04:00
if ( device_history_ptr [ slot ] ! = nullptr ) {
strncpy ( device_history_ptr [ slot ] , master_list [ type ] [ device ] [ slot ] . toUtf8 ( ) . constData ( ) , MAX_IMAGE_PATH_LEN ) ;
}
2022-08-30 17:18:51 -04:00
}
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager : : serializeAllImageHistory ( )
2022-08-30 17:18:51 -04:00
{
2022-11-19 08:49:04 -05:00
for ( const auto device_type : ui : : AllSupportedMediaHistoryTypes ) {
2022-08-30 17:18:51 -04:00
serializeImageHistoryType ( device_type ) ;
}
}
2022-11-19 08:49:04 -05:00
void
MediaHistoryManager : : initialDeduplication ( )
2022-08-30 17:18:51 -04:00
{
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 ) {
2022-08-30 17:18:51 -04:00
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 ;
2022-10-01 17:39:03 -04:00
case ui : : MediaType : : Floppy :
current_image = floppyfns [ device_index ] ;
break ;
2022-08-30 17:18:51 -04:00
default :
continue ;
break ;
}
2022-11-19 08:49:04 -05:00
deduplicateList ( device_history , QVector < QString > ( 1 , current_image ) ) ;
2022-08-30 17:18:51 -04:00
// Fill in missing, if any
int missing = MAX_PREV_IMAGES - device_history . size ( ) ;
2022-11-19 08:49:04 -05:00
if ( missing ) {
2022-08-30 17:18:51 -04:00
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 )
2022-08-30 17:18:51 -04:00
{
switch ( type ) {
case ui : : MediaType : : Optical :
return & cdrom [ index ] . image_history [ 0 ] ;
2022-10-01 17:39:03 -04:00
case ui : : MediaType : : Floppy :
return & fdd_image_history [ index ] [ 0 ] ;
2022-08-30 17:18:51 -04:00
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 )
2022-08-30 17:18:51 -04:00
{
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-08-30 17:18:51 -04:00
}
2022-11-19 08:49:04 -05:00
for ( const auto & path_to_check : filenames ) {
if ( path_to_check . isEmpty ( ) ) {
continue ;
2022-08-30 17:18:51 -04:00
}
QString adjusted_path = pathAdjustSingle ( path_to_check ) ;
2022-11-19 08:49:04 -05:00
int match = QString : : localeAwareCompare ( list_item_path , adjusted_path ) ;
2022-08-30 17:18:51 -04:00
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 ) {
2022-08-30 17:18:51 -04:00
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 )
2022-08-30 17:18:51 -04:00
{
device_index_list_t device_history = getHistoryListForDeviceIndex ( index , type ) ;
2022-11-19 08:49:04 -05:00
QVector < QString > files_to_check ;
2022-08-30 17:18:51 -04:00
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 ) {
2022-08-30 17:18:51 -04:00
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 ) {
2022-08-30 17:18:51 -04:00
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 )
2022-08-30 17:18:51 -04:00
{
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 ( ) ;
2022-08-30 17:18:51 -04:00
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 ( )
2022-08-30 17:18:51 -04:00
{
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
2022-08-31 19:46:36 -04:00
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 ( ) ) {
2022-08-30 17:18:51 -04:00
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 ( )
2022-08-30 17:18:51 -04:00
{
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