port: grp.SaveScreenShot via next-frame swapchain readback + JPEG
CPythonGraphic::SaveScreenShot queues a readback of the next presented frame (ScreenShotHost -> VulkanWindow::request_frame); WriteScreenShot runs the rest of 40250's function verbatim (X8R8G8B8 rows, SaveJPEG, EXIF tag). jpeg_save is encoded with stb_image_write. SHGetSpecialFolderPath returns $HOME/Documents (else cwd); CreateDirectory/_access/jpeg_save accept '\\'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c21e012965
commit
bda85eea50
@@ -599,3 +599,5 @@
|
||||
{"date": "2026-09-30T03:32:49+00:00", "unit": "UserInterface/PythonApplicationLogo.cpp", "action": "correction: N_A: CPythonApplication::OnLogo* has no caller: intrologo.py calls app.OnLogoOpen/Update/Render/Close, but those bind appLogo* in PythonApplicationModule.cpp, whose CPythonApplication::OnLogo* calls are commented out", "evidence": "LC_ALL=C grep app.OnLogo over the root .py (intrologo.py); PythonApplicationModule.cpp:1165-1195 (the earlier grep ran without LC_ALL=C and missed the CP949 scripts)"}
|
||||
{"date": "2026-09-30T03:32:49+00:00", "unit": "UserInterface/MovieMan.cpp", "action": "correction: N_A: reached at boot through prototype.py SetLogoPhase -> intrologo.py app.OnLogoOpen(\"logo1.avi\"/\"logo2.avi\") -> CMovieMan::PlayLogo -> PlayMovie(<cwd>\\\\logoN.avi), but the 40250 Client ships no .avi, so RenderFileToMMStream fails and nothing is shown; PlayIntro/PlayTutorial have no caller. The port stub matches that outcome", "evidence": "find Client -iname *.avi: none; LC_ALL=C grep PlayIntro/PlayTutorial over ClientVS22/source (the earlier grep ran without LC_ALL=C and missed the CP949 scripts)"}
|
||||
{"date": "2026-09-30T03:32:49+00:00", "unit": "UserInterface/PythonApplicationWebPage.cpp", "action": "correction: DIVERGENT: reachable: game.py __InGameShop_Show -> interfacemodule.OpenWebWindow -> uiweb.py app.ShowWebPage when constInfo.IN_GAME_SHOP_ENABLE (localeinfo sets it per locale), and uishop.MallPageDialog; the port has no embedded browser, so ShowWebPage/MoveWebPage/HideWebPage are stubs in platform/UserInterface/PythonApplication.cpp", "evidence": "LC_ALL=C grep ShowWebPage/uiweb/IN_GAME_SHOP_ENABLE over the root .py (the earlier grep ran without LC_ALL=C and missed the CP949 scripts)"}
|
||||
{"date": "2026-09-30", "unit": "EterLib/JpegFile.cpp", "action": "jpeg_save ADAPTED (stb_image_write), destination managers + unused load/save_to_mem N_A", "evidence": "tests/port/port_screenshot_test.cpp; only CPythonGraphic::SaveJPEG calls jpeg_save"}
|
||||
{"date": "2026-09-30", "unit": "EterPythonLib/PythonGraphic.cpp", "action": "GenScreenShotTag/SaveJPEG PORTED, SaveScreenShot ADAPTED (next-frame readback via ScreenShotHost)", "evidence": "port.screenshot; desktop grp.SaveScreenShot() -> ~/Documents/METIN2/0930_124051.jpg, colours checked"}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"reference": "EterLib/JpegFile.cpp",
|
||||
"reference_sha256": "5e4417d5dfe77c4921e22b3196ca894a40034e41a19936fc49b509ad2c19b98a",
|
||||
"priority": "P4",
|
||||
"contracts": [],
|
||||
"functions": {
|
||||
"file_init_destination": {
|
||||
"status": "N_A",
|
||||
"note": "libjpeg stdio destination manager behind jpeg_save; stb_image_write writes through its own callback (see jpeg_save ADAPTED)."
|
||||
},
|
||||
"file_empty_output_buffer": {
|
||||
"status": "N_A",
|
||||
"note": "libjpeg stdio destination manager behind jpeg_save; stb_image_write writes through its own callback (see jpeg_save ADAPTED)."
|
||||
},
|
||||
"file_term_destination": {
|
||||
"status": "N_A",
|
||||
"note": "libjpeg stdio destination manager behind jpeg_save; stb_image_write writes through its own callback (see jpeg_save ADAPTED)."
|
||||
},
|
||||
"mem_init_destination": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_empty_output_buffer": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_term_destination": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"jpeg_save": {
|
||||
"status": "ADAPTED",
|
||||
"impl": [
|
||||
"src/platform/EterLib/JpegFile.cpp:jpeg_save"
|
||||
],
|
||||
"test": [
|
||||
"tests/port/port_screenshot_test.cpp"
|
||||
],
|
||||
"note": "40250 encodes with libjpeg-9a (stdio destination, JCS_RGB, jpeg_set_defaults + jpeg_set_quality(quality)); the port encodes the same top-down RGB rows at the same quality with stb_image_write into a baseline JFIF file. Invariant: returns 1 with a complete JPEG (SOI..EOI) at `filename`, 0 when the file cannot be opened. '\\\\' in the path is taken as a separator like the Win32 CRT fopen."
|
||||
},
|
||||
"jpeg_save_to_file": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"jpeg_save_to_mem": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_init_source": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_fill_input_buffer": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_skip_input_data": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_resync_to_restart": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"mem_term_source": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"jpeg_load_from_mem": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
},
|
||||
"jpeg_load": {
|
||||
"status": "N_A",
|
||||
"note": "No caller in 40250 (LC_ALL=C grep -a over ClientVS22/source and the root .py: only CPythonGraphic::SaveJPEG calls jpeg_save); left undefined."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,13 +63,32 @@
|
||||
"note": "Verbatim 40250. The recording device reports no D3DCAPS2_FULLSCREENGAMMA (windowed output, where D3D8 ignores the ramp as well), so it returns at the caps check; IDirect3DDevice8::SetGammaRamp added to the D3D8 shim."
|
||||
},
|
||||
"GenScreenShotTag": {
|
||||
"status": "TODO"
|
||||
"status": "PORTED",
|
||||
"impl": [
|
||||
"src/platform/EterPythonLib/PythonGraphic.cpp:GenScreenShotTag"
|
||||
],
|
||||
"note": "Verbatim 40250; only reached when g_isScreenShotKey (false, never set)."
|
||||
},
|
||||
"CPythonGraphic::SaveJPEG": {
|
||||
"status": "TODO"
|
||||
"status": "PORTED",
|
||||
"impl": [
|
||||
"src/platform/EterPythonLib/PythonGraphic.cpp:CPythonGraphic::SaveJPEG"
|
||||
],
|
||||
"test": [
|
||||
"tests/port/port_screenshot_test.cpp"
|
||||
]
|
||||
},
|
||||
"CPythonGraphic::SaveScreenShot": {
|
||||
"status": "TODO"
|
||||
"status": "ADAPTED",
|
||||
"impl": [
|
||||
"src/platform/EterPythonLib/PythonGraphic.cpp:CPythonGraphic::SaveScreenShot",
|
||||
"src/platform/EterPythonLib/PythonGraphic.cpp:ScreenShotHost::WriteScreenShot",
|
||||
"src/host/live_client.cpp"
|
||||
],
|
||||
"test": [
|
||||
"tests/port/port_screenshot_test.cpp"
|
||||
],
|
||||
"note": "40250 locks the D3D8 back buffer and writes the JPEG before returning. The script thread and the SDL host alternate, so the call queues a readback of the next presented frame (ScreenShotHost::SetRequest -> VulkanWindow::request_frame) and returns true; ScreenShotHost::WriteScreenShot runs the rest verbatim one frame later (X8R8G8B8 row loop, SaveJPEG, TraceError on failure, g_isScreenShotKey EXIF block). No host -> TraceError + false (40250's GetBackBuffer failure). Only the X8R8G8B8 branch exists: the swapchain is read back as BGRA. Checked on desktop: grp.SaveScreenShot() wrote ~/Documents/METIN2/<MMDD_hhmmss>.jpg with correct colours (SHGetSpecialFolderPath shim = $HOME/Documents, else cwd)."
|
||||
},
|
||||
"CPythonGraphic::PushState": {
|
||||
"status": "PORTED",
|
||||
|
||||
Reference in New Issue
Block a user