mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-08 19:32:39 +08:00
Add preliminary QCow2 image support. Supports backing files only with aboslute paths. No compression, encryption, or snapshots.
This commit is contained in:
parent
de057369ce
commit
fe0974b9a3
10
aclocal.m4
vendored
10
aclocal.m4
vendored
@ -103,10 +103,9 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
|
||||
# configured tree to be moved without reconfiguration.
|
||||
|
||||
AC_DEFUN([AM_AUX_DIR_EXPAND],
|
||||
[dnl Rely on autoconf to set up CDPATH properly.
|
||||
AC_PREREQ([2.50])dnl
|
||||
# expand $ac_aux_dir to an absolute path
|
||||
am_aux_dir=`cd $ac_aux_dir && pwd`
|
||||
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
|
||||
# Expand $ac_aux_dir to an absolute path.
|
||||
am_aux_dir=`cd "$ac_aux_dir" && pwd`
|
||||
])
|
||||
|
||||
# AM_CONDITIONAL -*- Autoconf -*-
|
||||
@ -573,7 +572,8 @@ to "yes", and re-run configure.
|
||||
END
|
||||
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
|
||||
fi
|
||||
fi])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
|
||||
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
|
||||
|
5
configure
vendored
5
configure
vendored
@ -2944,8 +2944,8 @@ test "$program_suffix" != NONE &&
|
||||
ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
|
||||
program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
|
||||
|
||||
# expand $ac_aux_dir to an absolute path
|
||||
am_aux_dir=`cd $ac_aux_dir && pwd`
|
||||
# Expand $ac_aux_dir to an absolute path.
|
||||
am_aux_dir=`cd "$ac_aux_dir" && pwd`
|
||||
|
||||
if test x"${MISSING+set}" != xset; then
|
||||
case $am_aux_dir in
|
||||
@ -3350,6 +3350,7 @@ END
|
||||
as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
|
||||
fi
|
||||
fi
|
||||
|
||||
ac_config_headers="$ac_config_headers config.h"
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@ paging.h \
|
||||
pci_bus.h \
|
||||
pic.h \
|
||||
programs.h \
|
||||
qcow2_disk.h \
|
||||
render.h \
|
||||
regs.h \
|
||||
render.h \
|
||||
|
@ -268,6 +268,7 @@ paging.h \
|
||||
pci_bus.h \
|
||||
pic.h \
|
||||
programs.h \
|
||||
qcow2_disk.h \
|
||||
render.h \
|
||||
regs.h \
|
||||
render.h \
|
||||
|
140
include/qcow2_disk.h
Normal file
140
include/qcow2_disk.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Michael Greger
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DOSBOX_QCOW2_DISK_H
|
||||
#define DOSBOX_QCOW2_DISK_H
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
#include "bios_disk.h"
|
||||
|
||||
class QCow2Image{
|
||||
|
||||
public:
|
||||
|
||||
static const Bit32u magic = 0x514649FB;
|
||||
|
||||
typedef struct QCow2Header {
|
||||
Bit32u magic;
|
||||
Bit32u version;
|
||||
Bit64u backing_file_offset;
|
||||
Bit32u backing_file_size;
|
||||
Bit32u cluster_bits;
|
||||
Bit64u size; /* in bytes */
|
||||
Bit32u crypt_method;
|
||||
Bit32u l1_size;
|
||||
Bit64u l1_table_offset;
|
||||
Bit64u refcount_table_offset;
|
||||
Bit32u refcount_table_clusters;
|
||||
Bit32u nb_snapshots;
|
||||
Bit64u snapshots_offset;
|
||||
} QCow2Header;
|
||||
|
||||
static QCow2Header read_header(FILE* qcow2File);
|
||||
|
||||
QCow2Image(QCow2Header qcow2_header, FILE *qcow2File);
|
||||
|
||||
virtual ~QCow2Image();
|
||||
|
||||
Bit8u read_sector(Bit32u sectnum, Bit8u* data);
|
||||
|
||||
Bit8u write_sector(Bit32u sectnum, Bit8u* data);
|
||||
|
||||
private:
|
||||
|
||||
static Bit16u host_read16(Bit16u buffer);
|
||||
|
||||
static Bit32u host_read32(Bit32u buffer);
|
||||
|
||||
static Bit64u host_read64(Bit64u buffer);
|
||||
|
||||
static Bit64u mask64(Bit64u bits);
|
||||
|
||||
Bit8u pad_file(Bit64u& new_file_length);
|
||||
|
||||
Bit8u read_allocated_data(Bit64u file_offset, Bit8u* data, Bit64u data_size);
|
||||
|
||||
Bit8u read_cluster(Bit64u data_cluster_number, Bit8u* data);
|
||||
|
||||
Bit8u read_l1_table(Bit64u address, Bit64u& l2_table_offset);
|
||||
|
||||
Bit8u read_l2_table(Bit64u l2_table_offset, Bit64u address, Bit64u& data_cluster_offset);
|
||||
|
||||
Bit8u read_refcount_table(Bit64u data_cluster_offset, Bit64u& refcount_cluster_offset);
|
||||
|
||||
Bit8u read_table(Bit64u entry_offset, Bit64u entry_mask, Bit64u& entry_value);
|
||||
|
||||
Bit8u read_unallocated_cluster(Bit64u data_cluster_number, Bit8u* data);
|
||||
|
||||
Bit8u read_unallocated_sector(Bit32u sectnum, Bit8u* data);
|
||||
|
||||
Bit8u update_reference_count(Bit64u cluster_offset, Bit8u* cluster_buffer);
|
||||
|
||||
Bit8u write_data(Bit64u file_offset, Bit8u* data, Bit64u data_size);
|
||||
|
||||
Bit8u write_l1_table_entry(Bit64u address, Bit64u l2_table_offset);
|
||||
|
||||
Bit8u write_l2_table_entry(Bit64u l2_table_offset, Bit64u address, Bit64u data_cluster_offset);
|
||||
|
||||
Bit8u write_refcount(Bit64u cluster_offset, Bit64u refcount_cluster_offset, Bit16u refcount);
|
||||
|
||||
Bit8u write_refcount_table_entry(Bit64u cluster_offset, Bit64u refcount_cluster_offset);
|
||||
|
||||
Bit8u write_table_entry(Bit64u entry_offset, Bit64u entry_value);
|
||||
|
||||
FILE* file;
|
||||
QCow2Header header;
|
||||
static const Bit64u copy_flag = 0x8000000000000000;
|
||||
static const Bit64u empty_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
static const Bit32u sector_size = 512;
|
||||
static const Bit64u table_entry_mask = 0x00FFFFFFFFFFFFFF;
|
||||
Bit64u cluster_mask;
|
||||
Bit64u cluster_size;
|
||||
Bit64u sectors_per_cluster;
|
||||
Bit64u disk_sectors_total;
|
||||
Bit64u l2_mask;
|
||||
Bit64u l2_bits;
|
||||
Bit64u l1_bits;
|
||||
Bit64u refcount_mask;
|
||||
Bit64u refcount_bits;
|
||||
QCow2Image* backing_image = NULL;
|
||||
};
|
||||
|
||||
class QCow2Disk : public imageDisk{
|
||||
|
||||
public:
|
||||
|
||||
QCow2Disk(QCow2Image::QCow2Header qcow2_header, FILE *qcow2File, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk);
|
||||
|
||||
virtual ~QCow2Disk();
|
||||
|
||||
virtual Bit8u Read_AbsoluteSector(Bit32u sectnum, void* data);
|
||||
|
||||
virtual Bit8u Write_AbsoluteSector(Bit32u sectnum, void* data);
|
||||
|
||||
private:
|
||||
|
||||
QCow2Image qcowImage;
|
||||
};
|
||||
|
||||
#endif
|
@ -37,7 +37,8 @@
|
||||
#include "bios.h"
|
||||
#include "inout.h"
|
||||
#include "dma.h"
|
||||
#include "bios_disk.h"
|
||||
#include "bios_disk.h"
|
||||
#include "qcow2_disk.h"
|
||||
#include "setup.h"
|
||||
#include "control.h"
|
||||
#include <time.h>
|
||||
@ -2658,18 +2659,28 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *newDisk = fopen64(temp_line.c_str(), "rb+");
|
||||
fseeko64(newDisk,0L, SEEK_END);
|
||||
|
||||
/* auto-fill: sector size */
|
||||
if (sizes[0] == 0) sizes[0] = 512;
|
||||
|
||||
Bit64u sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0];
|
||||
FILE *newDisk = fopen64(temp_line.c_str(), "rb+");
|
||||
|
||||
imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */
|
||||
setbuf(newDisk,NULL);
|
||||
|
||||
newImage = new imageDisk(newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880));
|
||||
QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(newDisk);
|
||||
|
||||
Bit64u sectors;
|
||||
if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)){
|
||||
sectors = qcow2_header.size / 512; /* TODO: Currently only supporting 512 byte sectors */
|
||||
imagesize = (Bit32u)(sectors / 2);
|
||||
setbuf(newDisk,NULL);
|
||||
newImage = new QCow2Disk(qcow2_header, newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880));
|
||||
}
|
||||
else{
|
||||
fseeko64(newDisk,0L, SEEK_END);
|
||||
sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0];
|
||||
imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */
|
||||
setbuf(newDisk,NULL);
|
||||
newImage = new imageDisk(newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880));
|
||||
}
|
||||
|
||||
newImage->Addref();
|
||||
|
||||
/* auto-fill: sector/track count */
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "cross.h"
|
||||
#include "bios.h"
|
||||
#include "bios_disk.h"
|
||||
#include "qcow2_disk.h"
|
||||
|
||||
#define IMGTYPE_FLOPPY 0
|
||||
#define IMGTYPE_ISO 1
|
||||
@ -654,11 +655,19 @@ fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector,
|
||||
|
||||
diskfile = fopen64(sysFilename, "rb+");
|
||||
if(!diskfile) {created_successfully = false;return;}
|
||||
fseeko64(diskfile, 0L, SEEK_END);
|
||||
filesize = (Bit32u)(ftello64(diskfile) / 1024L);
|
||||
|
||||
QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(diskfile);
|
||||
|
||||
if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)){
|
||||
filesize = (Bit32u)(qcow2_header.size / 1024L);
|
||||
loadedDisk = new QCow2Disk(qcow2_header, diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
|
||||
}
|
||||
else{
|
||||
fseeko64(diskfile, 0L, SEEK_END);
|
||||
filesize = (Bit32u)(ftello64(diskfile) / 1024L);
|
||||
loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
|
||||
}
|
||||
|
||||
/* Load disk image */
|
||||
loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
|
||||
if(!loadedDisk) {
|
||||
created_successfully = false;
|
||||
return;
|
||||
|
@ -4,4 +4,4 @@ noinst_LIBRARIES = libints.a
|
||||
libints_a_SOURCES = mouse.cpp xms.cpp xms.h ems.cpp \
|
||||
int10.cpp int10.h int10_char.cpp int10_memory.cpp int10_misc.cpp int10_modes.cpp \
|
||||
int10_vesa.cpp int10_pal.cpp int10_put_pixel.cpp int10_video_state.cpp int10_vptable.cpp \
|
||||
bios.cpp bios_disk.cpp bios_keyboard.cpp
|
||||
bios.cpp bios_disk.cpp bios_keyboard.cpp qcow2_disk.cpp
|
||||
|
@ -105,7 +105,7 @@ am_libints_a_OBJECTS = mouse.$(OBJEXT) xms.$(OBJEXT) ems.$(OBJEXT) \
|
||||
int10_vesa.$(OBJEXT) int10_pal.$(OBJEXT) \
|
||||
int10_put_pixel.$(OBJEXT) int10_video_state.$(OBJEXT) \
|
||||
int10_vptable.$(OBJEXT) bios.$(OBJEXT) bios_disk.$(OBJEXT) \
|
||||
bios_keyboard.$(OBJEXT)
|
||||
bios_keyboard.$(OBJEXT) qcow2_disk.$(OBJEXT)
|
||||
libints_a_OBJECTS = $(am_libints_a_OBJECTS)
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
@ -287,7 +287,7 @@ noinst_LIBRARIES = libints.a
|
||||
libints_a_SOURCES = mouse.cpp xms.cpp xms.h ems.cpp \
|
||||
int10.cpp int10.h int10_char.cpp int10_memory.cpp int10_misc.cpp int10_modes.cpp \
|
||||
int10_vesa.cpp int10_pal.cpp int10_put_pixel.cpp int10_video_state.cpp int10_vptable.cpp \
|
||||
bios.cpp bios_disk.cpp bios_keyboard.cpp
|
||||
bios.cpp bios_disk.cpp bios_keyboard.cpp qcow2_disk.cpp
|
||||
|
||||
all: all-am
|
||||
|
||||
@ -353,6 +353,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/int10_video_state.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/int10_vptable.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mouse.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qcow2_disk.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xms.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
|
@ -16,7 +16,6 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "callback.h"
|
||||
#include "bios.h"
|
||||
@ -65,7 +64,6 @@ Bits swapPosition;
|
||||
imageDisk *GetINT13FloppyDrive(unsigned char drv) {
|
||||
if (drv >= 2)
|
||||
return NULL;
|
||||
|
||||
return imageDiskList[drv];
|
||||
}
|
||||
|
||||
|
422
src/ints/qcow2_disk.cpp
Normal file
422
src/ints/qcow2_disk.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Michael Greger
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "qcow2_disk.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
//Public function to read a QCow2 header.
|
||||
QCow2Image::QCow2Header QCow2Image::read_header(FILE* qcow2File){
|
||||
QCow2Header header = QCow2Header();
|
||||
if (1 != fseek(qcow2File, 0, SEEK_SET)){
|
||||
clearerr(qcow2File);
|
||||
}
|
||||
fread(&header, sizeof header, 1, qcow2File);
|
||||
header.magic = host_read32(header.magic);
|
||||
header.version = host_read32(header.version);
|
||||
header.backing_file_offset = host_read64(header.backing_file_offset);
|
||||
header.backing_file_size = host_read32(header.backing_file_size);
|
||||
header.cluster_bits = host_read32(header.cluster_bits);
|
||||
header.size = host_read64(header.size);
|
||||
header.crypt_method = host_read32(header.crypt_method);
|
||||
header.l1_size = host_read32(header.l1_size);
|
||||
header.l1_table_offset = host_read64(header.l1_table_offset);
|
||||
header.refcount_table_offset = host_read64(header.refcount_table_offset);
|
||||
header.refcount_table_clusters = host_read32(header.refcount_table_clusters);
|
||||
header.nb_snapshots = host_read32(header.nb_snapshots);
|
||||
header.snapshots_offset = host_read64(header.snapshots_offset);
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
//Public Constructor.
|
||||
QCow2Image::QCow2Image(QCow2Image::QCow2Header qcow2_header, FILE *qcow2File) : file(qcow2File), header(qcow2_header)
|
||||
{
|
||||
cluster_mask = mask64(header.cluster_bits);
|
||||
cluster_size = cluster_mask + 1;
|
||||
sectors_per_cluster = cluster_size / sector_size;
|
||||
disk_sectors_total = header.size/sector_size;
|
||||
l2_bits = header.cluster_bits - 3;
|
||||
l2_mask = mask64(l2_bits);
|
||||
l1_bits = header.cluster_bits + l2_bits;
|
||||
refcount_bits = header.cluster_bits - 1;
|
||||
refcount_mask = mask64(refcount_bits);
|
||||
if (header.backing_file_offset != 0 && header.backing_file_size != 0){
|
||||
char* backing_file_name = new char[header.backing_file_size];
|
||||
fseek(file, header.backing_file_offset, SEEK_SET);
|
||||
fread(backing_file_name, header.backing_file_size, 1, file);
|
||||
FILE* backing_file = fopen(backing_file_name, "rb");
|
||||
QCow2Header backing_header = read_header(backing_file);
|
||||
backing_image = new QCow2Image(backing_header, backing_file);
|
||||
delete[] backing_file_name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Public Destructor.
|
||||
QCow2Image::~QCow2Image(){
|
||||
if (backing_image != NULL){
|
||||
fclose(backing_image->file);
|
||||
delete backing_image;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Public function to a read a sector.
|
||||
Bit8u QCow2Image::read_sector(Bit32u sectnum, Bit8u* data){
|
||||
const Bit64u address = (Bit64u)sectnum * sector_size;
|
||||
if (address >= header.size){
|
||||
return 0x05;
|
||||
}
|
||||
Bit64u l2_table_offset;
|
||||
if (0 != read_l1_table(address, l2_table_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == l2_table_offset){
|
||||
return read_unallocated_sector(sectnum, data);
|
||||
}
|
||||
Bit64u data_cluster_offset;
|
||||
if (0 != read_l2_table(l2_table_offset, address, data_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == data_cluster_offset){
|
||||
return read_unallocated_sector(sectnum, data);
|
||||
}
|
||||
return read_allocated_data(data_cluster_offset + (address & cluster_mask), data, sector_size);
|
||||
}
|
||||
|
||||
|
||||
//Public function to a write a sector.
|
||||
Bit8u QCow2Image::write_sector(Bit32u sectnum, Bit8u* data){
|
||||
const Bit64u address = (Bit64u)sectnum * sector_size;
|
||||
if (address >= header.size){
|
||||
return 0x05;
|
||||
}
|
||||
Bit64u l2_table_offset;
|
||||
if (0 != read_l1_table(address, l2_table_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == l2_table_offset){
|
||||
if (0 != pad_file(l2_table_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != write_l1_table_entry(address, l2_table_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
Bit8u* cluster_buffer = new Bit8u[cluster_size];
|
||||
std::fill(cluster_buffer, cluster_buffer + cluster_size, 0);
|
||||
if (0 != write_data(l2_table_offset, cluster_buffer, cluster_size)){
|
||||
delete[] cluster_buffer;
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != update_reference_count(l2_table_offset, cluster_buffer)){
|
||||
delete[] cluster_buffer;
|
||||
return 0x05;
|
||||
}
|
||||
delete[] cluster_buffer;
|
||||
}
|
||||
Bit64u data_cluster_offset;
|
||||
if (0 != read_l2_table(l2_table_offset, address, data_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (data_cluster_offset == 0){
|
||||
if (0 != pad_file(data_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != write_l2_table_entry(l2_table_offset, address, data_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
Bit8u* cluster_buffer = new Bit8u[cluster_size];
|
||||
if ( 0 != read_unallocated_cluster(address/cluster_size, cluster_buffer)){
|
||||
delete[] cluster_buffer;
|
||||
return 0x05;
|
||||
}
|
||||
const Bit64u cluster_buffer_sector_offset = address & cluster_mask;
|
||||
for (Bit64u i = 0; i < sector_size; i++){
|
||||
cluster_buffer[cluster_buffer_sector_offset + i] = data[i];
|
||||
}
|
||||
if (0 != write_data(data_cluster_offset, cluster_buffer, cluster_size)){
|
||||
delete[] cluster_buffer;
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != update_reference_count(data_cluster_offset, cluster_buffer)){
|
||||
delete[] cluster_buffer;
|
||||
return 0x05;
|
||||
}
|
||||
delete[] cluster_buffer;
|
||||
return 0;
|
||||
}
|
||||
return write_data(data_cluster_offset + (address & cluster_mask), data, sector_size);
|
||||
}
|
||||
|
||||
|
||||
//Helper functions for endianness. QCOW format is big endian so we need different functions than those defined in mem.h.
|
||||
#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
|
||||
|
||||
|
||||
Bit16u QCow2Image::host_read16(Bit16u buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
Bit32u QCow2Image::host_read32(Bit32u buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
Bit64u QCow2Image::host_read64(Bit64u buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
Bit16u QCow2Image::host_read16(Bit16u buffer) {
|
||||
Bit8u* b = (Bit8u*)&buffer;
|
||||
return b[1] | (b[0] << 8);
|
||||
}
|
||||
|
||||
|
||||
Bit32u QCow2Image::host_read32(Bit32u buffer) {
|
||||
Bit8u* b = (Bit8u*)&buffer;
|
||||
return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
|
||||
}
|
||||
|
||||
|
||||
Bit64u QCow2Image::host_read64(Bit64u buffer) {
|
||||
Bit8u* b = (Bit8u*)&buffer;
|
||||
return b[7] | (b[6] << 8) | (b[5] << 16) | (b[4] << 24) | ((Bit64u)b[3] << 32) | ((Bit64u)b[2] << 40) | ((Bit64u)b[1] << 48) | ((Bit64u)b[0] << 56);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//Generate a mask for a given number of bits.
|
||||
Bit64u QCow2Image::mask64(Bit64u bits){
|
||||
return (1 << bits) - 1;
|
||||
}
|
||||
|
||||
|
||||
//Pad a file with zeros if it doesn't end on a cluster boundary.
|
||||
Bit8u QCow2Image::pad_file(Bit64u& new_file_length){
|
||||
if (0 != fseek(file, 0, SEEK_END)){
|
||||
return 0x05;
|
||||
}
|
||||
const Bit64u old_file_length = ftell(file);
|
||||
const Bit64u padding_size = (cluster_size - (old_file_length % cluster_size)) % cluster_size;
|
||||
new_file_length = old_file_length + padding_size;
|
||||
if (0 == padding_size){
|
||||
return 0;
|
||||
}
|
||||
Bit8u* padding = new Bit8u[padding_size];
|
||||
std::fill(padding, padding + padding_size, 0);
|
||||
Bit8u result = write_data(old_file_length, padding, padding_size);
|
||||
delete[] padding;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//Read data of arbitrary length that is present in the image file.
|
||||
Bit8u QCow2Image::read_allocated_data(Bit64u file_offset, Bit8u* data, Bit64u data_size)
|
||||
{
|
||||
if (0 != fseek(file, file_offset, SEEK_SET)){
|
||||
return 0x05;
|
||||
}
|
||||
if (1 != fread(data, data_size, 1, file)){
|
||||
return 0x05;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//Read an entire cluster that may or may not be allocated in the image file.
|
||||
inline Bit8u QCow2Image::read_cluster(Bit64u data_cluster_number, Bit8u* data)
|
||||
{
|
||||
const Bit64u address = data_cluster_number * cluster_size;
|
||||
if (address >= header.size){
|
||||
return 0x05;
|
||||
}
|
||||
Bit64u l2_table_offset;
|
||||
if (0 != read_l1_table(address, l2_table_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == l2_table_offset){
|
||||
return read_unallocated_cluster(data_cluster_number, data);
|
||||
}
|
||||
Bit64u data_cluster_offset;
|
||||
if (0 != read_l2_table(l2_table_offset, address, data_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == data_cluster_offset){
|
||||
return read_unallocated_cluster(data_cluster_number, data);
|
||||
}
|
||||
return read_allocated_data(data_cluster_offset, data, cluster_size);
|
||||
}
|
||||
|
||||
|
||||
//Read the L1 table to get the offset of the L2 table for a given address.
|
||||
inline Bit8u QCow2Image::read_l1_table(Bit64u address, Bit64u& l2_table_offset){
|
||||
const Bit64u l1_entry_offset = header.l1_table_offset + ((address >> l1_bits) << 3);
|
||||
return read_table(l1_entry_offset, table_entry_mask, l2_table_offset);
|
||||
}
|
||||
|
||||
|
||||
//Read an L2 table to get the offset of the data cluster for a given address.
|
||||
inline Bit8u QCow2Image::read_l2_table(Bit64u l2_table_offset, Bit64u address, Bit64u& data_cluster_offset){
|
||||
const Bit64u l2_entry_offset = l2_table_offset + (((address >> header.cluster_bits) & l2_mask) << 3);
|
||||
return read_table(l2_entry_offset, table_entry_mask, data_cluster_offset);
|
||||
}
|
||||
|
||||
|
||||
//Read the refcount table to get the offset of the refcount cluster for a given address.
|
||||
inline Bit8u QCow2Image::read_refcount_table(Bit64u data_cluster_offset, Bit64u& refcount_cluster_offset){
|
||||
const Bit64u refcount_entry_offset = header.refcount_table_offset + (((data_cluster_offset/cluster_size) >> refcount_bits) << 3);
|
||||
return read_table(refcount_entry_offset, empty_mask, refcount_cluster_offset);
|
||||
}
|
||||
|
||||
|
||||
//Read a table entry at the given offset.
|
||||
inline Bit8u QCow2Image::read_table(Bit64u entry_offset, Bit64u entry_mask, Bit64u& entry_value){
|
||||
Bit64u buffer;
|
||||
if (0 != read_allocated_data(entry_offset, (Bit8u*)&buffer, sizeof buffer)){
|
||||
return 0x05;
|
||||
}
|
||||
entry_value = host_read64(buffer) & table_entry_mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//Read a cluster not currently allocated in the image file.
|
||||
inline Bit8u QCow2Image::read_unallocated_cluster(Bit64u data_cluster_number, Bit8u* data)
|
||||
{
|
||||
if(backing_image == NULL){
|
||||
std::fill(data, data + cluster_size, 0);
|
||||
return 0;
|
||||
}
|
||||
return backing_image->read_cluster(data_cluster_number, data);
|
||||
}
|
||||
|
||||
|
||||
//Read a sector not currently allocated in the image file.
|
||||
inline Bit8u QCow2Image::read_unallocated_sector(Bit32u sectnum, Bit8u* data){
|
||||
if(backing_image == NULL){
|
||||
std::fill(data, data+sector_size, 0);
|
||||
return 0;
|
||||
}
|
||||
return backing_image->read_sector(sectnum, data);
|
||||
}
|
||||
|
||||
//Update the reference count for a cluster.
|
||||
inline Bit8u QCow2Image::update_reference_count(Bit64u cluster_offset, Bit8u* cluster_buffer){
|
||||
Bit64u refcount_cluster_offset;
|
||||
if (0 != read_refcount_table(cluster_offset, refcount_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 == refcount_cluster_offset){
|
||||
refcount_cluster_offset = cluster_offset + cluster_size;
|
||||
std::fill(cluster_buffer, cluster_buffer + cluster_size, 0);
|
||||
if (0 != write_refcount_table_entry(cluster_offset, refcount_cluster_offset)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != write_data(refcount_cluster_offset, cluster_buffer, cluster_size)){
|
||||
return 0x05;
|
||||
}
|
||||
if (0 != write_refcount(refcount_cluster_offset, refcount_cluster_offset, 0x1)){
|
||||
return 0x05;
|
||||
}
|
||||
}
|
||||
if (0 != write_refcount(cluster_offset, refcount_cluster_offset, 0x1)){
|
||||
return 0x05;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//Write data of arbitrary length to the image file.
|
||||
inline Bit8u QCow2Image::write_data(Bit64u file_offset, Bit8u* data, Bit64u data_size){
|
||||
if (0 != fseek(file, file_offset, SEEK_SET)){
|
||||
return 0x05;
|
||||
}
|
||||
if (1 != fwrite(data, data_size, 1, file)){
|
||||
return 0x05;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//Write an L2 table offset into the L1 table.
|
||||
inline Bit8u QCow2Image::write_l1_table_entry(Bit64u address, Bit64u l2_table_offset){
|
||||
const Bit64u l1_entry_offset = header.l1_table_offset + ((address >> l1_bits) << 3);
|
||||
return write_table_entry(l1_entry_offset, l2_table_offset | copy_flag);
|
||||
}
|
||||
|
||||
|
||||
//Write a data cluster offset into an L2 table.
|
||||
inline Bit8u QCow2Image::write_l2_table_entry(Bit64u l2_table_offset, Bit64u address, Bit64u data_cluster_offset){
|
||||
const Bit64u l2_entry_offset = l2_table_offset + (((address >> header.cluster_bits) & l2_mask) << 3);
|
||||
return write_table_entry(l2_entry_offset, data_cluster_offset | copy_flag);
|
||||
}
|
||||
|
||||
|
||||
//Write a refcount.
|
||||
inline Bit8u QCow2Image::write_refcount(Bit64u cluster_offset, Bit64u refcount_cluster_offset, Bit16u refcount){
|
||||
const Bit64u refcount_offset = refcount_cluster_offset + (((cluster_offset/cluster_size) & refcount_mask) << 1);
|
||||
Bit16u buffer = host_read16(refcount);
|
||||
return write_data(refcount_offset, (Bit8u*)&buffer, sizeof buffer);
|
||||
}
|
||||
|
||||
|
||||
//Write a refcount table entry.
|
||||
inline Bit8u QCow2Image::write_refcount_table_entry(Bit64u cluster_offset, Bit64u refcount_cluster_offset){
|
||||
const Bit64u refcount_entry_offset = header.refcount_table_offset + (((cluster_offset/cluster_size) >> refcount_bits) << 3);
|
||||
return write_table_entry(refcount_entry_offset, refcount_cluster_offset);
|
||||
}
|
||||
|
||||
|
||||
//Write a table entry at the given offset.
|
||||
inline Bit8u QCow2Image::write_table_entry(Bit64u entry_offset, Bit64u entry_value){
|
||||
Bit64u buffer = host_read64(entry_value);
|
||||
return write_data(entry_offset, (Bit8u*)&buffer, sizeof buffer);
|
||||
}
|
||||
|
||||
|
||||
//Public Constructor.
|
||||
QCow2Disk::QCow2Disk(QCow2Image::QCow2Header header, FILE *qcow2File, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(qcow2File, imgName, imgSizeK, isHardDisk), qcowImage(header, qcow2File){
|
||||
}
|
||||
|
||||
|
||||
//Public Destructor.
|
||||
QCow2Disk::~QCow2Disk(){
|
||||
}
|
||||
|
||||
|
||||
//Public function to a read a sector.
|
||||
Bit8u QCow2Disk::Read_AbsoluteSector(Bit32u sectnum, void* data){
|
||||
return qcowImage.read_sector(sectnum, (Bit8u*)data);
|
||||
}
|
||||
|
||||
|
||||
//Public function to a write a sector.
|
||||
Bit8u QCow2Disk::Write_AbsoluteSector(Bit32u sectnum, void* data){
|
||||
return qcowImage.write_sector(sectnum, (Bit8u*)data);
|
||||
}
|
@ -850,6 +850,10 @@
|
||||
RelativePath="..\src\hardware\ps1_sound.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\ints\qcow2_disk.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\gui\render.cpp"
|
||||
>
|
||||
|
@ -393,6 +393,7 @@
|
||||
<ClCompile Include="..\src\misc\cross.cpp" />
|
||||
<ClCompile Include="..\src\misc\messages.cpp" />
|
||||
<ClCompile Include="..\src\misc\programs.cpp" />
|
||||
<ClCompile Include="..\src\ints\qcow2_disk.cpp" />
|
||||
<ClCompile Include="..\src\misc\regionalloctracking.cpp" />
|
||||
<ClCompile Include="..\src\misc\setup.cpp" />
|
||||
<ClCompile Include="..\src\misc\support.cpp" />
|
||||
@ -429,4 +430,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -387,6 +387,9 @@
|
||||
<ClCompile Include="..\src\hardware\ps1_sound.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\ints\qcow2_disk.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\gui\render.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -642,4 +645,4 @@
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user