#pragma once #include "render_pass_replay.hpp" #include "gpu.h" #include "mod_memory.h" #include /* Preserve packets prepended after a title's world draw, and retarget the * completed OT's screen environment to the impending display bank. Operates * only on bounded, RAM-backed lists; command payloads are decoded, never * searched for byte patterns which could also be colors or texture data. */ class PSXOTReplay { struct Packet { uint32_t address; std::vector words; }; struct Bucket { uint32_t address, head; std::vector late; }; std::vector buckets; static uint32_t rd(uint32_t p) { return psx_mod_read_word(p); } static void wr(uint32_t p,uint32_t v) { psx_mod_write_word(p,v); } static bool valid(uint32_t p,uint32_t bytes) { if(p&3)return false; return (p>=0x10000 && p<=memory_get_ram_bytes() && bytes<=memory_get_ram_bytes()-p) || psx_mod_gpu_dma_memory_contains(p,bytes); } public: void reset() { buckets.clear(); } bool capture(uint32_t first,uint32_t count) { reset();first&=0x1FFFFFFF; if(count>8192 || !valid(first,count*4))return false; buckets.reserve(count); for(uint32_t i=0;i8192)return false; const uint32_t tag=rd(node),n=1+(tag>>24); words+=n; if(words>262144 || !valid(node,n*4))return false; Packet p{node,{}};p.words.reserve(n); for(uint32_t j=0;j512 || to+height>512)return false; uint32_t node=tail&0x1FFFFFFF,nodes=0,total=0; std::vector> edits; for(;;) { if(!valid(node,4) || ++nodes>65536)return false; const uint32_t tag=rd(node),n=tag>>24; total+=n; if(total>1048576 || !valid(node,4+4*n))return false; for(uint32_t i=0;i>24; int count=gpu_gp0_command_word_count(uint8_t(op)); if(count<0) { // Both mono and shaded polylines have at least two // vertices; shaded terminators occupy the next color. const uint32_t stride=(op&0x10)?2:1; uint32_t end=i+((op&0x10)?4:3); for(;end=n)return false; count=int(end-i+1); } if(count<=0 || i+uint32_t(count)>n)return false; if(op>=0xA0 && op<=0xBF) { const uint32_t wh=rd(p+8); const uint32_t width=((wh-1)&1023)+1, rows=(((wh>>16)-1)&511)+1; count+=int((width*rows+1)/2); if(i+uint32_t(count)>n)return false; } if(op==0xE3 || op==0xE4) { const int y=int((w>>10)&511); if(y>=from && y>11)&2047);if(y&1024)y-=2048; // Draw offsets may include authored camera shake. if(y>=from-128 && y