text: /**
   ******************************************************************************
   * Xenia : Xbox 360 Emulator Research Project                                 *
   ******************************************************************************
   * Copyright 2015 Ben Vanik. All rights reserved.                             *
   * Released under the BSD license - see LICENSE in the root for more details. *
   ******************************************************************************
   */
  
  #ifndef XENIA_GPU_TRACING_H_
  #define XENIA_GPU_TRACING_H_
  
  #include <string>
  
  #include "xenia/memory.h"
  
  namespace xe {
  namespace gpu {
  
  enum class TraceCommandType : uint32_t {
    kPrimaryBufferStart,
    kPrimaryBufferEnd,
    kIndirectBufferStart,
    kIndirectBufferEnd,
    kPacketStart,
    kPacketEnd,
    kMemoryRead,
    kMemoryWrite,
    kEvent,
  };
  
  struct PrimaryBufferStartCommand {
    TraceCommandType type;
    uint32_t base_ptr;
    uint32_t count;
  };
  
  struct PrimaryBufferEndCommand {
    TraceCommandType type;
  };
  
  struct IndirectBufferStartCommand {
    TraceCommandType type;
    uint32_t base_ptr;
    uint32_t count;
  };
  
  struct IndirectBufferEndCommand {
    TraceCommandType type;
  };
  
  struct PacketStartCommand {
    TraceCommandType type;
    uint32_t base_ptr;
    uint32_t count;
  };
  
  struct PacketEndCommand {
    TraceCommandType type;
  };
  
  struct MemoryReadCommand {
    TraceCommandType type;
    uint32_t base_ptr;
    uint32_t length;
  };
  
  struct MemoryWriteCommand {
    TraceCommandType type;
    uint32_t base_ptr;
    uint32_t length;
  };
  
  enum class EventType {
    kSwap,
  };
  
  struct EventCommand {
    TraceCommandType type;
    EventType event_type;
  };
  
  class TraceWriter {
   public:
    TraceWriter(uint8_t* membase) : membase_(membase), file_(nullptr) {}
    ~TraceWriter() = default;
  
    bool is_open() const { return file_ != nullptr; }
  
    bool Open(const std::wstring& path) {
      Close();
      file_ = _wfopen(path.c_str(), L"wb");
      return file_ != nullptr;
    }
  
    void Flush() {
      if (file_) {
        fflush(file_);
      }
    }
  
    void Close() {
      if (file_) {
        fflush(file_);
        fclose(file_);
        file_ = nullptr;
      }
    }
  
    void WritePrimaryBufferStart(uint32_t base_ptr, uint32_t count) {
      if (!file_) {
        return;
      }
      auto cmd = PrimaryBufferStartCommand({
          TraceCommandType::kPrimaryBufferStart, base_ptr, 0,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
    void WritePrimaryBufferEnd() {
      if (!file_) {
        return;
      }
      auto cmd = PrimaryBufferEndCommand({
          TraceCommandType::kPrimaryBufferEnd,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
    void WriteIndirectBufferStart(uint32_t base_ptr, uint32_t count) {
      if (!file_) {
        return;
      }
      auto cmd = IndirectBufferStartCommand({
          TraceCommandType::kIndirectBufferStart, base_ptr, 0,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
    void WriteIndirectBufferEnd() {
      if (!file_) {
        return;
      }
      auto cmd = IndirectBufferEndCommand({
          TraceCommandType::kIndirectBufferEnd,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
    void WritePacketStart(uint32_t base_ptr, uint32_t count) {
      if (!file_) {
        return;
      }
      auto cmd = PacketStartCommand({
          TraceCommandType::kPacketStart, base_ptr, count,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
      fwrite(membase_ + base_ptr, 4, count, file_);
    }
  
    void WritePacketEnd() {
      if (!file_) {
        return;
      }
      auto cmd = PacketEndCommand({
          TraceCommandType::kPacketEnd,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
    void WriteMemoryRead(uint32_t base_ptr, size_t length) {
      if (!file_) {
        return;
      }
      auto cmd = MemoryReadCommand({
          TraceCommandType::kMemoryRead, base_ptr, uint32_t(length),
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
      fwrite(membase_ + base_ptr, 1, length, file_);
    }
  
    void WriteMemoryWrite(uint32_t base_ptr, size_t length) {
      if (!file_) {
        return;
      }
      auto cmd = MemoryWriteCommand({
          TraceCommandType::kMemoryWrite, base_ptr, uint32_t(length),
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
      fwrite(membase_ + base_ptr, 1, length, file_);
    }
  
    void WriteEvent(EventType event_type) {
      if (!file_) {
        return;
      }
      auto cmd = EventCommand({
          TraceCommandType::kEvent, event_type,
      });
      fwrite(&cmd, 1, sizeof(cmd), file_);
    }
  
   private:
    uint8_t* membase_;
    FILE* file_;
  };
  
  }  // namespace gpu
  }  // namespace xe
  
  #endif  // XENIA_GPU_TRACING_H_
  