mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 09:00:44 +00:00
Add a tool menu item that will recompress a capture file
This commit is contained in:
@@ -567,6 +567,148 @@ void CaptureContext::AddFakeProfileMarkers()
|
||||
m_Drawcalls = ret;
|
||||
}
|
||||
|
||||
void CaptureContext::RecompressCapture()
|
||||
{
|
||||
QString destFilename = GetCaptureFilename();
|
||||
QString tempFilename;
|
||||
|
||||
ICaptureFile *cap = NULL;
|
||||
ICaptureFile *tempCap = NULL;
|
||||
|
||||
bool inplace = false;
|
||||
|
||||
if(IsCaptureTemporary() || !IsCaptureLocal())
|
||||
{
|
||||
QMessageBox::StandardButton res =
|
||||
RDDialog::question(m_MainWindow, tr("Unsaved capture"),
|
||||
tr("To recompress a capture you must save it first. Save this capture?"),
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||
|
||||
if(res == QMessageBox::Cancel || res == QMessageBox::No)
|
||||
return;
|
||||
|
||||
destFilename = m_MainWindow->GetSavePath();
|
||||
|
||||
// if it's already local, we'll do the save as part of the recompression convert. If it's
|
||||
// remote, we need to copy it first, but we copy it to a temporary so we can do the conversion
|
||||
// to the target location
|
||||
|
||||
if(IsCaptureLocal())
|
||||
{
|
||||
tempFilename = GetCaptureFilename();
|
||||
}
|
||||
else
|
||||
{
|
||||
tempFilename = TempCaptureFilename(lit("recompress"));
|
||||
Replay().CopyCaptureFromRemote(GetCaptureFilename(), tempFilename, m_MainWindow);
|
||||
|
||||
if(!QFile::exists(tempFilename))
|
||||
{
|
||||
RDDialog::critical(m_MainWindow, tr("Failed to save capture"),
|
||||
tr("Capture couldn't be saved from remote."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we're doing this inplace on an already saved capture, then we need to recompress to a
|
||||
// temporary and close/move it afterwards.
|
||||
inplace = true;
|
||||
destFilename = TempCaptureFilename(lit("recompress"));
|
||||
}
|
||||
|
||||
if(IsCaptureLocal())
|
||||
{
|
||||
// for local files we already have a handle. We'll reuse it, then re-open
|
||||
cap = Replay().GetCaptureFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// for remote files we open a new short-lived handle on the temporary file
|
||||
tempCap = cap = RENDERDOC_OpenCaptureFile();
|
||||
cap->OpenFile(tempFilename.toUtf8().data(), "rdc");
|
||||
}
|
||||
|
||||
if(!cap)
|
||||
{
|
||||
RDDialog::critical(m_MainWindow, tr("Unexpected missing handle"),
|
||||
tr("Couldn't get open handle to file for recompression."));
|
||||
return;
|
||||
}
|
||||
|
||||
int index = cap->FindSectionByType(SectionType::FrameCapture);
|
||||
SectionProperties props = cap->GetSectionProperties(index);
|
||||
|
||||
if(props.flags & SectionFlags::ZstdCompressed)
|
||||
{
|
||||
RDDialog::information(m_MainWindow, tr("Capture already compressed"),
|
||||
tr("This capture is already compressed as much as is possible."));
|
||||
|
||||
if(tempCap)
|
||||
tempCap->Shutdown();
|
||||
if(!tempFilename.isEmpty())
|
||||
QFile::remove(tempFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
// convert from the currently open cap to the destination
|
||||
float progress = 0.0f;
|
||||
|
||||
LambdaThread *th = new LambdaThread([this, cap, destFilename, &progress]() {
|
||||
cap->Convert(destFilename.toUtf8().data(), "rdc", &progress);
|
||||
});
|
||||
th->start();
|
||||
// wait a few ms before popping up a progress bar
|
||||
th->wait(500);
|
||||
if(th->isRunning())
|
||||
{
|
||||
ShowProgressDialog(m_MainWindow, tr("Recompressing file."), [th]() { return !th->isRunning(); },
|
||||
[&progress]() { return progress; });
|
||||
}
|
||||
th->deleteLater();
|
||||
|
||||
if(inplace)
|
||||
{
|
||||
// if we're recompressing "in place", we need to close our capture, move the temporary over
|
||||
// the original, then re-open.
|
||||
|
||||
// this releases the hold over the real desired location.
|
||||
cap->OpenFile("", "");
|
||||
|
||||
// now remove the old capture
|
||||
QFile::remove(GetCaptureFilename());
|
||||
|
||||
// move the recompressed one over
|
||||
QFile::rename(destFilename, GetCaptureFilename());
|
||||
|
||||
// and re-open
|
||||
cap->OpenFile(GetCaptureFilename().toUtf8().data(), "rdc");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've converted into the desired location. We don't have to do anything else but mark our
|
||||
// new locally saved non-temporary status.
|
||||
|
||||
m_CaptureFile = destFilename;
|
||||
m_CaptureLocal = true;
|
||||
m_CaptureTemporary = false;
|
||||
|
||||
// open the saved capture file. This will let us remove the old file too
|
||||
Replay().ReopenCaptureFile(m_CaptureFile);
|
||||
|
||||
m_CaptureMods = CaptureModifications::All;
|
||||
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
// close any temporary resources
|
||||
if(tempCap)
|
||||
tempCap->Shutdown();
|
||||
if(!tempFilename.isEmpty())
|
||||
QFile::remove(tempFilename);
|
||||
}
|
||||
|
||||
bool CaptureContext::SaveCaptureTo(const QString &captureFile)
|
||||
{
|
||||
bool success = false;
|
||||
@@ -633,33 +775,7 @@ bool CaptureContext::SaveCaptureTo(const QString &captureFile)
|
||||
m_CaptureTemporary = false;
|
||||
|
||||
Replay().ReopenCaptureFile(captureFile);
|
||||
|
||||
if(m_CaptureMods & CaptureModifications::Renames)
|
||||
{
|
||||
SectionProperties props;
|
||||
props.type = SectionType::ResourceRenames;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, SaveRenames().toUtf8());
|
||||
}
|
||||
if(m_CaptureMods & CaptureModifications::Bookmarks)
|
||||
{
|
||||
SectionProperties props;
|
||||
props.type = SectionType::Bookmarks;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, SaveBookmarks().toUtf8());
|
||||
}
|
||||
if(m_CaptureMods & CaptureModifications::Notes)
|
||||
{
|
||||
SectionProperties props;
|
||||
props.type = SectionType::Notes;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, SaveNotes().toUtf8());
|
||||
}
|
||||
|
||||
m_CaptureMods = CaptureModifications::NoModifications;
|
||||
SaveChanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -810,7 +926,21 @@ void CaptureContext::RemoveBookmark(uint32_t EID)
|
||||
RefreshUIStatus({}, true, true);
|
||||
}
|
||||
|
||||
QString CaptureContext::SaveRenames()
|
||||
void CaptureContext::SaveChanges()
|
||||
{
|
||||
if(m_CaptureMods & CaptureModifications::Renames)
|
||||
SaveRenames();
|
||||
|
||||
if(m_CaptureMods & CaptureModifications::Bookmarks)
|
||||
SaveBookmarks();
|
||||
|
||||
if(m_CaptureMods & CaptureModifications::Notes)
|
||||
SaveNotes();
|
||||
|
||||
m_CaptureMods = CaptureModifications::NoModifications;
|
||||
}
|
||||
|
||||
void CaptureContext::SaveRenames()
|
||||
{
|
||||
QVariantMap resources;
|
||||
for(ResourceId id : m_CustomNames.keys())
|
||||
@@ -821,7 +951,13 @@ QString CaptureContext::SaveRenames()
|
||||
QVariantMap root;
|
||||
root[lit("CustomResourceNames")] = resources;
|
||||
|
||||
return VariantToJSON(root);
|
||||
QString json = VariantToJSON(root);
|
||||
|
||||
SectionProperties props;
|
||||
props.type = SectionType::ResourceRenames;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8());
|
||||
}
|
||||
|
||||
void CaptureContext::LoadRenames(const QString &data)
|
||||
@@ -852,7 +988,7 @@ void CaptureContext::LoadRenames(const QString &data)
|
||||
}
|
||||
}
|
||||
|
||||
QString CaptureContext::SaveBookmarks()
|
||||
void CaptureContext::SaveBookmarks()
|
||||
{
|
||||
QVariantList bookmarks;
|
||||
for(const EventBookmark &mark : m_Bookmarks)
|
||||
@@ -867,7 +1003,13 @@ QString CaptureContext::SaveBookmarks()
|
||||
QVariantMap root;
|
||||
root[lit("Bookmarks")] = bookmarks;
|
||||
|
||||
return VariantToJSON(root);
|
||||
QString json = VariantToJSON(root);
|
||||
|
||||
SectionProperties props;
|
||||
props.type = SectionType::Bookmarks;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8());
|
||||
}
|
||||
|
||||
void CaptureContext::LoadBookmarks(const QString &data)
|
||||
@@ -892,13 +1034,19 @@ void CaptureContext::LoadBookmarks(const QString &data)
|
||||
}
|
||||
}
|
||||
|
||||
QString CaptureContext::SaveNotes()
|
||||
void CaptureContext::SaveNotes()
|
||||
{
|
||||
QVariantMap root;
|
||||
for(const QString &key : m_Notes.keys())
|
||||
root[key] = m_Notes[key];
|
||||
|
||||
return VariantToJSON(root);
|
||||
QString json = VariantToJSON(root);
|
||||
|
||||
SectionProperties props;
|
||||
props.type = SectionType::Notes;
|
||||
props.version = 1;
|
||||
|
||||
Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8());
|
||||
}
|
||||
|
||||
void CaptureContext::LoadNotes(const QString &data)
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
void LoadCapture(const QString &captureFile, const QString &origFilename, bool temporary,
|
||||
bool local) override;
|
||||
bool SaveCaptureTo(const QString &captureFile) override;
|
||||
void RecompressCapture() override;
|
||||
void CloseCapture() override;
|
||||
|
||||
void SetEventID(const QVector<ICaptureViewer *> &exclude, uint32_t selectedEventID,
|
||||
@@ -253,13 +254,15 @@ private:
|
||||
bool ContainsMarker(const rdcarray<DrawcallDescription> &m_Drawcalls);
|
||||
void AddFakeProfileMarkers();
|
||||
|
||||
QString SaveRenames();
|
||||
void SaveChanges();
|
||||
|
||||
void SaveRenames();
|
||||
void LoadRenames(const QString &data);
|
||||
|
||||
QString SaveBookmarks();
|
||||
void SaveBookmarks();
|
||||
void LoadBookmarks(const QString &data);
|
||||
|
||||
QString SaveNotes();
|
||||
void SaveNotes();
|
||||
void LoadNotes(const QString &data);
|
||||
|
||||
float m_LoadProgress = 0.0f;
|
||||
@@ -291,7 +294,6 @@ private:
|
||||
}
|
||||
|
||||
void setupDockWindow(QWidget *shad);
|
||||
|
||||
rdcarray<DrawcallDescription> m_Drawcalls;
|
||||
|
||||
APIProperties m_APIProps;
|
||||
|
||||
@@ -917,6 +917,7 @@ enum class CaptureModifications : uint32_t
|
||||
Renames = 0x0001,
|
||||
Bookmarks = 0x0002,
|
||||
Notes = 0x0004,
|
||||
All = 0xffffffff,
|
||||
};
|
||||
|
||||
BITMASK_OPERATORS(CaptureModifications);
|
||||
@@ -984,6 +985,9 @@ time.
|
||||
)");
|
||||
virtual bool SaveCaptureTo(const QString &captureFile) = 0;
|
||||
|
||||
DOCUMENT("Recompress the current capture as much as possible.");
|
||||
virtual void RecompressCapture() = 0;
|
||||
|
||||
DOCUMENT("Close the currently open capture file.");
|
||||
virtual void CloseCapture() = 0;
|
||||
|
||||
|
||||
@@ -183,6 +183,8 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai
|
||||
ui->action_Resolve_Symbols->setEnabled(false);
|
||||
ui->action_Resolve_Symbols->setText(tr("Resolve Symbols"));
|
||||
|
||||
ui->action_Recompress_Capture->setEnabled(false);
|
||||
|
||||
LambdaThread *th = new LambdaThread([this]() {
|
||||
m_Ctx.Config().AddAndroidHosts();
|
||||
for(RemoteHost *host : m_Ctx.Config().RemoteHosts)
|
||||
@@ -1295,6 +1297,8 @@ void MainWindow::OnCaptureLoaded()
|
||||
|
||||
statusProgress->setVisible(false);
|
||||
|
||||
ui->action_Recompress_Capture->setEnabled(true);
|
||||
|
||||
ui->action_Start_Replay_Loop->setEnabled(true);
|
||||
|
||||
setCaptureHasErrors(!m_Ctx.DebugMessages().empty());
|
||||
@@ -1335,6 +1339,8 @@ void MainWindow::OnCaptureClosed()
|
||||
ui->action_Resolve_Symbols->setEnabled(false);
|
||||
ui->action_Resolve_Symbols->setText(tr("Resolve Symbols"));
|
||||
|
||||
ui->action_Recompress_Capture->setEnabled(false);
|
||||
|
||||
SetTitle();
|
||||
|
||||
// if the remote sever disconnected during capture replay, resort back to a 'disconnected' state
|
||||
@@ -1628,6 +1634,11 @@ void MainWindow::on_action_Resolve_Symbols_triggered()
|
||||
m_Ctx.GetAPIInspector()->Refresh();
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Recompress_Capture_triggered()
|
||||
{
|
||||
m_Ctx.RecompressCapture();
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Start_Replay_Loop_triggered()
|
||||
{
|
||||
if(!m_Ctx.IsCaptureLoaded())
|
||||
|
||||
@@ -119,6 +119,7 @@ private slots:
|
||||
void on_action_Python_Shell_triggered();
|
||||
void on_action_Inject_into_Process_triggered();
|
||||
void on_action_Resolve_Symbols_triggered();
|
||||
void on_action_Recompress_Capture_triggered();
|
||||
void on_action_Start_Replay_Loop_triggered();
|
||||
void on_action_Attach_to_Running_Instance_triggered();
|
||||
void on_action_Manage_Remote_Servers_triggered();
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="action_Resolve_Symbols"/>
|
||||
<addaction name="action_Recompress_Capture"/>
|
||||
<addaction name="action_Start_Replay_Loop"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Settings"/>
|
||||
@@ -424,6 +425,11 @@
|
||||
<string>Capture C&omments</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Recompress_Capture">
|
||||
<property name="text">
|
||||
<string>Re&compress Capture</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
||||
@@ -169,6 +169,10 @@ struct CaptureContextInvoker : ICaptureContext
|
||||
{
|
||||
return InvokeRetFunction<bool>(&ICaptureContext::SaveCaptureTo, capture);
|
||||
}
|
||||
virtual void RecompressCapture() override
|
||||
{
|
||||
InvokeVoidFunction(&ICaptureContext::RecompressCapture);
|
||||
}
|
||||
virtual void CloseCapture() override { InvokeVoidFunction(&ICaptureContext::CloseCapture); }
|
||||
virtual void SetEventID(const QVector<ICaptureViewer *> &exclude, uint32_t selectedEventID,
|
||||
uint32_t eventID, bool force = false) override
|
||||
|
||||
@@ -1377,10 +1377,11 @@ representation back to native RDC.
|
||||
|
||||
:param str filename: The filename to save to.
|
||||
:param str filetype: The format to convert to.
|
||||
:param float progress: A reference to a ``float`` value that will be updated as the copy happens
|
||||
:return: The status of the conversion operation, whether it succeeded or failed (and how it failed).
|
||||
:rtype: ReplayStatus
|
||||
)");
|
||||
virtual ReplayStatus Convert(const char *filename, const char *filetype) = 0;
|
||||
virtual ReplayStatus Convert(const char *filename, const char *filetype, float *progress) = 0;
|
||||
|
||||
DOCUMENT(R"(Returns the list of capture file formats.
|
||||
|
||||
|
||||
@@ -240,9 +240,9 @@ typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, IReplayDriver **drive
|
||||
typedef void (*StructuredProcessor)(RDCFile *rdc, SDFile &structData);
|
||||
|
||||
typedef ReplayStatus (*CaptureImporter)(const char *filename, StreamReader &reader, RDCFile *rdc,
|
||||
SDFile &structData);
|
||||
SDFile &structData, float *progress);
|
||||
typedef ReplayStatus (*CaptureExporter)(const char *filename, const RDCFile &rdc,
|
||||
const SDFile &structData);
|
||||
const SDFile &structData, float *progress);
|
||||
|
||||
typedef bool (*VulkanLayerCheck)(VulkanLayerFlags &flags, std::vector<std::string> &myJSONs,
|
||||
std::vector<std::string> &otherJSONs);
|
||||
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
void SetMetadata(const char *driverName, uint64_t machineIdent, FileType thumbType,
|
||||
uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData);
|
||||
|
||||
ReplayStatus Convert(const char *filename, const char *filetype);
|
||||
ReplayStatus Convert(const char *filename, const char *filetype, float *progress);
|
||||
|
||||
rdcarray<CaptureFileFormat> GetCaptureFileFormats()
|
||||
{
|
||||
@@ -207,8 +207,9 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
|
||||
|
||||
{
|
||||
StreamReader reader(FileIO::fopen(filename, "rb"));
|
||||
delete m_RDC;
|
||||
m_RDC = new RDCFile;
|
||||
ret = importer(filename, reader, m_RDC, m_StructuredData);
|
||||
ret = importer(filename, reader, m_RDC, m_StructuredData, NULL);
|
||||
}
|
||||
|
||||
if(ret != ReplayStatus::Succeeded)
|
||||
@@ -243,7 +244,7 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype
|
||||
{
|
||||
StreamReader reader(vec);
|
||||
m_RDC = new RDCFile;
|
||||
ret = importer(NULL, reader, m_RDC, m_StructuredData);
|
||||
ret = importer(NULL, reader, m_RDC, m_StructuredData, NULL);
|
||||
}
|
||||
|
||||
if(ret != ReplayStatus::Succeeded)
|
||||
@@ -368,7 +369,7 @@ void CaptureFile::SetMetadata(const char *driverName, uint64_t machineIdent, Fil
|
||||
free((void *)th.pixels);
|
||||
}
|
||||
|
||||
ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype)
|
||||
ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype, float *progress)
|
||||
{
|
||||
if(!m_RDC)
|
||||
{
|
||||
@@ -379,7 +380,7 @@ ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype)
|
||||
CaptureExporter exporter = RenderDoc::Inst().GetCaptureExporter(filetype);
|
||||
|
||||
if(exporter)
|
||||
return exporter(filename, *m_RDC, GetStructuredData());
|
||||
return exporter(filename, *m_RDC, GetStructuredData(), progress);
|
||||
|
||||
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
|
||||
RDCWARN("Converting file to unrecognised filetype '%s' - treating as 'rdc'", filetype);
|
||||
@@ -419,7 +420,7 @@ ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype)
|
||||
|
||||
WriteSerialiser ser(writer, Ownership::Nothing);
|
||||
|
||||
ser.WriteStructuredFile(GetStructuredData());
|
||||
ser.WriteStructuredFile(GetStructuredData(), progress);
|
||||
|
||||
writer->Finish();
|
||||
|
||||
@@ -436,7 +437,7 @@ ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype)
|
||||
StreamWriter *writer = output.WriteSection(props);
|
||||
StreamReader *reader = m_RDC->ReadSection(frameCaptureIndex);
|
||||
|
||||
StreamTransfer(writer, reader, NULL);
|
||||
StreamTransfer(writer, reader, progress);
|
||||
|
||||
writer->Finish();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include <utility>
|
||||
#include "common/common.h"
|
||||
#include "serialise/rdcfile.h"
|
||||
|
||||
@@ -39,6 +40,16 @@ std::string GetBufferName(inttype i)
|
||||
return StringFormat::Fmt("%06u", (uint32_t)i);
|
||||
}
|
||||
|
||||
inline float BufferProgress(float progress)
|
||||
{
|
||||
return 0.2f * progress;
|
||||
}
|
||||
|
||||
inline float StructuredProgress(float progress)
|
||||
{
|
||||
return 0.2f + 0.8f * progress;
|
||||
}
|
||||
|
||||
struct xml_file_writer : pugi::xml_writer
|
||||
{
|
||||
StreamWriter stream;
|
||||
@@ -247,7 +258,7 @@ static void Obj2XML(pugi::xml_node &parent, SDObject &child)
|
||||
}
|
||||
|
||||
static ReplayStatus Structured2XML(const char *filename, const RDCFile &file, uint64_t version,
|
||||
const StructuredChunkList &chunks)
|
||||
const StructuredChunkList &chunks, float *progress)
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
|
||||
@@ -462,8 +473,8 @@ static SDObject *XML2Obj(pugi::xml_node &obj)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &buffers,
|
||||
RDCFile *rdc, uint64_t &version, StructuredChunkList &chunks)
|
||||
static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &buffers, RDCFile *rdc,
|
||||
uint64_t &version, StructuredChunkList &chunks, float *progress)
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
doc.load_string(xml);
|
||||
@@ -528,6 +539,9 @@ static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &
|
||||
// push in other sections
|
||||
pugi::xml_node xSection = xHeader.next_sibling();
|
||||
|
||||
if(progress)
|
||||
*progress = StructuredProgress(0.1f);
|
||||
|
||||
while(!strcmp(xSection.name(), "section"))
|
||||
{
|
||||
SectionProperties props;
|
||||
@@ -596,6 +610,9 @@ static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &
|
||||
xSection = xSection.next_sibling();
|
||||
}
|
||||
|
||||
if(progress)
|
||||
*progress = StructuredProgress(0.2f);
|
||||
|
||||
pugi::xml_node xChunks = xSection;
|
||||
|
||||
if(strcmp(xSection.name(), "chunks"))
|
||||
@@ -612,6 +629,9 @@ static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &
|
||||
|
||||
version = xChunks.attribute("version").as_ullong();
|
||||
|
||||
size_t chunkIdx = 0;
|
||||
size_t numChunks = std::distance(xChunks.begin(), xChunks.end());
|
||||
|
||||
for(pugi::xml_node xChunk = xChunks.first_child(); xChunk; xChunk = xChunk.next_sibling())
|
||||
{
|
||||
if(strcmp(xChunk.name(), "chunk"))
|
||||
@@ -657,13 +677,18 @@ static ReplayStatus XML2Structured(const char *xml, const StructuredBufferList &
|
||||
}
|
||||
|
||||
chunks.push_back(chunk);
|
||||
|
||||
if(progress)
|
||||
*progress = StructuredProgress(0.2f + 0.8f * (float(chunkIdx) / float(numChunks)));
|
||||
|
||||
chunkIdx++;
|
||||
}
|
||||
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
|
||||
static ReplayStatus Buffers2ZIP(const std::string &filename, const RDCFile &file,
|
||||
const StructuredBufferList &buffers)
|
||||
const StructuredBufferList &buffers, float *progress)
|
||||
{
|
||||
std::string zipFile = filename + ".zip";
|
||||
|
||||
@@ -679,9 +704,14 @@ static ReplayStatus Buffers2ZIP(const std::string &filename, const RDCFile &file
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < buffers.size(); i++)
|
||||
{
|
||||
mz_zip_writer_add_mem(&zip, GetBufferName(i).c_str(), buffers[i]->data(), buffers[i]->size(),
|
||||
MZ_BEST_COMPRESSION);
|
||||
|
||||
if(progress)
|
||||
*progress = BufferProgress(float(i) / float(buffers.size()));
|
||||
}
|
||||
|
||||
const RDCThumb &th = file.GetThumbnail();
|
||||
if(th.pixels && th.len > 0 && th.width > 0 && th.height > 0)
|
||||
mz_zip_writer_add_mem(&zip, "thumb.jpg", th.pixels, th.len, MZ_BEST_COMPRESSION);
|
||||
@@ -692,7 +722,7 @@ static ReplayStatus Buffers2ZIP(const std::string &filename, const RDCFile &file
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
|
||||
static void ZIP2Buffers(const std::string &filename, StructuredBufferList &buffers)
|
||||
static void ZIP2Buffers(const std::string &filename, StructuredBufferList &buffers, float *progress)
|
||||
{
|
||||
std::string zipFile = filename + ".zip";
|
||||
|
||||
@@ -735,33 +765,39 @@ static void ZIP2Buffers(const std::string &filename, StructuredBufferList &buffe
|
||||
buffers.back() = new bytebuf;
|
||||
buffers.back()->assign(buf, sz);
|
||||
}
|
||||
|
||||
if(progress)
|
||||
*progress = BufferProgress(float(i) / float(numfiles));
|
||||
}
|
||||
}
|
||||
|
||||
mz_zip_reader_end(&zip);
|
||||
}
|
||||
|
||||
ReplayStatus importXMLZ(const char *filename, StreamReader &reader, RDCFile *rdc, SDFile &structData)
|
||||
ReplayStatus importXMLZ(const char *filename, StreamReader &reader, RDCFile *rdc,
|
||||
SDFile &structData, float *progress)
|
||||
{
|
||||
if(filename)
|
||||
ZIP2Buffers(filename, structData.buffers);
|
||||
ZIP2Buffers(filename, structData.buffers, progress);
|
||||
|
||||
uint64_t len = reader.GetSize();
|
||||
char *buf = new char[(size_t)len + 1];
|
||||
reader.Read(buf, (size_t)len);
|
||||
buf[len] = 0;
|
||||
|
||||
return XML2Structured(buf, structData.buffers, rdc, structData.version, structData.chunks);
|
||||
return XML2Structured(buf, structData.buffers, rdc, structData.version, structData.chunks,
|
||||
progress);
|
||||
}
|
||||
|
||||
ReplayStatus exportXMLZ(const char *filename, const RDCFile &rdc, const SDFile &structData)
|
||||
ReplayStatus exportXMLZ(const char *filename, const RDCFile &rdc, const SDFile &structData,
|
||||
float *progress)
|
||||
{
|
||||
ReplayStatus ret = Buffers2ZIP(filename, rdc, structData.buffers);
|
||||
ReplayStatus ret = Buffers2ZIP(filename, rdc, structData.buffers, progress);
|
||||
|
||||
if(ret != ReplayStatus::Succeeded)
|
||||
return ret;
|
||||
|
||||
return Structured2XML(filename, rdc, structData.version, structData.chunks);
|
||||
return Structured2XML(filename, rdc, structData.version, structData.chunks, progress);
|
||||
}
|
||||
|
||||
static ConversionRegistration XMLConversionRegistration("xml", R"(XML+ZIP format.
|
||||
|
||||
@@ -241,6 +241,14 @@ RDCFile::~RDCFile()
|
||||
|
||||
void RDCFile::Open(const char *path)
|
||||
{
|
||||
// silently fail when opening the empty string, to allow 'releasing' a capture file by opening an
|
||||
// empty path.
|
||||
if(path == NULL || path[0] == 0)
|
||||
{
|
||||
m_Error = ContainerError::FileNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
RDCLOG("Opening RDCFile %s", path);
|
||||
|
||||
// ensure section header is compiled correctly
|
||||
|
||||
@@ -407,7 +407,7 @@ void Serialiser<SerialiserMode::Writing>::EndChunk()
|
||||
}
|
||||
|
||||
template <>
|
||||
void Serialiser<SerialiserMode::Writing>::WriteStructuredFile(const SDFile &file)
|
||||
void Serialiser<SerialiserMode::Writing>::WriteStructuredFile(const SDFile &file, float *progress)
|
||||
{
|
||||
Serialiser<SerialiserMode::Writing> scratchWriter(
|
||||
new StreamWriter(StreamWriter::DefaultScratchSize), Ownership::Stream);
|
||||
@@ -472,6 +472,9 @@ void Serialiser<SerialiserMode::Writing>::WriteStructuredFile(const SDFile &file
|
||||
m_Write->Write(scratchWriter.GetWriter()->GetData(), scratchWriter.GetWriter()->GetOffset());
|
||||
scratchWriter.GetWriter()->Rewind();
|
||||
}
|
||||
|
||||
if(progress)
|
||||
*progress = float(i) / float(file.chunks.size());
|
||||
}
|
||||
|
||||
m_StructuredFile = &m_StructData;
|
||||
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
// up-front
|
||||
void SetStreamingMode(bool stream) { m_DataStreaming = stream; }
|
||||
SDFile &GetStructuredFile() { return *m_StructuredFile; }
|
||||
void WriteStructuredFile(const SDFile &file);
|
||||
void WriteStructuredFile(const SDFile &file, float *progress);
|
||||
void SetDrawChunk() { m_DrawChunk = true; }
|
||||
//////////////////////////////////////////
|
||||
// Public serialisation interface
|
||||
|
||||
@@ -462,7 +462,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]")
|
||||
{
|
||||
WriteSerialiser rewrite(rewriteBuf, Ownership::Nothing);
|
||||
|
||||
rewrite.WriteStructuredFile(structFile);
|
||||
rewrite.WriteStructuredFile(structFile, NULL);
|
||||
}
|
||||
|
||||
// must be bitwise identical to the original serialised data.
|
||||
@@ -934,7 +934,7 @@ TEST_CASE("Read/write container types", "[serialiser][structured]")
|
||||
{
|
||||
WriteSerialiser rewrite(rewriteBuf, Ownership::Nothing);
|
||||
|
||||
rewrite.WriteStructuredFile(structData);
|
||||
rewrite.WriteStructuredFile(structData, NULL);
|
||||
}
|
||||
|
||||
// must be bitwise identical to the original serialised data.
|
||||
@@ -1304,7 +1304,7 @@ TEST_CASE("Read/write complex types", "[serialiser][structured]")
|
||||
{
|
||||
WriteSerialiser rewrite(rewriteBuf, Ownership::Nothing);
|
||||
|
||||
rewrite.WriteStructuredFile(structData);
|
||||
rewrite.WriteStructuredFile(structData, NULL);
|
||||
}
|
||||
|
||||
// must be bitwise identical to the original serialised data.
|
||||
|
||||
@@ -722,7 +722,7 @@ struct ConvertCommand : public Command
|
||||
return 1;
|
||||
}
|
||||
|
||||
st = file->Convert(outfile.c_str(), outfmt.c_str());
|
||||
st = file->Convert(outfile.c_str(), outfmt.c_str(), NULL);
|
||||
|
||||
if(st != ReplayStatus::Succeeded)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user