/* * Copyright (C) 2007-2009 Gabest * http://www.gabest.org * * 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, 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "GSDump.h" GSDumpBase::GSDumpBase(const std::string& fn) : m_frames(0) , m_extra_frames(2) { m_gs = px_fopen(fn, "wb"); if (!m_gs) fprintf(stderr, "GSDump: Error failed to open %s\n", fn.c_str()); } GSDumpBase::~GSDumpBase() { if(m_gs) fclose(m_gs); } void GSDumpBase::AddHeader(uint32 crc, const GSFreezeData& fd, const GSPrivRegSet* regs) { AppendRawData(&crc, 4); AppendRawData(&fd.size, 4); AppendRawData(fd.data, fd.size); AppendRawData(regs, sizeof(*regs)); } void GSDumpBase::Transfer(int index, const uint8* mem, size_t size) { if (size == 0) return; AppendRawData(0); AppendRawData(static_cast(index)); AppendRawData(&size, 4); AppendRawData(mem, size); } void GSDumpBase::ReadFIFO(uint32 size) { if (size == 0) return; AppendRawData(2); AppendRawData(&size, 4); } bool GSDumpBase::VSync(int field, bool last, const GSPrivRegSet* regs) { // dump file is bad, return done to delete the object if (!m_gs) return true; AppendRawData(3); AppendRawData(regs, sizeof(*regs)); AppendRawData(1); AppendRawData(static_cast(field)); if (last) m_extra_frames--; return (++m_frames & 1) == 0 && last && (m_extra_frames < 0); } void GSDumpBase::Write(const void *data, size_t size) { if (!m_gs || size == 0) return; size_t written = fwrite(data, 1, size, m_gs); if (written != size) fprintf(stderr, "GSDump: Error failed to write data\n"); } ////////////////////////////////////////////////////////////////////// // GSDump implementation ////////////////////////////////////////////////////////////////////// GSDump::GSDump(const std::string& fn, uint32 crc, const GSFreezeData& fd, const GSPrivRegSet* regs) : GSDumpBase(fn + ".gs") { AddHeader(crc, fd, regs); } void GSDump::AppendRawData(const void *data, size_t size) { Write(data, size); } void GSDump::AppendRawData(uint8 c) { Write(&c, 1); } ////////////////////////////////////////////////////////////////////// // GSDumpXz implementation ////////////////////////////////////////////////////////////////////// GSDumpXz::GSDumpXz(const std::string& fn, uint32 crc, const GSFreezeData& fd, const GSPrivRegSet* regs) : GSDumpBase(fn + ".gs.xz") { m_strm = LZMA_STREAM_INIT; lzma_ret ret = lzma_easy_encoder(&m_strm, 6 /*level*/, LZMA_CHECK_CRC64); if (ret != LZMA_OK) { fprintf(stderr, "GSDumpXz: Error initializing LZMA encoder ! (error code %u)\n", ret); return; } AddHeader(crc, fd, regs); } GSDumpXz::~GSDumpXz() { Flush(); // Finish the stream m_strm.avail_in = 0; Compress(LZMA_FINISH, LZMA_STREAM_END); lzma_end(&m_strm); } void GSDumpXz::AppendRawData(const void *data, size_t size) { size_t old_size = m_in_buff.size(); m_in_buff.resize(old_size + size); memcpy(&m_in_buff[old_size], data, size); // Enough data was accumulated, time to write/compress it. If compression // is enabled, it will freeze PCSX2. 1GB should be enough for long dump. // // Note: long dumps are currently not supported so this path won't be executed if (m_in_buff.size() > 1024*1024*1024) Flush(); } void GSDumpXz::AppendRawData(uint8 c) { m_in_buff.push_back(c); } void GSDumpXz::Flush() { if (m_in_buff.empty()) return; m_strm.next_in = m_in_buff.data(); m_strm.avail_in = m_in_buff.size(); Compress(LZMA_RUN, LZMA_OK); m_in_buff.clear(); } void GSDumpXz::Compress(lzma_action action, lzma_ret expected_status) { std::vector out_buff(1024*1024); do { m_strm.next_out = out_buff.data(); m_strm.avail_out = out_buff.size(); lzma_ret ret = lzma_code(&m_strm, action); if (ret != expected_status) { fprintf (stderr, "GSDumpXz: Error %d\n", (int) ret); return; } size_t write_size = out_buff.size() - m_strm.avail_out; Write(out_buff.data(), write_size); } while (m_strm.avail_out == 0); }