mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-08-24 14:44:35 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac171fa95 | ||
|
|
fa1b6493da | ||
|
|
e58766745c | ||
|
|
d8a9c5b479 | ||
|
|
9a4d8ba934 | ||
|
|
ab816543c8 | ||
|
|
37ccd81985 | ||
|
|
42dad4ade8 | ||
|
|
b2ac035862 | ||
|
|
f42d7eec74 | ||
|
|
e4ef41f86a | ||
|
|
103dd3954a | ||
|
|
2f9577a28e | ||
|
|
33ec4d9430 | ||
|
|
209f09ca7a | ||
|
|
2e6af75f1d | ||
|
|
f68122a380 | ||
|
|
d51bdccf7f | ||
|
|
7d8377b133 | ||
|
|
80d3a8a757 | ||
|
|
2e6c5cde29 | ||
|
|
0f2768dca4 | ||
|
|
d5be095482 | ||
|
|
b315625a94 | ||
|
|
6b51462fa0 | ||
|
|
373e698545 | ||
|
|
3b309c6d4e | ||
|
|
8e93e9f516 | ||
|
|
ac8582eca7 | ||
|
|
e6f72fdde7 | ||
|
|
9166218d07 | ||
|
|
c0b1bde94b | ||
|
|
756a679262 | ||
|
|
13b19b35c0 | ||
|
|
cadb70d1e4 | ||
|
|
4cb4844e8d | ||
|
|
556c7ca080 | ||
|
|
962db4bfc4 | ||
|
|
da651cabe4 | ||
|
|
e03076c6ff | ||
|
|
122968e63a | ||
|
|
6beac26727 | ||
|
|
3073281d13 | ||
|
|
0e0e23eefb | ||
|
|
bcec38bd97 | ||
|
|
bd706f99b2 | ||
|
|
501af632f7 | ||
|
|
a143122804 | ||
|
|
92aa426bba | ||
|
|
96a1ce2fb2 | ||
|
|
c203a09435 | ||
|
|
c4084b4162 | ||
|
|
2a7c948a57 | ||
|
|
46912595ea | ||
|
|
120ee6c673 | ||
|
|
112238fbd7 | ||
|
|
38e288ef6a | ||
|
|
35322cf49b | ||
|
|
8d44e1af0e | ||
|
|
16af078b3b | ||
|
|
3e968b4390 | ||
|
|
0029dac32d | ||
|
|
a083343c6e | ||
|
|
c277ee1d34 | ||
|
|
ebe8592a8d |
@@ -99,7 +99,7 @@ jobs:
|
||||
run: |
|
||||
# To save time, only brew update if running the install without it fails
|
||||
function do-install() {
|
||||
brew install sound-touch portaudio wxwidgets sdl2 libsamplerate glib
|
||||
brew install sound-touch portaudio wxwidgets sdl2 libsamplerate
|
||||
}
|
||||
if ! do-install; then
|
||||
brew update
|
||||
|
||||
@@ -49,9 +49,8 @@ chmod a+x ./squashfs-root/usr/optional/exec.so
|
||||
echo "$name" > "$GITHUB_WORKSPACE"/squashfs-root/version.txt
|
||||
mkdir -p "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/app
|
||||
cp -r "$GITHUB_WORKSPACE"/bin/Langs "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/
|
||||
cp -r "$GITHUB_WORKSPACE"/bin/resources "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/app/
|
||||
cp "$GITHUB_WORKSPACE"/bin/docs/{Configuration_Guide.pdf,PCSX2_FAQ.pdf} "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/app
|
||||
cp "$GITHUB_WORKSPACE"/bin/cheats_ws.zip "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/app
|
||||
cp ./bin/GameIndex.yaml "$GITHUB_WORKSPACE"/squashfs-root/usr/bin/app/GameIndex.yaml
|
||||
cp /usr/lib/$LIBARCH/libthai.so.0 "$GITHUB_WORKSPACE"/squashfs-root/usr/lib/
|
||||
cp --dereference /usr/lib/"$LIBARCH"/libstdc++.so.6 "$GITHUB_WORKSPACE"/squashfs-root/usr/optional/libstdc++/libstdc++.so.6
|
||||
cp --dereference /lib/"$LIBARCH"/libgcc_s.so.1 "$GITHUB_WORKSPACE"/squashfs-root/usr/optional/libgcc_s/libgcc_s.so.1
|
||||
|
||||
@@ -20,6 +20,7 @@ cmake \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DPACKAGE_MODE=TRUE \
|
||||
-DWAYLAND_API=TRUE \
|
||||
-DDISABLE_ADVANCE_SIMD=TRUE \
|
||||
-DCMAKE_INSTALL_LIBDIR="/tmp/" \
|
||||
-DCMAKE_INSTALL_DATADIR="/tmp/" \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import yaml
|
||||
|
||||
# Assumes this is ran from the root of the repository
|
||||
file_path = "./bin/GameIndex.yaml"
|
||||
file_path = "./bin/resources/GameIndex.yaml"
|
||||
|
||||
# These settings have to be manually kept in sync with the emulator code unfortunately.
|
||||
# up to date validation should ALWAYS be provided via the application!
|
||||
|
||||
@@ -79,6 +79,7 @@ oprofile_data/
|
||||
/bin/PCSX2-linux.sh
|
||||
/bin/GS*.txt
|
||||
/bin/bios
|
||||
/bin/cache
|
||||
/bin/dumps
|
||||
/bin/help
|
||||
/bin/inis
|
||||
|
||||
+4
-13
@@ -1,13 +1,9 @@
|
||||
# Project Name
|
||||
project(Pcsx2)
|
||||
|
||||
# Debian-based distributions require at least 2.8.5 due to multiarch.
|
||||
# Bumping up to 3.0 seems reasonable at this point, and will let us modernize
|
||||
# things a bit.
|
||||
#
|
||||
# Setting it to a range tells it that it supports the features on the newer
|
||||
# versions as well, avoiding setting policies.
|
||||
cmake_minimum_required(VERSION 3.10...3.17)
|
||||
cmake_minimum_required(VERSION 3.11...3.22)
|
||||
|
||||
# Project Name
|
||||
project(Pcsx2)
|
||||
|
||||
# Variable to check that people use the good file
|
||||
set(TOP_CMAKE_WAS_SOURCED TRUE)
|
||||
@@ -58,11 +54,6 @@ endif()
|
||||
|
||||
# Install some files to ease package creation
|
||||
if(PACKAGE_MODE)
|
||||
if(NOT DISABLE_CHEATS_ZIP)
|
||||
INSTALL(FILES "${CMAKE_SOURCE_DIR}/bin/cheats_ws.zip" DESTINATION "${CMAKE_INSTALL_DATADIR}/PCSX2")
|
||||
endif()
|
||||
INSTALL(FILES "${CMAKE_SOURCE_DIR}/bin/GameIndex.yaml" DESTINATION "${CMAKE_INSTALL_DATADIR}/PCSX2")
|
||||
|
||||
# set categories depending on system/distribution in pcsx2.desktop
|
||||
if(openSUSE)
|
||||
set(PCSX2_MENU_CATEGORIES "System;Emulator;")
|
||||
|
||||
@@ -57,8 +57,6 @@ Sys_TakeSnapshot = F8
|
||||
Sys_RenderswitchToggle = F9
|
||||
|
||||
Sys_LoggingToggle = F10
|
||||
# The FreezeGS function is currently disabled internally.
|
||||
Sys_FreezeGS = F11
|
||||
Sys_RecordingToggle = F12
|
||||
|
||||
GSwindow_CycleAspectRatio = F6
|
||||
|
||||
@@ -48,6 +48,12 @@ PAPX-90223:
|
||||
PAPX-90231:
|
||||
name: "Kaitou Sly Cooper [Demo, Taikenban]"
|
||||
region: "NTSC-J"
|
||||
PAPX-90506:
|
||||
name: "Dark Chronicle[Demo, Taikenban]"
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
PAPX-90512:
|
||||
name: "Gran Turismo 4 - Toyota Prius [Trial]"
|
||||
region: "NTSC-J"
|
||||
@@ -2338,9 +2344,10 @@ SCES-51135:
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
roundModes:
|
||||
vuRoundMode: 0 # Fixes SPS.
|
||||
vuRoundMode: 1 # Fixes SPS.
|
||||
eeRoundMode: 0 # Fixes textures on the doors.
|
||||
clampModes:
|
||||
vuClampMode: 2 # Fix other SPS.
|
||||
vuClampMode: 0 # Fix other SPS.
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes objects disappearing.
|
||||
SCES-51159:
|
||||
@@ -2371,6 +2378,8 @@ SCES-51190:
|
||||
name: "Dark Chronicle"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCES-51224:
|
||||
name: "War of the Monsters"
|
||||
region: "PAL-E"
|
||||
@@ -2487,9 +2496,10 @@ SCES-51685:
|
||||
name: "Primal"
|
||||
region: "PAL-E-R"
|
||||
roundModes:
|
||||
vuRoundMode: 0 # Fixes SPS.
|
||||
vuRoundMode: 1 # Fixes SPS.
|
||||
eeRoundMode: 0 # Fixes textures on the doors.
|
||||
clampModes:
|
||||
vuClampMode: 2 # Fix other SPS.
|
||||
vuClampMode: 0 # Fix other SPS.
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes objects disappearing.
|
||||
SCES-51706:
|
||||
@@ -3692,6 +3702,8 @@ SCKA-20014:
|
||||
name: "Dark Cloud 2"
|
||||
region: "NTSC-K"
|
||||
compat: 5
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCKA-20015:
|
||||
name: "Time Crisis 3"
|
||||
region: "NTSC-K"
|
||||
@@ -4260,6 +4272,8 @@ SCPS-15032:
|
||||
SCPS-15033:
|
||||
name: "Dark Chronicle"
|
||||
region: "NTSC-J"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCPS-15034:
|
||||
name: "This is Football 2003"
|
||||
region: "NTSC-J"
|
||||
@@ -4746,6 +4760,8 @@ SCPS-19214:
|
||||
SCPS-19215:
|
||||
name: "Dark Chronicle [PlayStation 2 The Best]"
|
||||
region: "NTSC-J"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCPS-19251:
|
||||
name: "Wild ARMs - Alter Code F [PlayStation 2 The Best]"
|
||||
region: "NTSC-J"
|
||||
@@ -4805,6 +4821,8 @@ SCPS-19305:
|
||||
SCPS-19306:
|
||||
name: "Dark Chronicle [PlayStation 2 The Best]"
|
||||
region: "NTSC-J"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCPS-19307:
|
||||
name: "Popolocrois - The First Adventure [PlayStation 2 The Best]"
|
||||
region: "NTSC-J"
|
||||
@@ -5159,6 +5177,8 @@ SCPS-55047:
|
||||
SCPS-55048:
|
||||
name: "Dark Chronicle"
|
||||
region: "NTSC-J"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCPS-55049:
|
||||
name: "King of Fighters 2000, The"
|
||||
region: "NTSC-J"
|
||||
@@ -5422,9 +5442,10 @@ SCUS-97142:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
roundModes:
|
||||
vuRoundMode: 0 # Fixes SPS.
|
||||
vuRoundMode: 1 # Fixes SPS.
|
||||
eeRoundMode: 0 # Fixes textures on the doors.
|
||||
clampModes:
|
||||
vuClampMode: 2 # Fix other SPS.
|
||||
vuClampMode: 0 # Fix other SPS.
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes objects disappearing.
|
||||
SCUS-97144:
|
||||
@@ -5655,6 +5676,8 @@ SCUS-97213:
|
||||
name: "Dark Cloud 2"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCUS-97214:
|
||||
name: "NCAA GameBreaker 2003"
|
||||
region: "NTSC-U"
|
||||
@@ -5691,9 +5714,10 @@ SCUS-97225:
|
||||
name: "Primal [Demo]"
|
||||
region: "NTSC-U"
|
||||
roundModes:
|
||||
vuRoundMode: 0 # Fixes SPS.
|
||||
vuRoundMode: 1 # Fixes SPS.
|
||||
eeRoundMode: 0 # Fixes textures on the doors.
|
||||
clampModes:
|
||||
vuClampMode: 2 # Fix other SPS.
|
||||
vuClampMode: 0 # Fix other SPS.
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes objects disappearing.
|
||||
SCUS-97226:
|
||||
@@ -5705,6 +5729,8 @@ SCUS-97227:
|
||||
SCUS-97229:
|
||||
name: "Dark Cloud 2 [Demo]"
|
||||
region: "NTSC-U"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes textbox.
|
||||
SCUS-97230:
|
||||
name: "SOCOM - U.S. Navy SEALs (Online)"
|
||||
region: "NTSC-U"
|
||||
@@ -7060,6 +7086,8 @@ SLAJ-35003:
|
||||
SLED-50117:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Demo] [Zone of the Enders Bonus Disc]"
|
||||
region: "PAL-E"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLED-50359:
|
||||
name: "Devil May Cry [Demo]"
|
||||
region: "PAL-E"
|
||||
@@ -8004,12 +8032,18 @@ SLES-50383:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "PAL-M3"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50384:
|
||||
name: "Project Zero II"
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "PAL-I"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50385:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "PAL-S"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50386:
|
||||
name: "Crash Bandicoot - Wrath of Cortex"
|
||||
region: "PAL-M6"
|
||||
@@ -9468,6 +9502,8 @@ SLES-51145:
|
||||
SLES-51150:
|
||||
name: "Scrabble Interactive - 2003 Edition"
|
||||
region: "PAL-M3"
|
||||
clampModes:
|
||||
vuClampMode: 0 # Fixes missing UI and game elements.
|
||||
SLES-51151:
|
||||
name: "NHL 2003"
|
||||
region: "PAL-M6"
|
||||
@@ -12414,6 +12450,8 @@ SLES-52710:
|
||||
SLES-52711:
|
||||
name: "Titeuf Mega Compet"
|
||||
region: "PAL-F"
|
||||
gameFixes:
|
||||
- GIFFIFOHack # Fixes corrupted textures.
|
||||
patches:
|
||||
958e5086:
|
||||
content: |-
|
||||
@@ -13097,6 +13135,14 @@ SLES-53021:
|
||||
SLES-53022:
|
||||
name: "ESPN NBA 2K5"
|
||||
region: "PAL-E"
|
||||
gameFixes:
|
||||
- SkipMPEGHack # Fixes videos.
|
||||
patches:
|
||||
CA37B42E:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,0034DA98,word,00000000
|
||||
SLES-53023:
|
||||
name: "RTL Ski Jump 2005"
|
||||
region: "PAL-E-G"
|
||||
@@ -13976,6 +14022,8 @@ SLES-53491:
|
||||
SLES-53492:
|
||||
name: "Total Overdose"
|
||||
region: "PAL-M5"
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes SPS.
|
||||
SLES-53494:
|
||||
name: "Spongebob SquarePants - Lights, Camera, PANTS!"
|
||||
region: "PAL-E"
|
||||
@@ -14513,6 +14561,12 @@ SLES-53686:
|
||||
SLES-53687:
|
||||
name: "NBA 2K6"
|
||||
region: "PAL-M5"
|
||||
patches:
|
||||
04808D11:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,00441ff8,word,00000000
|
||||
SLES-53689:
|
||||
name: "World Poker Tour 2K6"
|
||||
region: "PAL-M3"
|
||||
@@ -15614,6 +15668,12 @@ SLES-54209:
|
||||
SLES-54210:
|
||||
name: "NBA 2K7"
|
||||
region: "PAL-M5"
|
||||
patches:
|
||||
04808D11:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,0044f0f8,word,00000000
|
||||
SLES-54211:
|
||||
name: "NHL 2K7"
|
||||
region: "PAL-M5"
|
||||
@@ -18609,6 +18669,8 @@ SLES-82009:
|
||||
name: "Metal Gear Solid 2 - Substance"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLES-82010:
|
||||
name: "Metal Gear Solid 2, Document of"
|
||||
region: "PAL-E"
|
||||
@@ -18897,6 +18959,12 @@ SLKA-25043:
|
||||
name: "Virtua Fighter 4 - Evolution"
|
||||
region: "NTSC-K"
|
||||
compat: 5
|
||||
SLKA-25044:
|
||||
name: "Robotech: Battlecry"
|
||||
region: "NTSC-K"
|
||||
SLKA-25045:
|
||||
name: "The King of Fighters 2000"
|
||||
region: "NTSC-K"
|
||||
SLKA-25046:
|
||||
name: "Dragon Ball Z"
|
||||
region: "NTSC-K"
|
||||
@@ -19102,12 +19170,18 @@ SLKA-25202:
|
||||
memcardFilters:
|
||||
- "SLKA-25201"
|
||||
- "SLKA-25202"
|
||||
SLKA-25203:
|
||||
name: "World Soccer Winning Eleven 8 International"
|
||||
region: "NTSC-K"
|
||||
SLKA-25204:
|
||||
name: "Showdown - Legends of Wrestling"
|
||||
region: "NTSC-K"
|
||||
SLKA-25205:
|
||||
name: "Dragon Ball Z 3"
|
||||
region: "NTSC-K"
|
||||
SLKA-25206:
|
||||
name: "Burnout 3: Takedown"
|
||||
region: "NTSC-K"
|
||||
SLKA-25207:
|
||||
name: "Sakura Taisen V - Episode 0 - Samurai Girl of Wild"
|
||||
region: "NTSC-K"
|
||||
@@ -19215,6 +19289,9 @@ SLKA-25259:
|
||||
SLKA-25261:
|
||||
name: "Tsukiyo ni Saraba"
|
||||
region: "NTSC-K"
|
||||
SLKA-25262:
|
||||
name: "World Soccer Winning Eleven 8 International"
|
||||
region: "NTSC-K"
|
||||
SLKA-25263:
|
||||
name: "NanoBreaker"
|
||||
region: "NTSC-J"
|
||||
@@ -19356,6 +19433,9 @@ SLKA-25331:
|
||||
SLKA-25333:
|
||||
name: "Metal Slug Complete"
|
||||
region: "NTSC-K"
|
||||
SLKA-25335:
|
||||
name: "Shadow the Hedgehog"
|
||||
region: "NTSC-K"
|
||||
SLKA-25341:
|
||||
name: "Driver - Parallel Lines"
|
||||
region: "NTSC-K"
|
||||
@@ -19464,6 +19544,8 @@ SLKA-35001:
|
||||
name: "Metal Gear Solid 2 Substance"
|
||||
region: "NTSC-K"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLKA-35003:
|
||||
name: "Sakura Taisen - Atsuki Chishioni"
|
||||
region: "NTSC-K"
|
||||
@@ -19577,6 +19659,9 @@ SLPM-55131:
|
||||
SLPM-55138:
|
||||
name: "Harukanaru Toki no Naka de - Yume no Ukihashi Special"
|
||||
region: "NTSC-J"
|
||||
SLPM-55146:
|
||||
name: "Jikkyou Powerful Pro Yakyuu 2009"
|
||||
region: "NTSC-J"
|
||||
SLPM-55148:
|
||||
name: "007: Nagusame no Houshuu"
|
||||
region: "NTSC-J"
|
||||
@@ -20889,6 +20974,8 @@ SLPM-62485:
|
||||
SLPM-62486:
|
||||
name: "Chou Saisoku! Zokusha King B.U. [Simple Series DX]"
|
||||
region: "NTSC-J"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes white shadows.
|
||||
SLPM-62488:
|
||||
name: "Hot Gimmick Cosplay Mahjong"
|
||||
region: "NTSC-J"
|
||||
@@ -22010,9 +22097,13 @@ SLPM-65076:
|
||||
SLPM-65077:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Premium Package]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-65078:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-65080:
|
||||
name: "Tokimeki Memorial 3"
|
||||
region: "NTSC-J"
|
||||
@@ -25689,6 +25780,13 @@ SLPM-66294:
|
||||
SLPM-66295:
|
||||
name: "Yamiyo ni Sasayaku - Tantei Sagara Kyouichirou [Limited Edition]"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
23A9A026:
|
||||
content: |-
|
||||
author=PSI, Jelta
|
||||
// Game does weird stack manipulation, causing data sent to the IOP to be corrupted unless EE data cache is enabled.
|
||||
// This patch skips over the stack code, allowing the game to boot.
|
||||
patch=1,EE,00156888,word,10000003
|
||||
SLPM-66296:
|
||||
name: "Yamiyo ni Sasayaku - Tantei Sagara Kyouichirou"
|
||||
region: "NTSC-J"
|
||||
@@ -26001,6 +26099,13 @@ SLPM-66398:
|
||||
SLPM-66399:
|
||||
name: "Samurai 7 [Limited Edition]"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
836F4606:
|
||||
content: |-
|
||||
author=PSI, Jelta
|
||||
// Game does weird stack manipulation, causing data sent to the IOP to be corrupted unless EE data cache is enabled.
|
||||
// This patch skips over the stack code, allowing the game to boot.
|
||||
patch=1,EE,00157F78,word,10000003
|
||||
SLPM-66400:
|
||||
name: "Samurai 7"
|
||||
region: "NTSC-J"
|
||||
@@ -26168,6 +26273,12 @@ SLPM-66450:
|
||||
SLPM-66451:
|
||||
name: "Major League Baseball 2K6"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
F0393708:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,0035d8b8,word,00000000
|
||||
SLPM-66452:
|
||||
name: "Kamaitachi no Yoru 3"
|
||||
region: "NTSC-J"
|
||||
@@ -26368,6 +26479,8 @@ SLPM-66502:
|
||||
SLPM-66503:
|
||||
name: "Metal Gear Solid 2 [Mega Hits]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-66504:
|
||||
name: "Onimusha 2 [Mega Hits]"
|
||||
region: "NTSC-J"
|
||||
@@ -27311,6 +27424,8 @@ SLPM-66791:
|
||||
SLPM-66792:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [20th Anniversary Edition]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-66794:
|
||||
name: "Metal Gear Solid 3 - Snake Eater [20th Anniversary Edition]"
|
||||
region: "NTSC-J"
|
||||
@@ -27827,6 +27942,9 @@ SLPM-66978:
|
||||
name: "Persona 4 [Konami-style Special Edition]"
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
SLPM-66980:
|
||||
name: "Gin no Eclipse"
|
||||
region: "NTSC-J"
|
||||
SLPM-66987:
|
||||
name: "Kowloon Youma Gakuen Ki [Best Version]"
|
||||
region: "NTSC-J"
|
||||
@@ -27870,6 +27988,8 @@ SLPM-67000:
|
||||
SLPM-67002:
|
||||
name: "Metal Gear Solid 2 - Substance"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-67003:
|
||||
name: "Sakura Taisen - Atsuki Chishioni"
|
||||
region: "NTSC-J"
|
||||
@@ -28419,6 +28539,8 @@ SLPS-20048:
|
||||
SLPS-20050:
|
||||
name: "Happy! Happy!! Boarders in Hokkaidou Rusutsu Resort"
|
||||
region: "NTSC-J"
|
||||
speedHacks:
|
||||
mvuFlagSpeedHack: 0 # Fixes graphical issues.
|
||||
SLPS-20051:
|
||||
name: "Hissatsu Pachinko Station V"
|
||||
region: "NTSC-J"
|
||||
@@ -28648,6 +28770,15 @@ SLPS-20187:
|
||||
name: "Black Matrix 2"
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
SLPS-20190:
|
||||
name: "Netsu Chu! Pro Yakyuu 2002"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
78E100AD:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang before ingame
|
||||
patch=1,EE,001022c0,word,00000000
|
||||
SLPS-20194:
|
||||
name: "Underwater Unit"
|
||||
region: "NTSC-J"
|
||||
@@ -28735,7 +28866,7 @@ SLPS-20272:
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
SLPS-20273:
|
||||
name: "Netsu Chu! Pro Baseball 2003"
|
||||
name: "Netsu Chu! Pro Yakyuu 2003"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
34fb1fb7:
|
||||
@@ -28821,8 +28952,14 @@ SLPS-20321:
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
SLPS-20322:
|
||||
name: "Netsu Chu! Pro Baseball 2003 Fall"
|
||||
name: "Netsu Chu! Pro Yakyuu 2003: Aki no Night Matsuri"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
D3DAF7F4:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang before ingame
|
||||
patch=1,EE,00102398,word,00000000
|
||||
SLPS-20323:
|
||||
name: "Pochi to Nyaa"
|
||||
region: "NTSC-J"
|
||||
@@ -33280,6 +33417,8 @@ SLUS-20144:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLUS-20145:
|
||||
name: "Ring of Red"
|
||||
region: "NTSC-U"
|
||||
@@ -34981,6 +35120,8 @@ SLUS-20554:
|
||||
name: "Metal Gear Solid 2 - Substance"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLUS-20555:
|
||||
name: "Reel Fishing 3"
|
||||
region: "NTSC-U"
|
||||
@@ -38180,6 +38321,8 @@ SLUS-21283:
|
||||
name: "Total Overdose - A Gunslinger's Tale in Mexico"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- VUKickstartHack # Fixes SPS.
|
||||
SLUS-21284:
|
||||
name: "Nicktoons Unite!"
|
||||
region: "NTSC-U"
|
||||
@@ -39328,6 +39471,12 @@ SLUS-21560:
|
||||
SLUS-21561:
|
||||
name: "Major League Baseball 2K7"
|
||||
region: "NTSC-U"
|
||||
patches:
|
||||
73D6D171:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,003CDE10,word,00000000
|
||||
SLUS-21563:
|
||||
name: "Horsez"
|
||||
region: "NTSC-U"
|
||||
@@ -40626,6 +40775,12 @@ SLUS-21870:
|
||||
SLUS-21871:
|
||||
name: "Major League Baseball 2K9"
|
||||
region: "NTSC-U"
|
||||
patches:
|
||||
72905002:
|
||||
content: |-
|
||||
author=Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,003CC5D8,word,00000000
|
||||
SLUS-21872:
|
||||
name: "MTV Pimp my Ride - Street Racing"
|
||||
region: "NTSC-U"
|
||||
@@ -40904,6 +41059,12 @@ SLUS-21944:
|
||||
SLUS-21945:
|
||||
name: "Major League Baseball 2K11"
|
||||
region: "NTSC-U"
|
||||
patches:
|
||||
3D621C56:
|
||||
content: |-
|
||||
author=By Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,003CFE58,word,00000000
|
||||
SLUS-21946:
|
||||
name: "Madden NFL 12"
|
||||
region: "NTSC-U"
|
||||
@@ -40926,6 +41087,12 @@ SLUS-21950:
|
||||
SLUS-21951:
|
||||
name: "Major League Baseball 2K12"
|
||||
region: "NTSC-U"
|
||||
patches:
|
||||
C26C70A9:
|
||||
content: |-
|
||||
author=By Prafull
|
||||
comment=fixes hang at start
|
||||
patch=1,EE,003D4098,word,00000000
|
||||
SLUS-21954:
|
||||
name: "FIFA 13"
|
||||
region: "NTSC-U" # Latin America with En,Fr,Es
|
||||
@@ -41094,6 +41261,8 @@ SLUS-29003:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Demo]"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes broken half-bottom artifacts.
|
||||
SLUS-29004:
|
||||
name: "Unison & Dead or Alive 2 Hardcore [Demo]"
|
||||
region: "NTSC-U"
|
||||
@@ -43,7 +43,6 @@ option(BUILD_REPLAY_LOADERS "Build GS replayer to ease testing (developer option
|
||||
# Path and lib option
|
||||
#-------------------------------------------------------------------------------
|
||||
option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)")
|
||||
option(DISABLE_CHEATS_ZIP "Disable including the cheats_ws.zip file")
|
||||
option(DISABLE_PCSX2_WRAPPER "Disable including the PCSX2-linux.sh file")
|
||||
option(DISABLE_SETCAP "Do not set files capabilities")
|
||||
option(XDG_STD "Use XDG standard path instead of the standard PCSX2 path")
|
||||
@@ -59,7 +58,7 @@ endif()
|
||||
if(PACKAGE_MODE)
|
||||
# Compile all source codes with those defines
|
||||
list(APPEND PCSX2_DEFS
|
||||
GAMEINDEX_DIR_COMPILATION=${CMAKE_INSTALL_FULL_DATADIR}/PCSX2
|
||||
PCSX2_APP_DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/PCSX2"
|
||||
DOC_DIR_COMPILATION=${CMAKE_INSTALL_FULL_DOCDIR})
|
||||
endif()
|
||||
|
||||
@@ -73,7 +72,7 @@ endif()
|
||||
#-------------------------------------------------------------------------------
|
||||
option(USE_ASAN "Enable address sanitizer")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
set(USE_CLANG TRUE)
|
||||
message(STATUS "Building with Clang/LLVM.")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
|
||||
+38
-36
@@ -2,16 +2,13 @@
|
||||
#
|
||||
# This will define:
|
||||
#
|
||||
# WAYLAND_FOUND - True if Wayland is found
|
||||
# WAYLAND_LIBRARIES - Link these to use Wayland
|
||||
# WAYLAND_INCLUDE_DIR - Include directory for Wayland
|
||||
# WAYLAND_DEFINITIONS - Compiler flags for using Wayland
|
||||
# Wayland_FOUND - True if Wayland is found
|
||||
#
|
||||
# In addition the following more fine grained variables will be defined:
|
||||
#
|
||||
# WAYLAND_CLIENT_FOUND WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
|
||||
# WAYLAND_SERVER_FOUND WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
|
||||
# WAYLAND_EGL_FOUND WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
|
||||
# The following imported targets:
|
||||
# Wayland::Client - Imported Client
|
||||
# Wayland::Server - Imported Server
|
||||
# Wayland::Egl - Imported Egl
|
||||
# Wayland::Cursor - Imported Cursor
|
||||
#
|
||||
# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
#
|
||||
@@ -24,39 +21,44 @@ IF (NOT WIN32)
|
||||
SET(WAYLAND_FIND_QUIETLY TRUE)
|
||||
ENDIF ()
|
||||
|
||||
# Use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
FIND_PACKAGE(PkgConfig)
|
||||
PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
|
||||
FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h)
|
||||
FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h)
|
||||
FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h)
|
||||
FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h)
|
||||
|
||||
SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
|
||||
|
||||
FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
|
||||
FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
|
||||
FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
|
||||
FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
|
||||
|
||||
FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
|
||||
FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
|
||||
FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
|
||||
FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
|
||||
|
||||
set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR})
|
||||
|
||||
set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
|
||||
|
||||
list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR)
|
||||
FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client)
|
||||
FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server)
|
||||
FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl)
|
||||
FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR)
|
||||
# FIND_PACKAGE_HANDLE_STANDARD_ARGS is just meant to find the main package and set package found. Not set variables or find individual libs
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Wayland REQUIRED_VARS
|
||||
WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR
|
||||
WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR
|
||||
WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR
|
||||
WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARIES AND NOT TARGET Wayland::Client)
|
||||
add_library(Wayland::Client UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Client PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CLIENT_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_SERVER_INCLUDE_DIR AND WAYLAND_SERVER_LIBRARIES AND NOT TARGET Wayland::Server)
|
||||
add_library(Wayland::Server UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Server PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_SERVER_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_SERVER_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARIES AND NOT TARGET Wayland::Egl)
|
||||
add_library(Wayland::Egl UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Egl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_EGL_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_EGL_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_CURSOR_INCLUDE_DIR AND WAYLAND_CURSOR_LIBRARIES AND NOT TARGET Wayland::Cursor)
|
||||
add_library(Wayland::Cursor UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Cursor PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CURSOR_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CURSOR_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES
|
||||
WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
|
||||
WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
|
||||
WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
|
||||
|
||||
@@ -162,11 +162,7 @@ else()
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(X11 REQUIRED)
|
||||
make_imported_target_if_missing(X11::X11 X11)
|
||||
endif()
|
||||
if(APPLE)
|
||||
check_lib(GIO gio-2.0 gio/gio.h)
|
||||
elseif(UNIX)
|
||||
# Most plugins (if not all) and PCSX2 core need gtk2, so set the required flags
|
||||
|
||||
if (GTK2_API)
|
||||
find_package(GTK2 REQUIRED gtk)
|
||||
alias_library(GTK::gtk GTK2::gtk)
|
||||
|
||||
@@ -20,12 +20,14 @@ target_sources(common PRIVATE
|
||||
FastJmp.cpp
|
||||
GL/Context.cpp
|
||||
GL/StreamBuffer.cpp
|
||||
FileSystem.cpp
|
||||
IniInterface.cpp
|
||||
Mutex.cpp
|
||||
Misc.cpp
|
||||
PathUtils.cpp
|
||||
PrecompiledHeader.cpp
|
||||
Perf.cpp
|
||||
ProgressCallback.cpp
|
||||
pxStreams.cpp
|
||||
pxTranslate.cpp
|
||||
RwMutex.cpp
|
||||
@@ -33,6 +35,7 @@ target_sources(common PRIVATE
|
||||
SettingsWrapper.cpp
|
||||
StringHelpers.cpp
|
||||
StringUtil.cpp
|
||||
Timer.cpp
|
||||
ThreadTools.cpp
|
||||
WindowInfo.cpp
|
||||
emitter/bmi.cpp
|
||||
@@ -69,6 +72,7 @@ target_sources(common PRIVATE
|
||||
EventSource.h
|
||||
Exceptions.h
|
||||
FastJmp.h
|
||||
FileSystem.h
|
||||
General.h
|
||||
GL/Context.h
|
||||
GL/StreamBuffer.h
|
||||
@@ -77,6 +81,7 @@ target_sources(common PRIVATE
|
||||
Path.h
|
||||
PageFaultSource.h
|
||||
PrecompiledHeader.h
|
||||
ProgressCallback.h
|
||||
pxForwardDefs.h
|
||||
pxStreams.h
|
||||
RedtapeWindows.h
|
||||
@@ -88,6 +93,7 @@ target_sources(common PRIVATE
|
||||
SettingsWrapper.h
|
||||
StringHelpers.h
|
||||
StringUtil.h
|
||||
Timer.h
|
||||
Threading.h
|
||||
TraceLog.h
|
||||
WindowInfo.h
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
pxAssert(EnumIsValid(id)); \
|
||||
} \
|
||||
\
|
||||
extern const wxChar* EnumToString(enumName id)
|
||||
extern const char* EnumToString(enumName id)
|
||||
|
||||
class pxEnumEnd_t
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "Pcsx2Defs.h"
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define FS_OSPATH_SEPARATOR_CHARACTER '\\'
|
||||
#define FS_OSPATH_SEPARATOR_STR "\\"
|
||||
#else
|
||||
#define FS_OSPATH_SEPARATOR_CHARACTER '/'
|
||||
#define FS_OSPATH_SEPARATOR_STR "/"
|
||||
#endif
|
||||
|
||||
enum FILESYSTEM_FILE_ATTRIBUTES
|
||||
{
|
||||
FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY = 1,
|
||||
FILESYSTEM_FILE_ATTRIBUTE_READ_ONLY = 2,
|
||||
FILESYSTEM_FILE_ATTRIBUTE_COMPRESSED = 4,
|
||||
};
|
||||
|
||||
enum FILESYSTEM_FIND_FLAGS
|
||||
{
|
||||
FILESYSTEM_FIND_RECURSIVE = (1 << 0),
|
||||
FILESYSTEM_FIND_RELATIVE_PATHS = (1 << 1),
|
||||
FILESYSTEM_FIND_HIDDEN_FILES = (1 << 2),
|
||||
FILESYSTEM_FIND_FOLDERS = (1 << 3),
|
||||
FILESYSTEM_FIND_FILES = (1 << 4),
|
||||
FILESYSTEM_FIND_KEEP_ARRAY = (1 << 5),
|
||||
};
|
||||
|
||||
struct FILESYSTEM_STAT_DATA
|
||||
{
|
||||
std::time_t ModificationTime;
|
||||
s64 Size;
|
||||
u32 Attributes;
|
||||
};
|
||||
|
||||
struct FILESYSTEM_FIND_DATA
|
||||
{
|
||||
std::time_t ModificationTime;
|
||||
std::string FileName;
|
||||
s64 Size;
|
||||
u32 Attributes;
|
||||
};
|
||||
|
||||
namespace FileSystem
|
||||
{
|
||||
|
||||
using FindResultsArray = std::vector<FILESYSTEM_FIND_DATA>;
|
||||
|
||||
/// Builds a path relative to the specified file
|
||||
std::string BuildRelativePath(const std::string_view& filename, const std::string_view& new_filename);
|
||||
|
||||
/// Joins path components together, producing a new path.
|
||||
std::string JoinPath(const std::string_view& base, const std::string_view& next);
|
||||
|
||||
/// Sanitizes a filename for use in a filesystem.
|
||||
void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes /* = true */);
|
||||
void SanitizeFileName(std::string& Destination, bool StripSlashes = true);
|
||||
|
||||
/// Returns true if the specified path is an absolute path (C:\Path on Windows or /path on Unix).
|
||||
bool IsAbsolutePath(const std::string_view& path);
|
||||
|
||||
/// Returns a view of the extension of a filename.
|
||||
std::string_view GetExtension(const std::string_view& path);
|
||||
|
||||
/// Removes the extension of a filename.
|
||||
std::string_view StripExtension(const std::string_view& path);
|
||||
|
||||
/// Replaces the extension of a filename with another.
|
||||
std::string ReplaceExtension(const std::string_view& path, const std::string_view& new_extension);
|
||||
|
||||
/// Returns the display name of a filename. Usually this is the same as the path.
|
||||
std::string GetDisplayNameFromPath(const std::string_view& path);
|
||||
|
||||
/// Returns the directory component of a filename.
|
||||
std::string_view GetPathDirectory(const std::string_view& path);
|
||||
|
||||
/// Returns the filename component of a filename.
|
||||
std::string_view GetFileNameFromPath(const std::string_view& path);
|
||||
|
||||
/// Returns the file title (less the extension and path) from a filename.
|
||||
std::string_view GetFileTitleFromPath(const std::string_view& path);
|
||||
|
||||
/// Returns a list of "root directories" (i.e. root/home directories on Linux, drive letters on Windows).
|
||||
std::vector<std::string> GetRootDirectoryList();
|
||||
|
||||
/// Search for files
|
||||
bool FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results);
|
||||
|
||||
/// Stat file
|
||||
bool StatFile(const char* path, struct stat* st);
|
||||
bool StatFile(std::FILE* fp, struct stat* st);
|
||||
bool StatFile(const char* path, FILESYSTEM_STAT_DATA* pStatData);
|
||||
bool StatFile(std::FILE* fp, FILESYSTEM_STAT_DATA* pStatData);
|
||||
s64 GetPathFileSize(const char* path);
|
||||
|
||||
/// File exists?
|
||||
bool FileExists(const char* path);
|
||||
|
||||
/// Directory exists?
|
||||
bool DirectoryExists(const char* path);
|
||||
|
||||
/// Delete file
|
||||
bool DeleteFilePath(const char* path);
|
||||
|
||||
/// Rename file
|
||||
bool RenamePath(const char* OldPath, const char* NewPath);
|
||||
|
||||
/// open files
|
||||
using ManagedCFilePtr = std::unique_ptr<std::FILE, void (*)(std::FILE*)>;
|
||||
ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode);
|
||||
std::FILE* OpenCFile(const char* filename, const char* mode);
|
||||
int FSeek64(std::FILE* fp, s64 offset, int whence);
|
||||
s64 FTell64(std::FILE* fp);
|
||||
s64 FSize64(std::FILE* fp);
|
||||
|
||||
int OpenFDFile(const char* filename, int flags, int mode);
|
||||
|
||||
std::optional<std::vector<u8>> ReadBinaryFile(const char* filename);
|
||||
std::optional<std::vector<u8>> ReadBinaryFile(std::FILE* fp);
|
||||
std::optional<std::string> ReadFileToString(const char* filename);
|
||||
std::optional<std::string> ReadFileToString(std::FILE* fp);
|
||||
bool WriteBinaryFile(const char* filename, const void* data, size_t data_length);
|
||||
bool WriteFileToString(const char* filename, const std::string_view& sv);
|
||||
|
||||
/// creates a directory in the local filesystem
|
||||
/// if the directory already exists, the return value will be true.
|
||||
/// if Recursive is specified, all parent directories will be created
|
||||
/// if they do not exist.
|
||||
bool CreateDirectoryPath(const char* path, bool recursive);
|
||||
|
||||
/// Returns the path to the current executable.
|
||||
std::string GetProgramPath();
|
||||
|
||||
/// Retrieves the current working directory.
|
||||
std::string GetWorkingDirectory();
|
||||
|
||||
/// Sets the current working directory. Returns true if successful.
|
||||
bool SetWorkingDirectory(const char* path);
|
||||
|
||||
}; // namespace FileSystem
|
||||
@@ -225,6 +225,8 @@ namespace Path
|
||||
extern wxString Combine(const wxString& srcPath, const wxString& srcFile);
|
||||
extern wxString Combine(const wxDirName& srcPath, const wxFileName& srcFile);
|
||||
extern wxString Combine(const wxString& srcPath, const wxDirName& srcFile);
|
||||
extern std::string CombineStdString(const wxDirName& srcPath, const std::string_view& srcFile);
|
||||
extern std::string CombineStdString(const std::string_view& srcPath, const std::string_view& srcFile);
|
||||
extern wxString ReplaceExtension(const wxString& src, const wxString& ext);
|
||||
extern wxString ReplaceFilename(const wxString& src, const wxString& newfilename);
|
||||
extern wxString GetFilename(const wxString& src);
|
||||
|
||||
@@ -149,6 +149,21 @@ wxString Path::Combine(const wxString& srcPath, const wxDirName& srcFile)
|
||||
return (wxDirName(srcPath) + srcFile).ToString();
|
||||
}
|
||||
|
||||
std::string Path::CombineStdString(const wxDirName& srcPath, const std::string_view& srcFile)
|
||||
{
|
||||
const wxString wxResult((srcPath + wxString::FromUTF8(srcFile.data(), srcFile.length())).GetFullPath());
|
||||
const wxCharBuffer wxBuf(wxResult.ToUTF8());
|
||||
return std::string(wxBuf.data(), wxBuf.length());
|
||||
}
|
||||
|
||||
std::string Path::CombineStdString(const std::string_view& srcPath, const std::string_view& srcFile)
|
||||
{
|
||||
const wxDirName srcPathDir(wxString::FromUTF8(srcPath.data(), srcPath.length()));
|
||||
const wxString wxResult((srcPathDir + wxString::FromUTF8(srcFile.data(), srcFile.length())).GetFullPath());
|
||||
const wxCharBuffer wxBuf(wxResult.ToUTF8());
|
||||
return std::string(wxBuf.data(), wxBuf.length());
|
||||
}
|
||||
|
||||
// Replaces the extension of the file with the one given.
|
||||
// This function works for path names as well as file names.
|
||||
wxString Path::ReplaceExtension(const wxString& src, const wxString& ext)
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "ProgressCallback.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Console.h"
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
ProgressCallback::~ProgressCallback() {}
|
||||
|
||||
void ProgressCallback::SetFormattedStatusText(const char* Format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, Format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(Format, ap));
|
||||
va_end(ap);
|
||||
|
||||
SetStatusText(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedError(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
DisplayError(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedWarning(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
DisplayWarning(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedInformation(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
DisplayInformation(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedDebugMessage(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
DisplayDebugMessage(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedModalError(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
ModalError(str.c_str());
|
||||
}
|
||||
|
||||
bool ProgressCallback::DisplayFormattedModalConfirmation(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
return ModalConfirmation(str.c_str());
|
||||
}
|
||||
|
||||
void ProgressCallback::DisplayFormattedModalInformation(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
const std::string str(StringUtil::StdStringFromFormatV(format, ap));
|
||||
va_end(ap);
|
||||
|
||||
ModalInformation(str.c_str());
|
||||
}
|
||||
|
||||
class NullProgressCallbacks final : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
void PushState() override {}
|
||||
void PopState() override {}
|
||||
|
||||
bool IsCancelled() const override { return false; }
|
||||
bool IsCancellable() const override { return false; }
|
||||
|
||||
void SetCancellable(bool cancellable) override {}
|
||||
void SetTitle(const char* title) override {}
|
||||
void SetStatusText(const char* statusText) override {}
|
||||
void SetProgressRange(u32 range) override {}
|
||||
void SetProgressValue(u32 value) override {}
|
||||
void IncrementProgressValue() override {}
|
||||
|
||||
void DisplayError(const char* message) override { Console.Error("%s", message); }
|
||||
void DisplayWarning(const char* message) override { Console.Warning("%s", message); }
|
||||
void DisplayInformation(const char* message) override { Console.WriteLn("%s", message); }
|
||||
void DisplayDebugMessage(const char* message) override { DevCon.WriteLn("%s", message); }
|
||||
|
||||
void ModalError(const char* message) override { Console.Error(message); }
|
||||
bool ModalConfirmation(const char* message) override
|
||||
{
|
||||
Console.WriteLn("%s", message);
|
||||
return false;
|
||||
}
|
||||
void ModalInformation(const char* message) override { Console.WriteLn("%s", message); }
|
||||
};
|
||||
|
||||
static NullProgressCallbacks s_nullProgressCallbacks;
|
||||
ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks;
|
||||
|
||||
BaseProgressCallback::BaseProgressCallback()
|
||||
: m_cancellable(false)
|
||||
, m_cancelled(false)
|
||||
, m_progress_range(1)
|
||||
, m_progress_value(0)
|
||||
, m_base_progress_value(0)
|
||||
, m_saved_state(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
BaseProgressCallback::~BaseProgressCallback()
|
||||
{
|
||||
State* pNextState = m_saved_state;
|
||||
while (pNextState != NULL)
|
||||
{
|
||||
State* pCurrentState = pNextState;
|
||||
pNextState = pCurrentState->next_saved_state;
|
||||
delete pCurrentState;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProgressCallback::PushState()
|
||||
{
|
||||
State* pNewState = new State;
|
||||
pNewState->cancellable = m_cancellable;
|
||||
pNewState->status_text = m_status_text;
|
||||
pNewState->progress_range = m_progress_range;
|
||||
pNewState->progress_value = m_progress_value;
|
||||
pNewState->base_progress_value = m_base_progress_value;
|
||||
pNewState->next_saved_state = m_saved_state;
|
||||
m_saved_state = pNewState;
|
||||
}
|
||||
|
||||
void BaseProgressCallback::PopState()
|
||||
{
|
||||
pxAssert(m_saved_state);
|
||||
State* state = m_saved_state;
|
||||
m_saved_state = nullptr;
|
||||
|
||||
// impose the current position into the previous range
|
||||
const u32 new_progress_value =
|
||||
(m_progress_range != 0) ?
|
||||
static_cast<u32>(((float)m_progress_value / (float)m_progress_range) * (float)state->progress_range) :
|
||||
state->progress_value;
|
||||
|
||||
m_cancellable = state->cancellable;
|
||||
m_status_text = std::move(state->status_text);
|
||||
m_progress_range = state->progress_range;
|
||||
m_progress_value = new_progress_value;
|
||||
|
||||
m_base_progress_value = state->base_progress_value;
|
||||
m_saved_state = state->next_saved_state;
|
||||
delete state;
|
||||
}
|
||||
|
||||
bool BaseProgressCallback::IsCancelled() const
|
||||
{
|
||||
return m_cancelled;
|
||||
}
|
||||
|
||||
bool BaseProgressCallback::IsCancellable() const
|
||||
{
|
||||
return m_cancellable;
|
||||
}
|
||||
|
||||
void BaseProgressCallback::SetCancellable(bool cancellable)
|
||||
{
|
||||
m_cancellable = cancellable;
|
||||
}
|
||||
|
||||
void BaseProgressCallback::SetStatusText(const char* text)
|
||||
{
|
||||
m_status_text = text;
|
||||
}
|
||||
|
||||
void BaseProgressCallback::SetProgressRange(u32 range)
|
||||
{
|
||||
if (m_saved_state)
|
||||
{
|
||||
// impose the previous range on this range
|
||||
m_progress_range = m_saved_state->progress_range * range;
|
||||
m_base_progress_value = m_progress_value = m_saved_state->progress_value * range;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progress_range = range;
|
||||
m_progress_value = 0;
|
||||
m_base_progress_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProgressCallback::SetProgressValue(u32 value)
|
||||
{
|
||||
m_progress_value = m_base_progress_value + value;
|
||||
}
|
||||
|
||||
void BaseProgressCallback::IncrementProgressValue()
|
||||
{
|
||||
SetProgressValue((m_progress_value - m_base_progress_value) + 1);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "Pcsx2Defs.h"
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Progress callbacks, abstracts a blocking operation and allows it to report progress
|
||||
* without having any dependency on the UI.
|
||||
*/
|
||||
|
||||
class ProgressCallback
|
||||
{
|
||||
public:
|
||||
virtual ~ProgressCallback();
|
||||
|
||||
virtual void PushState() = 0;
|
||||
virtual void PopState() = 0;
|
||||
|
||||
virtual bool IsCancelled() const = 0;
|
||||
virtual bool IsCancellable() const = 0;
|
||||
|
||||
virtual void SetCancellable(bool cancellable) = 0;
|
||||
|
||||
virtual void SetTitle(const char* title) = 0;
|
||||
virtual void SetStatusText(const char* text) = 0;
|
||||
virtual void SetProgressRange(u32 range) = 0;
|
||||
virtual void SetProgressValue(u32 value) = 0;
|
||||
virtual void IncrementProgressValue() = 0;
|
||||
|
||||
void SetFormattedStatusText(const char* Format, ...);
|
||||
|
||||
virtual void DisplayError(const char* message) = 0;
|
||||
virtual void DisplayWarning(const char* message) = 0;
|
||||
virtual void DisplayInformation(const char* message) = 0;
|
||||
virtual void DisplayDebugMessage(const char* message) = 0;
|
||||
|
||||
virtual void ModalError(const char* message) = 0;
|
||||
virtual bool ModalConfirmation(const char* message) = 0;
|
||||
virtual void ModalInformation(const char* message) = 0;
|
||||
|
||||
void DisplayFormattedError(const char* format, ...);
|
||||
void DisplayFormattedWarning(const char* format, ...);
|
||||
void DisplayFormattedInformation(const char* format, ...);
|
||||
void DisplayFormattedDebugMessage(const char* format, ...);
|
||||
void DisplayFormattedModalError(const char* format, ...);
|
||||
bool DisplayFormattedModalConfirmation(const char* format, ...);
|
||||
void DisplayFormattedModalInformation(const char* format, ...);
|
||||
|
||||
public:
|
||||
static ProgressCallback* NullProgressCallback;
|
||||
};
|
||||
|
||||
class BaseProgressCallback : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
BaseProgressCallback();
|
||||
virtual ~BaseProgressCallback();
|
||||
|
||||
virtual void PushState() override;
|
||||
virtual void PopState() override;
|
||||
|
||||
virtual bool IsCancelled() const override;
|
||||
virtual bool IsCancellable() const override;
|
||||
|
||||
virtual void SetCancellable(bool cancellable) override;
|
||||
virtual void SetStatusText(const char* text) override;
|
||||
virtual void SetProgressRange(u32 range) override;
|
||||
virtual void SetProgressValue(u32 value) override;
|
||||
virtual void IncrementProgressValue() override;
|
||||
|
||||
protected:
|
||||
struct State
|
||||
{
|
||||
State* next_saved_state;
|
||||
std::string status_text;
|
||||
u32 progress_range;
|
||||
u32 progress_value;
|
||||
u32 base_progress_value;
|
||||
bool cancellable;
|
||||
};
|
||||
|
||||
bool m_cancellable;
|
||||
bool m_cancelled;
|
||||
std::string m_status_text;
|
||||
u32 m_progress_range;
|
||||
u32 m_progress_value;
|
||||
|
||||
u32 m_base_progress_value;
|
||||
|
||||
State* m_saved_state;
|
||||
};
|
||||
@@ -0,0 +1,347 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Timer.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "RedtapeWindows.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/thread_act.h>
|
||||
#include <mach/mach_port.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static double s_counter_frequency;
|
||||
static bool s_counter_initialized = false;
|
||||
|
||||
Timer::Value Timer::GetCurrentValue()
|
||||
{
|
||||
// even if this races, it should still result in the same value..
|
||||
if (!s_counter_initialized)
|
||||
{
|
||||
LARGE_INTEGER Freq;
|
||||
QueryPerformanceFrequency(&Freq);
|
||||
s_counter_frequency = static_cast<double>(Freq.QuadPart) / 1000000000.0;
|
||||
s_counter_initialized = true;
|
||||
}
|
||||
|
||||
Timer::Value ReturnValue;
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&ReturnValue));
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToNanoseconds(Timer::Value value)
|
||||
{
|
||||
return (static_cast<double>(value) / s_counter_frequency);
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToMilliseconds(Timer::Value value)
|
||||
{
|
||||
return ((static_cast<double>(value) / s_counter_frequency) / 1000000.0);
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToSeconds(Timer::Value value)
|
||||
{
|
||||
return ((static_cast<double>(value) / s_counter_frequency) / 1000000000.0);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertSecondsToValue(double s)
|
||||
{
|
||||
return static_cast<Value>((s * 1000000000.0) * s_counter_frequency);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
|
||||
{
|
||||
return static_cast<Value>((ms * 1000000.0) * s_counter_frequency);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
|
||||
{
|
||||
return static_cast<Value>(ns * s_counter_frequency);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Timer::Value Timer::GetCurrentValue()
|
||||
{
|
||||
struct timespec tv;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tv);
|
||||
return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000);
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToNanoseconds(Timer::Value value)
|
||||
{
|
||||
return static_cast<double>(value);
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToMilliseconds(Timer::Value value)
|
||||
{
|
||||
return (static_cast<double>(value) / 1000000.0);
|
||||
}
|
||||
|
||||
double Timer::ConvertValueToSeconds(Timer::Value value)
|
||||
{
|
||||
return (static_cast<double>(value) / 1000000000.0);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertSecondsToValue(double s)
|
||||
{
|
||||
return static_cast<Value>(s * 1000000000.0);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
|
||||
{
|
||||
return static_cast<Value>(ms * 1000000.0);
|
||||
}
|
||||
|
||||
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
|
||||
{
|
||||
return static_cast<Value>(ns);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Timer::Timer()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Timer::Reset()
|
||||
{
|
||||
m_tvStartValue = GetCurrentValue();
|
||||
}
|
||||
|
||||
double Timer::GetTimeSeconds() const
|
||||
{
|
||||
return ConvertValueToSeconds(GetCurrentValue() - m_tvStartValue);
|
||||
}
|
||||
|
||||
double Timer::GetTimeMilliseconds() const
|
||||
{
|
||||
return ConvertValueToMilliseconds(GetCurrentValue() - m_tvStartValue);
|
||||
}
|
||||
|
||||
double Timer::GetTimeNanoseconds() const
|
||||
{
|
||||
return ConvertValueToNanoseconds(GetCurrentValue() - m_tvStartValue);
|
||||
}
|
||||
|
||||
double Timer::GetTimeSecondsAndReset()
|
||||
{
|
||||
const Value value = GetCurrentValue();
|
||||
const double ret = ConvertValueToSeconds(value - m_tvStartValue);
|
||||
m_tvStartValue = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
double Timer::GetTimeMillisecondsAndReset()
|
||||
{
|
||||
const Value value = GetCurrentValue();
|
||||
const double ret = ConvertValueToMilliseconds(value - m_tvStartValue);
|
||||
m_tvStartValue = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
double Timer::GetTimeNanosecondsAndReset()
|
||||
{
|
||||
const Value value = GetCurrentValue();
|
||||
const double ret = ConvertValueToNanoseconds(value - m_tvStartValue);
|
||||
m_tvStartValue = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ThreadCPUTimer::ThreadCPUTimer() = default;
|
||||
|
||||
ThreadCPUTimer::ThreadCPUTimer(ThreadCPUTimer&& move)
|
||||
: m_thread_handle(move.m_thread_handle)
|
||||
{
|
||||
move.m_thread_handle = nullptr;
|
||||
}
|
||||
|
||||
ThreadCPUTimer::~ThreadCPUTimer()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle(reinterpret_cast<HANDLE>(m_thread_handle));
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadCPUTimer& ThreadCPUTimer::operator=(ThreadCPUTimer&& move)
|
||||
{
|
||||
std::swap(m_thread_handle, move.m_thread_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ThreadCPUTimer::Reset()
|
||||
{
|
||||
m_start_value = GetCurrentValue();
|
||||
}
|
||||
|
||||
ThreadCPUTimer::Value ThreadCPUTimer::GetCurrentValue() const
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
FILETIME create, exit, user, kernel;
|
||||
if (!m_thread_handle || !GetThreadTimes((HANDLE)m_thread_handle, &create, &exit, &user, &kernel))
|
||||
return 0;
|
||||
|
||||
Value value = (static_cast<Value>(user.dwHighDateTime) << 32) | (static_cast<Value>(user.dwLowDateTime));
|
||||
value += (static_cast<Value>(kernel.dwHighDateTime) << 32) | (static_cast<Value>(kernel.dwLowDateTime));
|
||||
return value;
|
||||
#elif defined(__APPLE__)
|
||||
thread_basic_info_data_t info;
|
||||
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
const kern_return_t kr = thread_info((mach_port_t) reinterpret_cast<uintptr_t>(m_thread_handle), THREAD_BASIC_INFO, (thread_info_t)&info, &count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
return 0;
|
||||
|
||||
Value value = (static_cast<Value>(info.user_time.seconds) * 1000000) + (static_cast<Value>(info.user_time.microseconds));
|
||||
value += (static_cast<Value>(info.system_time.seconds) * 1000000) + (static_cast<Value>(info.system_time.microseconds));
|
||||
return value;
|
||||
#else
|
||||
clockid_t cid;
|
||||
if (!m_thread_handle || pthread_getcpuclockid((pthread_t)m_thread_handle, &cid) != 0)
|
||||
return 0;
|
||||
|
||||
struct timespec ts;
|
||||
if (clock_gettime(cid, &ts) != 0)
|
||||
return 0;
|
||||
|
||||
return (static_cast<Value>(ts.tv_nsec) + static_cast<Value>(ts.tv_sec) * 1000000000LL);
|
||||
#endif
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::GetTimeSeconds() const
|
||||
{
|
||||
return ConvertValueToSeconds(GetCurrentValue() - m_start_value);
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::GetTimeMilliseconds() const
|
||||
{
|
||||
return ConvertValueToMilliseconds(GetCurrentValue() - m_start_value);
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::GetTimeNanoseconds() const
|
||||
{
|
||||
return ConvertValueToNanoseconds(GetCurrentValue() - m_start_value);
|
||||
}
|
||||
|
||||
void ThreadCPUTimer::GetUsageInSecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
|
||||
{
|
||||
const Value new_value = GetCurrentValue();
|
||||
const Value diff = new_value - m_start_value;
|
||||
m_start_value = new_value;
|
||||
|
||||
*usage_time = ConvertValueToSeconds(diff);
|
||||
*usage_percent = GetUtilizationPercentage(time_diff, diff);
|
||||
}
|
||||
|
||||
void ThreadCPUTimer::GetUsageInMillisecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
|
||||
{
|
||||
const Value new_value = GetCurrentValue();
|
||||
const Value diff = new_value - m_start_value;
|
||||
m_start_value = new_value;
|
||||
|
||||
*usage_time = ConvertValueToMilliseconds(diff);
|
||||
*usage_percent = GetUtilizationPercentage(time_diff, diff);
|
||||
}
|
||||
|
||||
void ThreadCPUTimer::GetUsageInNanosecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
|
||||
{
|
||||
const Value new_value = GetCurrentValue();
|
||||
const Value diff = new_value - m_start_value;
|
||||
m_start_value = new_value;
|
||||
|
||||
*usage_time = ConvertValueToNanoseconds(diff);
|
||||
*usage_percent = GetUtilizationPercentage(time_diff, diff);
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::GetUtilizationPercentage(Timer::Value time_diff, Value cpu_time_diff)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ((static_cast<double>(cpu_time_diff) * 10000.0) / (static_cast<double>(time_diff) / s_counter_frequency));
|
||||
#elif defined(__APPLE__)
|
||||
// microseconds, but time_tiff is in nanoseconds, so multiply by 1000 * 100
|
||||
return (static_cast<double>(cpu_time_diff) * 100000.0) / static_cast<double>(time_diff);
|
||||
#else
|
||||
// nanoseconds
|
||||
return (static_cast<double>(cpu_time_diff) * 100.0) / static_cast<double>(time_diff);
|
||||
#endif
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::ConvertValueToSeconds(Value value)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// 100ns units
|
||||
return (static_cast<double>(value) / 10000000.0);
|
||||
#elif defined(__APPLE__)
|
||||
// microseconds
|
||||
return (static_cast<double>(value) / 1000000.0);
|
||||
#else
|
||||
// nanoseconds
|
||||
return (static_cast<double>(value) / 1000000000.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::ConvertValueToMilliseconds(Value value)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (static_cast<double>(value) / 10000.0);
|
||||
#elif defined(__APPLE__)
|
||||
return (static_cast<double>(value) / 1000.0);
|
||||
#else
|
||||
return (static_cast<double>(value) / 1000000.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
double ThreadCPUTimer::ConvertValueToNanoseconds(Value value)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (static_cast<double>(value) * 100.0);
|
||||
#elif defined(__APPLE__)
|
||||
return (static_cast<double>(value) * 1000.0);
|
||||
#else
|
||||
return static_cast<double>(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadCPUTimer ThreadCPUTimer::GetForCallingThread()
|
||||
{
|
||||
ThreadCPUTimer ret;
|
||||
#if defined(_WIN32)
|
||||
ret.m_thread_handle = (void*)OpenThread(THREAD_QUERY_INFORMATION, FALSE, GetCurrentThreadId());
|
||||
#elif defined(__APPLE__)
|
||||
ret.m_thread_handle = reinterpret_cast<void*>((uintptr_t)mach_thread_self());
|
||||
#else
|
||||
ret.m_thread_handle = (void*)pthread_self();
|
||||
#endif
|
||||
ret.Reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,94 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
using Value = std::uint64_t;
|
||||
|
||||
Timer();
|
||||
|
||||
static Value GetCurrentValue();
|
||||
static double ConvertValueToSeconds(Value value);
|
||||
static double ConvertValueToMilliseconds(Value value);
|
||||
static double ConvertValueToNanoseconds(Value value);
|
||||
static Value ConvertSecondsToValue(double s);
|
||||
static Value ConvertMillisecondsToValue(double s);
|
||||
static Value ConvertNanosecondsToValue(double ns);
|
||||
|
||||
void Reset();
|
||||
void ResetTo(Value value) { m_tvStartValue = value; }
|
||||
|
||||
Value GetStartValue() const { return m_tvStartValue; }
|
||||
|
||||
double GetTimeSeconds() const;
|
||||
double GetTimeMilliseconds() const;
|
||||
double GetTimeNanoseconds() const;
|
||||
|
||||
double GetTimeSecondsAndReset();
|
||||
double GetTimeMillisecondsAndReset();
|
||||
double GetTimeNanosecondsAndReset();
|
||||
|
||||
private:
|
||||
Value m_tvStartValue;
|
||||
};
|
||||
|
||||
class ThreadCPUTimer
|
||||
{
|
||||
public:
|
||||
using Value = std::uint64_t;
|
||||
|
||||
ThreadCPUTimer();
|
||||
ThreadCPUTimer(ThreadCPUTimer&& move);
|
||||
ThreadCPUTimer(const ThreadCPUTimer&) = delete;
|
||||
~ThreadCPUTimer();
|
||||
|
||||
void Reset();
|
||||
void ResetTo(Value value) { m_start_value = value; }
|
||||
|
||||
Value GetStartValue() const { return m_start_value; }
|
||||
Value GetCurrentValue() const;
|
||||
|
||||
double GetTimeSeconds() const;
|
||||
double GetTimeMilliseconds() const;
|
||||
double GetTimeNanoseconds() const;
|
||||
|
||||
void GetUsageInSecondsAndReset(Value time_diff, double* usage_time, double* usage_percent);
|
||||
void GetUsageInMillisecondsAndReset(Value time_diff, double* usage_time, double* usage_percent);
|
||||
void GetUsageInNanosecondsAndReset(Value time_diff, double* usage_time, double* usage_percent);
|
||||
|
||||
static double GetUtilizationPercentage(Timer::Value time_diff, Value cpu_time_diff);
|
||||
|
||||
static double ConvertValueToSeconds(Value value);
|
||||
static double ConvertValueToMilliseconds(Value value);
|
||||
static double ConvertValueToNanoseconds(Value value);
|
||||
|
||||
static ThreadCPUTimer GetForCallingThread();
|
||||
|
||||
ThreadCPUTimer& operator=(const ThreadCPUTimer&) = delete;
|
||||
ThreadCPUTimer& operator=(ThreadCPUTimer&& move);
|
||||
|
||||
private:
|
||||
void* m_thread_handle = nullptr;
|
||||
Value m_start_value = 0;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -52,11 +52,14 @@
|
||||
<ClCompile Include="GL\Context.cpp" />
|
||||
<ClCompile Include="GL\ContextWGL.cpp" />
|
||||
<ClCompile Include="GL\StreamBuffer.cpp" />
|
||||
<ClCompile Include="FileSystem.cpp" />
|
||||
<ClCompile Include="IniInterface.cpp" />
|
||||
<ClCompile Include="ProgressCallback.cpp" />
|
||||
<ClCompile Include="pxStreams.cpp" />
|
||||
<ClCompile Include="pxTranslate.cpp" />
|
||||
<ClCompile Include="StringUtil.cpp" />
|
||||
<ClCompile Include="SettingsWrapper.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="VirtualMemory.cpp" />
|
||||
<ClCompile Include="WindowInfo.cpp" />
|
||||
<ClCompile Include="x86\MemcpyFast.cpp" />
|
||||
@@ -108,6 +111,8 @@
|
||||
<ClInclude Include="GL\Context.h" />
|
||||
<ClInclude Include="GL\ContextWGL.h" />
|
||||
<ClInclude Include="GL\StreamBuffer.h" />
|
||||
<ClInclude Include="FileSystem.h" />
|
||||
<ClInclude Include="ProgressCallback.h" />
|
||||
<ClInclude Include="ScopedGuard.h" />
|
||||
<ClInclude Include="StringUtil.h" />
|
||||
<ClInclude Include="SettingsInterface.h" />
|
||||
@@ -126,6 +131,7 @@
|
||||
<ClInclude Include="RedtapeWindows.h" />
|
||||
<ClInclude Include="SafeArray.h" />
|
||||
<ClInclude Include="StringHelpers.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="WindowInfo.h" />
|
||||
<ClInclude Include="wxBaseTools.h" />
|
||||
<ClInclude Include="Threading.h" />
|
||||
|
||||
@@ -130,6 +130,18 @@
|
||||
<ClCompile Include="GL\StreamBuffer.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileSystem.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringUtil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProgressCallback.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AlignedMalloc.h">
|
||||
@@ -282,9 +294,6 @@
|
||||
<ClInclude Include="SettingsWrapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScopedGuard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Align.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -303,6 +312,24 @@
|
||||
<ClInclude Include="GL\StreamBuffer.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileSystem.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringUtil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScopedGuard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProgressCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScopedGuard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -328,4 +355,4 @@
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -21,23 +21,21 @@
|
||||
// AND'ing this mask against an MXCSR prior to LDMXCSR.
|
||||
SSE_MXCSR MXCSR_Mask;
|
||||
|
||||
const wxChar* EnumToString(SSE_RoundMode sse)
|
||||
const char* EnumToString(SSE_RoundMode sse)
|
||||
{
|
||||
switch (sse)
|
||||
{
|
||||
case SSEround_Nearest:
|
||||
return L"Nearest";
|
||||
return "Nearest";
|
||||
case SSEround_NegInf:
|
||||
return L"NegativeInfinity";
|
||||
return "NegativeInfinity";
|
||||
case SSEround_PosInf:
|
||||
return L"PositiveInfinity";
|
||||
return "PositiveInfinity";
|
||||
case SSEround_Chop:
|
||||
return L"Chop";
|
||||
return "Chop";
|
||||
default:
|
||||
return L"Invalid";
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return L"Invalid";
|
||||
}
|
||||
|
||||
SSE_RoundMode SSE_MXCSR::GetRoundMode() const
|
||||
|
||||
+34
-33
@@ -24,21 +24,22 @@
|
||||
# include <aio.h>
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class AsyncFileReader
|
||||
{
|
||||
protected:
|
||||
AsyncFileReader() : m_dataoffset(0), m_blocksize(0) {}
|
||||
|
||||
wxString m_filename;
|
||||
std::string m_filename;
|
||||
|
||||
int m_dataoffset;
|
||||
uint m_blocksize;
|
||||
|
||||
public:
|
||||
virtual ~AsyncFileReader(void) {};
|
||||
virtual ~AsyncFileReader() {};
|
||||
|
||||
virtual bool Open(const wxString& fileName)=0;
|
||||
virtual bool Open(std::string fileName)=0;
|
||||
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count)=0;
|
||||
|
||||
@@ -55,7 +56,7 @@ public:
|
||||
|
||||
uint GetBlockSize() const { return m_blocksize; }
|
||||
|
||||
const wxString& GetFilename() const
|
||||
const std::string& GetFilename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
@@ -86,22 +87,22 @@ class FlatFileReader : public AsyncFileReader
|
||||
|
||||
public:
|
||||
FlatFileReader(bool shareWrite = false);
|
||||
virtual ~FlatFileReader(void);
|
||||
virtual ~FlatFileReader() override;
|
||||
|
||||
virtual bool Open(const wxString& fileName);
|
||||
virtual bool Open(std::string fileName) override;
|
||||
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count);
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count) override;
|
||||
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count);
|
||||
virtual int FinishRead(void);
|
||||
virtual void CancelRead(void);
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count) override;
|
||||
virtual int FinishRead(void) override;
|
||||
virtual void CancelRead(void) override;
|
||||
|
||||
virtual void Close(void);
|
||||
virtual void Close(void) override;
|
||||
|
||||
virtual uint GetBlockCount(void) const;
|
||||
virtual uint GetBlockCount(void) const override;
|
||||
|
||||
virtual void SetBlockSize(uint bytes) { m_blocksize = bytes; }
|
||||
virtual void SetDataOffset(int bytes) { m_dataoffset = bytes; }
|
||||
virtual void SetBlockSize(uint bytes) override { m_blocksize = bytes; }
|
||||
virtual void SetDataOffset(int bytes) override { m_dataoffset = bytes; }
|
||||
};
|
||||
|
||||
class MultipartFileReader : public AsyncFileReader
|
||||
@@ -123,21 +124,21 @@ class MultipartFileReader : public AsyncFileReader
|
||||
|
||||
public:
|
||||
MultipartFileReader(AsyncFileReader* firstPart);
|
||||
virtual ~MultipartFileReader(void);
|
||||
virtual ~MultipartFileReader() override;
|
||||
|
||||
virtual bool Open(const wxString& fileName);
|
||||
virtual bool Open(std::string fileName) override;
|
||||
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count);
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count) override;
|
||||
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count);
|
||||
virtual int FinishRead(void);
|
||||
virtual void CancelRead(void);
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count) override;
|
||||
virtual int FinishRead(void) override;
|
||||
virtual void CancelRead(void) override;
|
||||
|
||||
virtual void Close(void);
|
||||
virtual void Close(void) override;
|
||||
|
||||
virtual uint GetBlockCount(void) const;
|
||||
virtual uint GetBlockCount(void) const override;
|
||||
|
||||
virtual void SetBlockSize(uint bytes);
|
||||
virtual void SetBlockSize(uint bytes) override;
|
||||
|
||||
static AsyncFileReader* DetectMultipart(AsyncFileReader* reader);
|
||||
};
|
||||
@@ -146,7 +147,7 @@ class BlockdumpFileReader : public AsyncFileReader
|
||||
{
|
||||
DeclareNoncopyableObject( BlockdumpFileReader );
|
||||
|
||||
wxFileInputStream* m_file;
|
||||
std::FILE* m_file;
|
||||
|
||||
// total number of blocks in the ISO image (including all parts)
|
||||
u32 m_blocks;
|
||||
@@ -159,20 +160,20 @@ class BlockdumpFileReader : public AsyncFileReader
|
||||
int m_lresult;
|
||||
|
||||
public:
|
||||
BlockdumpFileReader(void);
|
||||
virtual ~BlockdumpFileReader(void);
|
||||
BlockdumpFileReader();
|
||||
virtual ~BlockdumpFileReader() override;
|
||||
|
||||
virtual bool Open(const wxString& fileName);
|
||||
virtual bool Open(std::string fileName) override;
|
||||
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count);
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count) override;
|
||||
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count);
|
||||
virtual int FinishRead(void);
|
||||
virtual void CancelRead(void);
|
||||
virtual void BeginRead(void* pBuffer, uint sector, uint count) override;
|
||||
virtual int FinishRead(void) override;
|
||||
virtual void CancelRead(void) override;
|
||||
|
||||
virtual void Close(void);
|
||||
virtual void Close(void) override;
|
||||
|
||||
virtual uint GetBlockCount(void) const;
|
||||
virtual uint GetBlockCount(void) const override;
|
||||
|
||||
static bool DetectBlockdump(AsyncFileReader* reader);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "AsyncFileReader.h"
|
||||
#include "IopCommon.h"
|
||||
#include "IsoFileFormats.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
@@ -59,48 +60,45 @@ BlockdumpFileReader::~BlockdumpFileReader(void)
|
||||
Close();
|
||||
}
|
||||
|
||||
bool BlockdumpFileReader::Open(const wxString& fileName)
|
||||
bool BlockdumpFileReader::Open(std::string fileName)
|
||||
{
|
||||
char buf[32];
|
||||
char signature[4];
|
||||
|
||||
m_filename = fileName;
|
||||
|
||||
m_file = new wxFileInputStream(m_filename);
|
||||
|
||||
m_file->SeekI(0);
|
||||
m_file->Read(buf, 4);
|
||||
|
||||
if (strncmp(buf, "BDV2", 4) != 0)
|
||||
m_filename = std::move(fileName);
|
||||
m_file = FileSystem::OpenCFile(m_filename.c_str(), "rb");
|
||||
if (!m_file || std::fread(signature, sizeof(signature), 1, m_file) != 1 || std::memcmp(signature, "BDV2", sizeof(signature)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//m_flags = ISOFLAGS_BLOCKDUMP_V2;
|
||||
m_file->Read(&m_blocksize, sizeof(m_blocksize));
|
||||
m_file->Read(&m_blocks, sizeof(m_blocks));
|
||||
m_file->Read(&m_blockofs, sizeof(m_blockofs));
|
||||
if (std::fread(&m_blocksize, sizeof(m_blocksize), 1, m_file) != 1 ||
|
||||
std::fread(&m_blocks, sizeof(m_blocks), 1, m_file) != 1 ||
|
||||
std::fread(&m_blockofs, sizeof(m_blockofs), 1, m_file) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wxFileOffset flen = m_file->GetLength();
|
||||
static const wxFileOffset datalen = flen - BlockDumpHeaderSize;
|
||||
const s64 flen = FileSystem::FSize64(m_file);
|
||||
const s64 datalen = flen - BlockDumpHeaderSize;
|
||||
|
||||
pxAssert((datalen % (m_blocksize + 4)) == 0);
|
||||
|
||||
m_dtablesize = datalen / (m_blocksize + 4);
|
||||
m_dtable = std::unique_ptr<u32[]>(new u32[m_dtablesize]);
|
||||
m_dtable = std::make_unique<u32[]>(m_dtablesize);
|
||||
|
||||
m_file->SeekI(BlockDumpHeaderSize);
|
||||
if (FileSystem::FSeek64(m_file, BlockDumpHeaderSize, SEEK_SET) != 0)
|
||||
return false;
|
||||
|
||||
u32 bs = 1024 * 1024;
|
||||
u32 off = 0;
|
||||
u32 has = 0;
|
||||
int i = 0;
|
||||
|
||||
std::unique_ptr<u8[]> buffer(new u8[bs]);
|
||||
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(bs);
|
||||
do
|
||||
{
|
||||
m_file->Read(buffer.get(), bs);
|
||||
has = m_file->LastRead();
|
||||
|
||||
has = static_cast<u32>(std::fread(buffer.get(), 1, bs, m_file));
|
||||
while (i < m_dtablesize && off < has)
|
||||
{
|
||||
m_dtable[i++] = *reinterpret_cast<u32*>(buffer.get() + off);
|
||||
@@ -132,15 +130,17 @@ int BlockdumpFileReader::ReadSync(void* pBuffer, uint lsn, uint count)
|
||||
// seek position ends up being based on (m_blocksize + 4) instead of just m_blocksize.
|
||||
|
||||
#ifdef PCSX2_DEBUG
|
||||
u32 check_lsn;
|
||||
m_file->SeekI(BlockDumpHeaderSize + (i * (m_blocksize + 4)));
|
||||
m_file->Read(&check_lsn, sizeof(check_lsn));
|
||||
u32 check_lsn = 0;
|
||||
FileSystem::FSeek64(m_file, BlockDumpHeaderSize + (i * (m_blocksize + 4)), SEEK_SET);
|
||||
std::fread(&check_lsn, sizeof(check_lsn), 1, m_file);
|
||||
pxAssert(check_lsn == lsn);
|
||||
#else
|
||||
m_file->SeekI(BlockDumpHeaderSize + (i * (m_blocksize + 4)) + 4);
|
||||
if (FileSystem::FSeek64(m_file, BlockDumpHeaderSize + (i * (m_blocksize + 4)) + 4, SEEK_SET) != 0)
|
||||
break;
|
||||
#endif
|
||||
|
||||
m_file->Read(dst, m_blocksize);
|
||||
if (std::fread(dst, m_blocksize, 1, m_file) != 1)
|
||||
break;
|
||||
|
||||
ok = true;
|
||||
break;
|
||||
@@ -178,7 +178,7 @@ void BlockdumpFileReader::Close(void)
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
delete m_file;
|
||||
std::fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+67
-106
@@ -20,6 +20,8 @@
|
||||
#include <ctype.h>
|
||||
#include <wx/datetime.h>
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#include "CdRom.h"
|
||||
#include "CDVD.h"
|
||||
#include "CDVD_internal.h"
|
||||
@@ -111,34 +113,28 @@ static int mg_BIToffset(u8* buffer)
|
||||
|
||||
static void cdvdGetMechaVer(u8* ver)
|
||||
{
|
||||
wxFileName mecfile(EmuConfig.FullpathToBios());
|
||||
mecfile.SetExt(L"mec");
|
||||
const wxString fname(mecfile.GetFullPath());
|
||||
|
||||
// Likely a bad idea to go further
|
||||
if (mecfile.IsDir())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
|
||||
|
||||
if (Path::GetFileSize(fname) < 4)
|
||||
std::string mecfile(FileSystem::ReplaceExtension(BiosPath, "mec"));
|
||||
auto fp = FileSystem::OpenManagedCFile(mecfile.c_str(), "rb");
|
||||
if (!fp || FileSystem::FSize64(fp.get()) < 4)
|
||||
{
|
||||
Console.Warning("MEC File Not Found, creating substitute...");
|
||||
|
||||
wxFFile fp(fname, L"wb");
|
||||
if (!fp.IsOpened())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
fp.reset();
|
||||
fp = FileSystem::OpenManagedCFile(mecfile.c_str(), "w+b");
|
||||
if (!fp)
|
||||
{
|
||||
Console.Error("Failed to read/write NVM/MEC file. Check your BIOS setup/permission settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
u8 version[4] = {0x3, 0x6, 0x2, 0x0};
|
||||
fp.Write(version, sizeof(version));
|
||||
std::fwrite(version, sizeof(version), 1, fp.get());
|
||||
FileSystem::FSeek64(fp.get(), 0, SEEK_SET);
|
||||
}
|
||||
|
||||
wxFFile fp(fname, L"rb");
|
||||
if (!fp.IsOpened())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
|
||||
size_t ret = fp.Read(ver, 4);
|
||||
auto ret = std::fread(ver, 1, 4, fp.get());
|
||||
if (ret != 4)
|
||||
Console.Error(L"Failed to read from %s. Did only %zu/4 bytes", WX_STR(fname), ret);
|
||||
Console.Error("Failed to read from %s. Did only %zu/4 bytes", mecfile.c_str(), ret);
|
||||
}
|
||||
|
||||
NVMLayout* getNvmLayout()
|
||||
@@ -153,14 +149,10 @@ NVMLayout* getNvmLayout()
|
||||
return nvmLayout;
|
||||
}
|
||||
|
||||
static void cdvdCreateNewNVM(const wxString& filename)
|
||||
static void cdvdCreateNewNVM(std::FILE* fp)
|
||||
{
|
||||
wxFFile fp(filename, L"wb");
|
||||
if (!fp.IsOpened())
|
||||
throw Exception::CannotCreateStream(filename);
|
||||
|
||||
u8 zero[1024] = {0};
|
||||
fp.Write(zero, sizeof(zero));
|
||||
u8 zero[1024] = {};
|
||||
std::fwrite(zero, sizeof(zero), 1, fp);
|
||||
|
||||
// Write NVM ILink area with dummy data (Age of Empires 2)
|
||||
// Also write language data defaulting to English (Guitar Hero 2)
|
||||
@@ -168,36 +160,34 @@ static void cdvdCreateNewNVM(const wxString& filename)
|
||||
NVMLayout* nvmLayout = getNvmLayout();
|
||||
u8 ILinkID_Data[8] = {0x00, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x86};
|
||||
|
||||
fp.Seek(*(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, ilinkId)));
|
||||
fp.Write(ILinkID_Data, sizeof(ILinkID_Data));
|
||||
std::fseek(fp, *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, ilinkId)), SEEK_SET);
|
||||
std::fwrite(ILinkID_Data, sizeof(ILinkID_Data), 1, fp);
|
||||
|
||||
u8 biosLanguage[16];
|
||||
memcpy(biosLanguage, &biosLangDefaults[BiosRegion][0], 16);
|
||||
// Config sections first 16 bytes are generally blank expect the last byte which is PS1 mode stuff
|
||||
// So let's ignore that and just write the PS2 mode stuff
|
||||
fp.Seek(*(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, config1)) + 0x10);
|
||||
fp.Write(biosLanguage, sizeof(biosLanguage));
|
||||
|
||||
fp.Close();
|
||||
std::fseek(fp, *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, config1)) + 0x10, SEEK_SET);
|
||||
std::fwrite(biosLanguage, sizeof(biosLanguage), 1, fp);
|
||||
}
|
||||
|
||||
// Throws Exception::CannotCreateStream if the file cannot be opened for reading, or cannot
|
||||
// be created for some reason.
|
||||
static void cdvdNVM(u8* buffer, int offset, size_t bytes, bool read)
|
||||
{
|
||||
wxFileName nvmfile(EmuConfig.FullpathToBios());
|
||||
nvmfile.SetExt(L"nvm");
|
||||
const wxString fname(nvmfile.GetFullPath());
|
||||
|
||||
// Likely a bad idea to go further
|
||||
if (nvmfile.IsDir())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
|
||||
if (Path::GetFileSize(fname) < 1024)
|
||||
std::string nvmfile(FileSystem::ReplaceExtension(BiosPath, "nvm"));
|
||||
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "r+b");
|
||||
if (!fp || FileSystem::FSize64(fp.get()) < 1024)
|
||||
{
|
||||
Console.Warning("NVM File Not Found, creating substitute...");
|
||||
fp.reset();
|
||||
fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "w+b");
|
||||
if (!fp)
|
||||
{
|
||||
Console.Error("Failed to open NVM file '%s' for writing", nvmfile.c_str());
|
||||
if (read)
|
||||
std::memset(buffer, 0, bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
cdvdCreateNewNVM(fname);
|
||||
cdvdCreateNewNVM(fp.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -205,38 +195,28 @@ static void cdvdNVM(u8* buffer, int offset, size_t bytes, bool read)
|
||||
u8 zero[16] = {0};
|
||||
NVMLayout* nvmLayout = getNvmLayout();
|
||||
|
||||
wxFFile fp(fname, L"r+b");
|
||||
if (!fp.IsOpened())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
|
||||
fp.Seek(*(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, config1)) + 0x10);
|
||||
fp.Read(LanguageParams, 16);
|
||||
|
||||
fp.Close();
|
||||
|
||||
if (memcmp(LanguageParams, zero, sizeof(LanguageParams)) == 0)
|
||||
if (std::fseek(fp.get(), *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, config1)) + 0x10, SEEK_SET) != 0 ||
|
||||
std::fread(LanguageParams, 16, 1, fp.get()) != 1 ||
|
||||
std::memcmp(LanguageParams, zero, sizeof(LanguageParams)) == 0)
|
||||
{
|
||||
Console.Warning("Language Parameters missing, filling in defaults");
|
||||
|
||||
cdvdCreateNewNVM(fname);
|
||||
FileSystem::FSeek64(fp.get(), 0, SEEK_SET);
|
||||
cdvdCreateNewNVM(fp.get());
|
||||
}
|
||||
}
|
||||
|
||||
wxFFile fp(fname, L"r+b");
|
||||
if (!fp.IsOpened())
|
||||
throw Exception::CannotCreateStream(fname);
|
||||
|
||||
fp.Seek(offset);
|
||||
std::fseek(fp.get(), offset, SEEK_SET);
|
||||
|
||||
size_t ret;
|
||||
if (read)
|
||||
ret = fp.Read(buffer, bytes);
|
||||
ret = std::fread(buffer, 1, bytes, fp.get());
|
||||
else
|
||||
ret = fp.Write(buffer, bytes);
|
||||
ret = std::fwrite(buffer, 1, bytes, fp.get());
|
||||
|
||||
if (ret != bytes)
|
||||
Console.Error(L"Failed to %s %s. Did only %zu/%zu bytes",
|
||||
read ? L"read from" : L"write to", WX_STR(fname), ret, bytes);
|
||||
read ? L"read from" : L"write to", nvmfile.c_str(), ret, bytes);
|
||||
}
|
||||
|
||||
static void cdvdReadNVM(u8* dst, int offset, int bytes)
|
||||
@@ -384,7 +364,7 @@ static MutexRecursive Mutex_NewDiskCB;
|
||||
static __fi ElfObject* loadElf(const wxString filename, bool isPSXElf)
|
||||
{
|
||||
if (filename.StartsWith(L"host"))
|
||||
return new ElfObject(filename.After(':'), Path::GetFileSize(filename.After(':')), isPSXElf);
|
||||
return new ElfObject(filename.After(':'), FileSystem::GetPathFileSize(filename.After(':').ToUTF8()), isPSXElf);
|
||||
|
||||
// Mimic PS2 behavior!
|
||||
// Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that
|
||||
@@ -1058,7 +1038,7 @@ __fi void cdvdActionInterrupt()
|
||||
cdvd.Spinning = true;
|
||||
cdvd.Ready |= CDVD_DRIVE_READY; //check (rama)
|
||||
cdvd.Sector = cdvd.SeekToSector;
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.nextSectorsBuffered = 0;
|
||||
cdvd.triggerDataReady = true;
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
@@ -1069,7 +1049,7 @@ __fi void cdvdActionInterrupt()
|
||||
cdvd.Spinning = true; //check (rama)
|
||||
cdvd.Ready |= CDVD_DRIVE_READY; //check (rama)
|
||||
cdvd.Sector = cdvd.SeekToSector;
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.nextSectorsBuffered = 0;
|
||||
cdvd.triggerDataReady = true;
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
@@ -1123,18 +1103,8 @@ __fi void cdvdSectorReady()
|
||||
}
|
||||
}
|
||||
|
||||
if (cdvd.nextSectorsBuffered == 16 && (cdvd.Ready & CDVD_DRIVE_READY))
|
||||
{
|
||||
cdvd.Status = CDVD_STATUS_PAUSE; // Needed here but could be smth else than Pause (rama)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cdvd.nextSectorsBuffered < 16)
|
||||
{
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
}
|
||||
}
|
||||
if (cdvd.nextSectorsBuffered < 16)
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
}
|
||||
|
||||
// inlined due to being referenced in only one place.
|
||||
@@ -1159,14 +1129,10 @@ __fi void cdvdReadInterrupt()
|
||||
cdvd.Reading = 1;
|
||||
cdvd.Readed = 1;
|
||||
cdvd.Sector = cdvd.SeekToSector;
|
||||
CDVD_LOG("Cdvd Seek Complete > Scheduling block read interrupt at iopcycle=%8.8x.",
|
||||
psxRegs.cycle + cdvd.ReadTime);
|
||||
|
||||
CDVDREAD_INT(cdvd.ReadTime);
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
return;
|
||||
CDVD_LOG("Cdvd Seek Complete at iopcycle=%8.8x.", psxRegs.cycle);
|
||||
}
|
||||
else if (cdvd.Reading)
|
||||
|
||||
if (cdvd.Reading)
|
||||
{
|
||||
if (cdvd.RErr == 0)
|
||||
{
|
||||
@@ -1237,10 +1203,7 @@ __fi void cdvdReadInterrupt()
|
||||
iopIntcIrq(2);
|
||||
cdvd.Ready |= CDVD_DRIVE_READY;
|
||||
|
||||
if (cdvd.nextSectorsBuffered < 16)
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
else
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
|
||||
cdvd.nCommand = 0;
|
||||
//DevCon.Warning("Scheduling interrupt in %d cycles", cdvd.ReadTime - ((cdvd.BlockSize / 4) * 12));
|
||||
@@ -1264,7 +1227,7 @@ __fi void cdvdReadInterrupt()
|
||||
if (cdvd.nextSectorsBuffered)
|
||||
CDVDREAD_INT((cdvd.BlockSize / 4) * 12);
|
||||
else
|
||||
CDVDREAD_INT(cdvd.ReadTime + ((cdvd.BlockSize / 4) * 12));
|
||||
CDVDREAD_INT((psxRegs.cycle - psxRegs.sCycle[IopEvt_CdvdSectorReady]) + ((cdvd.BlockSize / 4) * 12));
|
||||
}
|
||||
|
||||
// Returns the number of IOP cycles until the event completes.
|
||||
@@ -1274,6 +1237,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
|
||||
uint delta = abs((s32)(cdvd.SeekToSector - cdvd.Sector));
|
||||
uint seektime;
|
||||
bool isSeeking = cdvd.nCommand == N_CD_SEEK;
|
||||
|
||||
cdvd.Ready &= ~(CDVD_DRIVE_READY | CDVD_DRIVE_DATARDY);
|
||||
cdvd.Reading = 1;
|
||||
@@ -1311,6 +1275,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
CDVD_LOG("CdSeek Begin > to sector %d, from %d - delta=%d [FAST]", cdvd.SeekToSector, cdvd.Sector, delta);
|
||||
seektime = Cdvd_FastSeek_Cycles;
|
||||
}
|
||||
isSeeking = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1318,6 +1283,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
|
||||
// if delta > 0 it will read a new sector so the readInterrupt will account for this.
|
||||
seektime = 0;
|
||||
isSeeking = false;
|
||||
|
||||
if (delta == 0)
|
||||
{
|
||||
@@ -1342,12 +1308,11 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
}
|
||||
else
|
||||
{
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
seektime = cdvd.ReadTime + ((cdvd.BlockSize / 4) * 12);
|
||||
delta = 1; // Forces it to use the rotational delay since we have no sectors buffered and it isn't buffering any.
|
||||
}
|
||||
}
|
||||
else
|
||||
seektime = (cdvd.BlockSize / 4) * 12;
|
||||
return (cdvd.BlockSize / 4) * 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1357,7 +1322,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
}
|
||||
|
||||
// Only do this on reads, the seek kind of accounts for this and then it reads the sectors after
|
||||
if (delta && cdvd.nCommand != N_CD_SEEK)
|
||||
if (delta && !isSeeking)
|
||||
{
|
||||
int rotationalLatency = cdvdRotationalLatency((CDVD_MODE_TYPE)cdvdIsDVD());
|
||||
//DevCon.Warning("%s rotational latency at sector %d is %d cycles", (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? "CAV" : "CLV", cdvd.SeekToSector, rotationalLatency);
|
||||
@@ -1365,6 +1330,9 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
|
||||
CDVDSECTORREADY_INT(seektime);
|
||||
seektime += (cdvd.BlockSize / 4) * 12;
|
||||
}
|
||||
else
|
||||
CDVDSECTORREADY_INT(seektime);
|
||||
|
||||
return seektime;
|
||||
}
|
||||
|
||||
@@ -1642,7 +1610,7 @@ static void cdvdWrite04(u8 rt)
|
||||
cdvdSetIrq();
|
||||
cdvd.nCommand = 0;
|
||||
//After Pausing needs to buffer the next sector
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.nextSectorsBuffered = 0;
|
||||
cdvd.triggerDataReady = true;
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
@@ -1874,7 +1842,7 @@ static void cdvdWrite04(u8 rt)
|
||||
HW_DMA3_CHCR &= ~0x01000000;
|
||||
psxDmaInterrupt(3);
|
||||
//After reading the TOC it needs to go back to buffer the next sector
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.nextSectorsBuffered = 0;
|
||||
cdvd.triggerDataReady = true;
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
@@ -1891,7 +1859,7 @@ static void cdvdWrite04(u8 rt)
|
||||
cdvdSetIrq();
|
||||
cdvd.nCommand = 0;
|
||||
//After reading the key it needs to go back to buffer the next sector
|
||||
cdvd.Status = CDVD_STATUS_READ;
|
||||
cdvd.Status = CDVD_STATUS_PAUSE;
|
||||
cdvd.nextSectorsBuffered = 0;
|
||||
cdvd.triggerDataReady = true;
|
||||
CDVDSECTORREADY_INT(cdvd.ReadTime);
|
||||
@@ -1996,7 +1964,6 @@ static __fi void fail_pol_cal()
|
||||
|
||||
static void cdvdWrite16(u8 rt) // SCOMMAND
|
||||
{
|
||||
try
|
||||
{
|
||||
// cdvdTN diskInfo;
|
||||
// cdvdTD trackInfo;
|
||||
@@ -2655,12 +2622,6 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
|
||||
cdvd.ParamP = 0;
|
||||
cdvd.ParamC = 0;
|
||||
}
|
||||
catch (Exception::CannotCreateStream&)
|
||||
{
|
||||
Cpu->ThrowException(Exception::RuntimeError()
|
||||
.SetDiagMsg(L"Failed to read/write NVM/MEC file.")
|
||||
.SetUserMsg(pxE(L"Failed to read/write NVM/MEC file. Check your BIOS setup/permission settings.")));
|
||||
}
|
||||
}
|
||||
|
||||
static __fi void cdvdWrite17(u8 rt)
|
||||
|
||||
+19
-13
@@ -33,6 +33,7 @@
|
||||
#include "IsoFS/IsoFSCDVD.h"
|
||||
#include "CDVDisoReader.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "Config.h"
|
||||
@@ -376,33 +377,33 @@ bool DoCDVDopen()
|
||||
return true;
|
||||
}
|
||||
|
||||
wxString somepick(Path::GetFilenameWithoutExt(fromUTF8(m_SourceFilename[CurrentSourceType])));
|
||||
std::string somepick(FileSystem::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType])));
|
||||
//FWIW Disc serial availability doesn't seem reliable enough, sometimes it's there and sometime it's just null
|
||||
//Shouldn't the serial be available all time? Potentially need to look into Elfreloadinfo() reliability
|
||||
//TODO: Add extra fallback case for CRC.
|
||||
if (somepick.IsEmpty() && !DiscSerial.IsEmpty())
|
||||
somepick = L"Untitled-" + DiscSerial;
|
||||
else if (somepick.IsEmpty())
|
||||
somepick = L"Untitled";
|
||||
if (somepick.empty() && !DiscSerial.IsEmpty())
|
||||
somepick = StringUtil::StdStringFromFormat("Untitled-%s", DiscSerial.ToUTF8().data());
|
||||
else if (somepick.empty())
|
||||
somepick = "Untitled";
|
||||
|
||||
if (EmuConfig.CurrentBlockdump.empty())
|
||||
EmuConfig.CurrentBlockdump = StringUtil::wxStringToUTF8String(wxGetCwd());
|
||||
EmuConfig.CurrentBlockdump = FileSystem::GetWorkingDirectory();
|
||||
|
||||
wxString temp(Path::Combine(StringUtil::UTF8StringToWxString(EmuConfig.CurrentBlockdump), somepick));
|
||||
std::string temp(Path::CombineStdString(EmuConfig.CurrentBlockdump, somepick));
|
||||
|
||||
#ifdef ENABLE_TIMESTAMPS
|
||||
wxDateTime curtime(wxDateTime::GetTimeNow());
|
||||
|
||||
temp += pxsFmt(L" (%04d-%02d-%02d %02d-%02d-%02d)",
|
||||
curtime.GetYear(), curtime.GetMonth(), curtime.GetDay(),
|
||||
curtime.GetHour(), curtime.GetMinute(), curtime.GetSecond());
|
||||
temp += StringUtil::StdStringFromFormat(" (%04d-%02d-%02d %02d-%02d-%02d)",
|
||||
curtime.GetYear(), curtime.GetMonth(), curtime.GetDay(),
|
||||
curtime.GetHour(), curtime.GetMinute(), curtime.GetSecond());
|
||||
#endif
|
||||
temp += L".dump";
|
||||
temp += ".dump";
|
||||
|
||||
cdvdTD td;
|
||||
CDVD->getTD(0, &td);
|
||||
|
||||
blockDumpFile.Create(temp, 2);
|
||||
blockDumpFile.Create(std::move(temp), 2);
|
||||
|
||||
if (blockDumpFile.IsOpened())
|
||||
{
|
||||
@@ -432,7 +433,12 @@ bool DoCDVDopen()
|
||||
void DoCDVDclose()
|
||||
{
|
||||
CheckNullCDVD();
|
||||
//blockDumpFile.Close();
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
// This was commented out, presumably because pausing/resuming in wx reopens CDVD.
|
||||
// This is a non-issue in Qt, so we'll leave it behind the ifdef.
|
||||
blockDumpFile.Close();
|
||||
#endif
|
||||
|
||||
if (CDVD->close != NULL)
|
||||
CDVD->close();
|
||||
|
||||
@@ -52,7 +52,7 @@ s32 CALLBACK ISOopen(const char* pTitle)
|
||||
|
||||
try
|
||||
{
|
||||
iso.Open(fromUTF8(pTitle));
|
||||
iso.Open(pTitle);
|
||||
}
|
||||
catch (BaseException& ex)
|
||||
{
|
||||
|
||||
@@ -16,71 +16,98 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "ChdFileReader.h"
|
||||
|
||||
#include "CDVD/CompressedFileReaderUtils.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <wx/dir.h>
|
||||
|
||||
bool ChdFileReader::CanHandle(const wxString& fileName)
|
||||
ChdFileReader::~ChdFileReader()
|
||||
{
|
||||
if (!wxFileName::FileExists(fileName) || !fileName.Lower().EndsWith(L".chd"))
|
||||
{
|
||||
Close();
|
||||
|
||||
for (std::FILE* fp : m_files)
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
bool ChdFileReader::CanHandle(const std::string& fileName, const std::string& displayName)
|
||||
{
|
||||
if (!StringUtil::EndsWith(displayName, ".chd"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChdFileReader::Open2(const wxString& fileName)
|
||||
static chd_error chd_open_wrapper(const char* filename, std::FILE** fp, int mode, chd_file* parent, chd_file** chd)
|
||||
{
|
||||
*fp = FileSystem::OpenCFile(filename, "rb");
|
||||
if (!*fp)
|
||||
return CHDERR_FILE_NOT_FOUND;
|
||||
|
||||
const chd_error err = chd_open_file(*fp, mode, parent, chd);
|
||||
if (err == CHDERR_NONE)
|
||||
return err;
|
||||
|
||||
std::fclose(*fp);
|
||||
*fp = nullptr;
|
||||
return err;
|
||||
}
|
||||
|
||||
bool ChdFileReader::Open2(std::string fileName)
|
||||
{
|
||||
Close2();
|
||||
|
||||
m_filename = fileName;
|
||||
m_filename = std::move(fileName);
|
||||
|
||||
chd_file* child = NULL;
|
||||
chd_file* parent = NULL;
|
||||
chd_file* child = nullptr;
|
||||
chd_file* parent = nullptr;
|
||||
std::FILE* fp = nullptr;
|
||||
chd_header header;
|
||||
chd_header parent_header;
|
||||
|
||||
wxString chds[8];
|
||||
chds[0] = fileName;
|
||||
std::string chds[8];
|
||||
chds[0] = m_filename;
|
||||
int chd_depth = 0;
|
||||
chd_error error;
|
||||
|
||||
// TODO: Unicode correctness on Windows
|
||||
while (CHDERR_REQUIRES_PARENT == (error = chd_open(chds[chd_depth].c_str(), CHD_OPEN_READ, NULL, &child)))
|
||||
std::string dirname;
|
||||
FileSystem::FindResultsArray results;
|
||||
|
||||
while (CHDERR_REQUIRES_PARENT == (error = chd_open_wrapper(chds[chd_depth].c_str(), &fp, CHD_OPEN_READ, NULL, &child)))
|
||||
{
|
||||
if (chd_depth >= static_cast<int>(std::size(chds) - 1))
|
||||
{
|
||||
Console.Error(L"CDVD: chd_open hit recursion limit searching for parents");
|
||||
Console.Error("CDVD: chd_open hit recursion limit searching for parents");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This is still broken on Windows. Needs to be fixed in libchdr.
|
||||
if (chd_read_header(chds[chd_depth].c_str(), &header) != CHDERR_NONE)
|
||||
{
|
||||
Console.Error(L"CDVD: chd_open chd_read_header error: %s: %s", chd_error_string(error), WX_STR(chds[chd_depth]));
|
||||
Console.Error("CDVD: chd_open chd_read_header error: %s: %s", chd_error_string(error), chds[chd_depth].c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_parent = false;
|
||||
wxFileName wxfilename(chds[chd_depth]);
|
||||
wxString dir_path = wxfilename.GetPath();
|
||||
wxDir dir(dir_path);
|
||||
if (dir.IsOpened())
|
||||
dirname = FileSystem::GetPathDirectory(chds[chd_depth]);
|
||||
if (FileSystem::FindFiles(dirname.c_str(), "*.*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES, &results))
|
||||
{
|
||||
wxString parent_fileName;
|
||||
bool cont = dir.GetFirst(&parent_fileName, wxString("*.") + wxfilename.GetExt(), wxDIR_FILES | wxDIR_HIDDEN);
|
||||
for (; cont; cont = dir.GetNext(&parent_fileName))
|
||||
for (const FILESYSTEM_FIND_DATA& fd : results)
|
||||
{
|
||||
parent_fileName = wxFileName(dir_path, parent_fileName).GetFullPath();
|
||||
if (chd_read_header(parent_fileName.c_str(), &parent_header) == CHDERR_NONE &&
|
||||
const std::string_view extension(FileSystem::GetExtension(fd.FileName));
|
||||
if (extension.empty() || StringUtil::Strncasecmp(extension.data(), "chd", 3) != 0)
|
||||
continue;
|
||||
|
||||
if (chd_read_header(fd.FileName.c_str(), &parent_header) == CHDERR_NONE &&
|
||||
memcmp(parent_header.sha1, header.parentsha1, sizeof(parent_header.sha1)) == 0)
|
||||
{
|
||||
found_parent = true;
|
||||
chds[++chd_depth] = wxString(parent_fileName);
|
||||
chds[++chd_depth] = std::move(fd.FileName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_parent)
|
||||
{
|
||||
Console.Error(L"CDVD: chd_open no parent for: %s", WX_STR(chds[chd_depth]));
|
||||
Console.Error("CDVD: chd_open no parent for: %s", chds[chd_depth].c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -91,11 +118,17 @@ bool ChdFileReader::Open2(const wxString& fileName)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child)
|
||||
{
|
||||
pxAssert(fp != nullptr);
|
||||
m_files.push_back(fp);
|
||||
}
|
||||
|
||||
for (int d = chd_depth - 1; d >= 0; d--)
|
||||
{
|
||||
parent = child;
|
||||
child = NULL;
|
||||
error = chd_open(chds[d].c_str(), CHD_OPEN_READ, parent, &child);
|
||||
error = chd_open_wrapper(chds[d].c_str(), &fp, CHD_OPEN_READ, parent, &child);
|
||||
if (error != CHDERR_NONE)
|
||||
{
|
||||
Console.Error(L"CDVD: chd_open return error: %s", chd_error_string(error));
|
||||
@@ -103,19 +136,17 @@ bool ChdFileReader::Open2(const wxString& fileName)
|
||||
chd_close(parent);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_files.push_back(fp);
|
||||
}
|
||||
ChdFile = child;
|
||||
if (chd_read_header(chds[0].c_str(), &header) != CHDERR_NONE)
|
||||
{
|
||||
Console.Error(L"CDVD: chd_open chd_read_header error: %s: %s", chd_error_string(error), WX_STR(chds[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
file_size = static_cast<u64>(header.unitbytes) * header.unitcount;
|
||||
hunk_size = header.hunkbytes;
|
||||
const chd_header* chd_header = chd_get_header(ChdFile);
|
||||
file_size = static_cast<u64>(chd_header->unitbytes) * chd_header->unitcount;
|
||||
hunk_size = chd_header->hunkbytes;
|
||||
// CHD likes to use full 2448 byte blocks, but keeps the +24 offset of source ISOs
|
||||
// The rest of PCSX2 likes to use 2448 byte buffers, which can't fit that so trim blocks instead
|
||||
m_internalBlockSize = header.unitbytes;
|
||||
m_internalBlockSize = chd_header->unitbytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -136,7 +167,7 @@ ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset)
|
||||
return chunk;
|
||||
}
|
||||
|
||||
int ChdFileReader::ReadChunk(void *dst, s64 chunkID)
|
||||
int ChdFileReader::ReadChunk(void* dst, s64 chunkID)
|
||||
{
|
||||
if (chunkID < 0)
|
||||
return -1;
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
#pragma once
|
||||
#include "ThreadedFileReader.h"
|
||||
#include "libchdr/chd.h"
|
||||
#include <vector>
|
||||
|
||||
class ChdFileReader : public ThreadedFileReader
|
||||
{
|
||||
DeclareNoncopyableObject(ChdFileReader);
|
||||
|
||||
public:
|
||||
virtual ~ChdFileReader(void) { Close(); };
|
||||
virtual ~ChdFileReader() override;;
|
||||
|
||||
static bool CanHandle(const wxString& fileName);
|
||||
bool Open2(const wxString& fileName) override;
|
||||
static bool CanHandle(const std::string& fileName, const std::string& displayName);
|
||||
bool Open2(std::string fileName) override;
|
||||
|
||||
Chunk ChunkForOffset(u64 offset) override;
|
||||
int ReadChunk(void *dst, s64 blockID) override;
|
||||
@@ -38,4 +39,5 @@ private:
|
||||
chd_file* ChdFile;
|
||||
u64 file_size;
|
||||
u32 hunk_size;
|
||||
std::vector<std::FILE*> m_files;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
void ChunksCache::SetLimit(uint megabytes)
|
||||
{
|
||||
m_limit = (PX_off_t)megabytes * 1024 * 1024;
|
||||
m_limit = (s64)megabytes * 1024 * 1024;
|
||||
MatchLimit();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ void ChunksCache::MatchLimit(bool removeAll)
|
||||
}
|
||||
}
|
||||
|
||||
void ChunksCache::Take(void* pMallocedSrc, PX_off_t offset, int length, int coverage)
|
||||
void ChunksCache::Take(void* pMallocedSrc, s64 offset, int length, int coverage)
|
||||
{
|
||||
m_entries.push_front(new CacheEntry(pMallocedSrc, offset, length, coverage));
|
||||
m_size += length;
|
||||
@@ -42,7 +42,7 @@ void ChunksCache::Take(void* pMallocedSrc, PX_off_t offset, int length, int cove
|
||||
}
|
||||
|
||||
// By design, succeed only if the entire request is in a single cached chunk
|
||||
int ChunksCache::Read(void* pDest, PX_off_t offset, int length)
|
||||
int ChunksCache::Read(void* pDest, s64 offset, int length)
|
||||
{
|
||||
for (auto it = m_entries.begin(); it != m_entries.end(); it++)
|
||||
{
|
||||
|
||||
@@ -30,11 +30,11 @@ public:
|
||||
void SetLimit(uint megabytes);
|
||||
void Clear() { MatchLimit(true); };
|
||||
|
||||
void Take(void* pMallocedSrc, PX_off_t offset, int length, int coverage);
|
||||
int Read(void* pDest, PX_off_t offset, int length);
|
||||
void Take(void* pMallocedSrc, s64 offset, int length, int coverage);
|
||||
int Read(void* pDest, s64 offset, int length);
|
||||
|
||||
static int CopyAvailable(void* pSrc, PX_off_t srcOffset, int srcSize,
|
||||
void* pDst, PX_off_t dstOffset, int maxCopySize)
|
||||
static int CopyAvailable(void* pSrc, s64 srcOffset, int srcSize,
|
||||
void* pDst, s64 dstOffset, int maxCopySize)
|
||||
{
|
||||
int available = CLAMP(maxCopySize, 0, (int)(srcOffset + srcSize - dstOffset));
|
||||
memcpy(pDst, (char*)pSrc + (dstOffset - srcOffset), available);
|
||||
@@ -45,7 +45,7 @@ private:
|
||||
class CacheEntry
|
||||
{
|
||||
public:
|
||||
CacheEntry(void* pMallocedSrc, PX_off_t offset, int length, int coverage)
|
||||
CacheEntry(void* pMallocedSrc, s64 offset, int length, int coverage)
|
||||
: data(pMallocedSrc)
|
||||
, offset(offset)
|
||||
, coverage(coverage)
|
||||
@@ -58,15 +58,15 @@ private:
|
||||
};
|
||||
|
||||
void* data;
|
||||
PX_off_t offset;
|
||||
s64 offset;
|
||||
int coverage;
|
||||
int size;
|
||||
};
|
||||
|
||||
std::list<CacheEntry*> m_entries;
|
||||
void MatchLimit(bool removeAll = false);
|
||||
PX_off_t m_size;
|
||||
PX_off_t m_limit;
|
||||
s64 m_size;
|
||||
s64 m_limit;
|
||||
};
|
||||
|
||||
#undef CLAMP
|
||||
|
||||
@@ -19,19 +19,27 @@
|
||||
#include "ChdFileReader.h"
|
||||
#include "CsoFileReader.h"
|
||||
#include "GzippedFileReader.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include <cctype>
|
||||
|
||||
// CompressedFileReader factory.
|
||||
AsyncFileReader* CompressedFileReader::GetNewReader(const wxString& fileName)
|
||||
AsyncFileReader* CompressedFileReader::GetNewReader(const std::string& fileName)
|
||||
{
|
||||
if (ChdFileReader::CanHandle(fileName))
|
||||
if (!FileSystem::FileExists(fileName.c_str()))
|
||||
return nullptr;
|
||||
|
||||
std::string displayName(FileSystem::GetDisplayNameFromPath(fileName));
|
||||
std::transform(displayName.begin(), displayName.end(), displayName.begin(), tolower);
|
||||
|
||||
if (ChdFileReader::CanHandle(fileName, displayName))
|
||||
{
|
||||
return new ChdFileReader();
|
||||
}
|
||||
if (GzippedFileReader::CanHandle(fileName))
|
||||
if (GzippedFileReader::CanHandle(fileName, displayName))
|
||||
{
|
||||
return new GzippedFileReader();
|
||||
}
|
||||
if (CsoFileReader::CanHandle(fileName))
|
||||
if (CsoFileReader::CanHandle(fileName, displayName))
|
||||
{
|
||||
return new CsoFileReader();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
// Factory - creates an AsyncFileReader derived instance which can read a compressed file
|
||||
class CompressedFileReader
|
||||
@@ -23,7 +24,7 @@ public:
|
||||
// If no matching handler is found, NULL is returned.
|
||||
// The returned instance still needs ->Open(filename) before usage.
|
||||
// Open(filename) may still fail.
|
||||
static AsyncFileReader* GetNewReader(const wxString& fileName);
|
||||
static AsyncFileReader* GetNewReader(const std::string& fileName);
|
||||
|
||||
private:
|
||||
virtual ~CompressedFileReader() = 0;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2014 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
||||
/////////// Some complementary utilities for zlib_indexed.c //////////
|
||||
|
||||
// This is ugly, but it's hard to find something which will work/compile for both
|
||||
// windows and *nix and work with non-english file names.
|
||||
// Maybe some day we'll convert all file related ops to wxWidgets, which means also the
|
||||
// instances at zlib_indexed.h (which use plain stdio FILE*)
|
||||
#ifdef _WIN32
|
||||
#define PX_wfilename(name_wxstr) (name_wxstr.wc_str())
|
||||
#define PX_fopen_rb(name_wxstr) (_wfopen(PX_wfilename(name_wxstr), L"rb"))
|
||||
#else
|
||||
#define PX_wfilename(name_wxstr) (name_wxstr.mbc_str())
|
||||
#define PX_fopen_rb(name_wxstr) (fopen(PX_wfilename(name_wxstr), "rb"))
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PX_fseeko _fseeki64
|
||||
#define PX_ftello _ftelli64
|
||||
#define PX_off_t s64 /* __int64 */
|
||||
#else
|
||||
#define PX_fseeko fseeko
|
||||
#define PX_ftello ftello
|
||||
#define PX_off_t off_t
|
||||
#endif
|
||||
|
||||
/////////// End of complementary utilities for zlib_indexed.c //////////
|
||||
@@ -15,9 +15,10 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "AsyncFileReader.h"
|
||||
#include "CompressedFileReaderUtils.h"
|
||||
#include "CsoFileReader.h"
|
||||
#include "common/Pcsx2Types.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#ifdef __POSIX__
|
||||
#include <zlib.h>
|
||||
#else
|
||||
@@ -39,12 +40,12 @@ struct CsoHeader
|
||||
|
||||
static const u32 CSO_READ_BUFFER_SIZE = 256 * 1024;
|
||||
|
||||
bool CsoFileReader::CanHandle(const wxString& fileName)
|
||||
bool CsoFileReader::CanHandle(const std::string& fileName, const std::string& displayName)
|
||||
{
|
||||
bool supported = false;
|
||||
if (wxFileName::FileExists(fileName) && fileName.Lower().EndsWith(L".cso"))
|
||||
if (StringUtil::EndsWith(displayName, ".cso"))
|
||||
{
|
||||
FILE* fp = PX_fopen_rb(fileName);
|
||||
FILE* fp = FileSystem::OpenCFile(fileName.c_str(), "rb");
|
||||
CsoHeader hdr;
|
||||
if (fp)
|
||||
{
|
||||
@@ -67,17 +68,17 @@ bool CsoFileReader::ValidateHeader(const CsoHeader& hdr)
|
||||
}
|
||||
if (hdr.ver > 1)
|
||||
{
|
||||
Console.Error(L"Only CSOv1 files are supported.");
|
||||
Console.Error("Only CSOv1 files are supported.");
|
||||
return false;
|
||||
}
|
||||
if ((hdr.frame_size & (hdr.frame_size - 1)) != 0)
|
||||
{
|
||||
Console.Error(L"CSO frame size must be a power of two.");
|
||||
Console.Error("CSO frame size must be a power of two.");
|
||||
return false;
|
||||
}
|
||||
if (hdr.frame_size < 2048)
|
||||
{
|
||||
Console.Error(L"CSO frame size must be at least one sector.");
|
||||
Console.Error("CSO frame size must be at least one sector.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -85,11 +86,11 @@ bool CsoFileReader::ValidateHeader(const CsoHeader& hdr)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CsoFileReader::Open2(const wxString& fileName)
|
||||
bool CsoFileReader::Open2(std::string fileName)
|
||||
{
|
||||
Close2();
|
||||
m_filename = fileName;
|
||||
m_src = PX_fopen_rb(m_filename);
|
||||
m_filename = std::move(fileName);
|
||||
m_src = FileSystem::OpenCFile(m_filename.c_str(), "rb");
|
||||
|
||||
bool success = false;
|
||||
if (m_src && ReadFileHeader() && InitializeBuffers())
|
||||
@@ -109,16 +110,15 @@ bool CsoFileReader::ReadFileHeader()
|
||||
{
|
||||
CsoHeader hdr = {};
|
||||
|
||||
PX_fseeko(m_src, m_dataoffset, SEEK_SET);
|
||||
if (fread(&hdr, 1, sizeof(hdr), m_src) != sizeof(hdr))
|
||||
if (FileSystem::FSeek64(m_src, m_dataoffset, SEEK_SET) != 0 || std::fread(&hdr, 1, sizeof(hdr), m_src) != sizeof(hdr))
|
||||
{
|
||||
Console.Error(L"Failed to read CSO file header.");
|
||||
Console.Error("Failed to read CSO file header.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateHeader(hdr))
|
||||
{
|
||||
Console.Error(L"CSO has invalid header.");
|
||||
Console.Error("CSO has invalid header.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ bool CsoFileReader::InitializeBuffers()
|
||||
m_index = new u32[indexSize];
|
||||
if (fread(m_index, sizeof(u32), indexSize, m_src) != indexSize)
|
||||
{
|
||||
Console.Error(L"Unable to read index data from CSO.");
|
||||
Console.Error("Unable to read index data from CSO.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ bool CsoFileReader::InitializeBuffers()
|
||||
|
||||
void CsoFileReader::Close2()
|
||||
{
|
||||
m_filename.Empty();
|
||||
m_filename.clear();
|
||||
|
||||
if (m_src)
|
||||
{
|
||||
@@ -235,7 +235,7 @@ int CsoFileReader::ReadChunk(void *dst, s64 chunkID)
|
||||
if (!compressed)
|
||||
{
|
||||
// Just read directly, easy.
|
||||
if (PX_fseeko(m_src, frameRawPos, SEEK_SET) != 0)
|
||||
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
||||
{
|
||||
Console.Error("Unable to seek to uncompressed CSO data.");
|
||||
return 0;
|
||||
@@ -244,7 +244,7 @@ int CsoFileReader::ReadChunk(void *dst, s64 chunkID)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PX_fseeko(m_src, frameRawPos, SEEK_SET) != 0)
|
||||
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
||||
{
|
||||
Console.Error("Unable to seek to compressed CSO data.");
|
||||
return 0;
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
|
||||
~CsoFileReader(void) { Close(); };
|
||||
|
||||
static bool CanHandle(const wxString& fileName);
|
||||
bool Open2(const wxString& fileName) override;
|
||||
static bool CanHandle(const std::string& fileName, const std::string& displayName);
|
||||
bool Open2(std::string fileName) override;
|
||||
|
||||
Chunk ChunkForOffset(u64 offset) override;
|
||||
int ReadChunk(void *dst, s64 chunkID) override;
|
||||
|
||||
@@ -14,29 +14,17 @@
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include <fstream>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <fstream>
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "Config.h"
|
||||
#include "ChunksCache.h"
|
||||
#include "CompressedFileReaderUtils.h"
|
||||
#include "GzippedFileReader.h"
|
||||
#include "zlib_indexed.h"
|
||||
|
||||
#define CLAMP(val, minval, maxval) (std::min(maxval, std::max(minval, val)))
|
||||
|
||||
static s64 fsize(const wxString& filename)
|
||||
{
|
||||
if (!wxFileName::FileExists(filename))
|
||||
return -1;
|
||||
|
||||
std::ifstream f(PX_wfilename(filename), std::ifstream::binary);
|
||||
f.seekg(0, f.end);
|
||||
s64 size = f.tellg();
|
||||
f.close();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#define GZIP_ID "PCSX2.index.gzip.v1|"
|
||||
#define GZIP_ID_LEN (sizeof(GZIP_ID) - 1) /* sizeof includes the \0 terminator */
|
||||
|
||||
@@ -44,71 +32,75 @@ static s64 fsize(const wxString& filename)
|
||||
// - [GZIP_ID_LEN] GZIP_ID (no \0)
|
||||
// - [sizeof(Access)] index (should be allocated, contains various sizes)
|
||||
// - [rest] the indexed data points (should be allocated, index->list should then point to it)
|
||||
static Access* ReadIndexFromFile(const wxString& filename)
|
||||
static Access* ReadIndexFromFile(const char* filename)
|
||||
{
|
||||
s64 size = fsize(filename);
|
||||
if (size <= 0)
|
||||
auto fp = FileSystem::OpenManagedCFile(filename, "rb");
|
||||
s64 size;
|
||||
if (!fp || (size = FileSystem::FSize64(fp.get())) <= 0)
|
||||
{
|
||||
Console.Error(L"Error: Can't open index file: '%s'", WX_STR(filename));
|
||||
Console.Error("Error: Can't open index file: '%s'", filename);
|
||||
return 0;
|
||||
}
|
||||
std::ifstream infile(PX_wfilename(filename), std::ifstream::binary);
|
||||
|
||||
char fileId[GZIP_ID_LEN + 1] = {0};
|
||||
infile.read(fileId, GZIP_ID_LEN);
|
||||
if (wxString::From8BitData(GZIP_ID) != wxString::From8BitData(fileId))
|
||||
if (std::fread(fileId, GZIP_ID_LEN, 1, fp.get()) != 1 || std::memcmp(fileId, GZIP_ID, 4) != 0)
|
||||
{
|
||||
Console.Error(L"Error: Incompatible gzip index, please delete it manually: '%s'", WX_STR(filename));
|
||||
infile.close();
|
||||
Console.Error("Error: Incompatible gzip index, please delete it manually: '%s'", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Access* index = (Access*)malloc(sizeof(Access));
|
||||
infile.read((char*)index, sizeof(Access));
|
||||
|
||||
s64 datasize = size - GZIP_ID_LEN - sizeof(Access);
|
||||
if (datasize != (s64)index->have * sizeof(Point))
|
||||
Access* const index = (Access*)malloc(sizeof(Access));
|
||||
const s64 datasize = size - GZIP_ID_LEN - sizeof(Access);
|
||||
if (std::fread(index, sizeof(Access), 1, fp.get()) != 1 ||
|
||||
datasize != static_cast<s64>(index->have) * static_cast<s64>(sizeof(Point)))
|
||||
{
|
||||
Console.Error(L"Error: unexpected size of gzip index, please delete it manually: '%s'.", WX_STR(filename));
|
||||
infile.close();
|
||||
Console.Error("Error: unexpected size of gzip index, please delete it manually: '%s'.", filename);
|
||||
free(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* buffer = (char*)malloc(datasize);
|
||||
infile.read(buffer, datasize);
|
||||
infile.close();
|
||||
if (std::fread(buffer, datasize, 1, fp.get()) != 1)
|
||||
{
|
||||
Console.Error("Error: failed read of gzip index, please delete it manually: '%s'.", filename);
|
||||
free(buffer);
|
||||
free(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
index->list = (Point*)buffer; // adjust list pointer
|
||||
return index;
|
||||
}
|
||||
|
||||
static void WriteIndexToFile(Access* index, const wxString filename)
|
||||
static void WriteIndexToFile(Access* index, const char* filename)
|
||||
{
|
||||
if (wxFileName::FileExists(filename))
|
||||
if (FileSystem::FileExists(filename))
|
||||
{
|
||||
Console.Warning(L"WARNING: Won't write index - file name exists (please delete it manually): '%s'", WX_STR(filename));
|
||||
Console.Warning("WARNING: Won't write index - file name exists (please delete it manually): '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream outfile(PX_wfilename(filename), std::ofstream::binary);
|
||||
outfile.write(GZIP_ID, GZIP_ID_LEN);
|
||||
auto fp = FileSystem::OpenManagedCFile(filename, "wb");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
bool success = (std::fwrite(GZIP_ID, GZIP_ID_LEN, 1, fp.get()) == 1);
|
||||
|
||||
Point* tmp = index->list;
|
||||
index->list = 0; // current pointer is useless on disk, normalize it as 0.
|
||||
outfile.write((char*)index, sizeof(Access));
|
||||
std::fwrite((char*)index, sizeof(Access), 1, fp.get());
|
||||
index->list = tmp;
|
||||
|
||||
outfile.write((char*)index->list, sizeof(Point) * index->have);
|
||||
outfile.close();
|
||||
success = success && (std::fwrite((char*)index->list, sizeof(Point) * index->have, 1, fp.get()) == 1);
|
||||
|
||||
// Verify
|
||||
if (fsize(filename) != (s64)GZIP_ID_LEN + sizeof(Access) + sizeof(Point) * index->have)
|
||||
if (!success)
|
||||
{
|
||||
Console.Warning(L"Warning: Can't write index file to disk: '%s'", WX_STR(filename));
|
||||
Console.Warning("Warning: Can't write index file to disk: '%s'", filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn(Color_Green, L"OK: Gzip quick access index file saved to disk: '%s'", WX_STR(filename));
|
||||
Console.WriteLn(Color_Green, "OK: Gzip quick access index file saved to disk: '%s'", filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,10 +115,10 @@ static wxString INDEX_TEMPLATE_KEY(L"$(f)");
|
||||
// No checks are performed if the result file name can be created.
|
||||
// If this proves useful, we can move it into Path:: . Right now there's no need.
|
||||
static wxString ApplyTemplate(const wxString& name, const wxDirName& base,
|
||||
const wxString& fileTemplate, const wxString& filename,
|
||||
const std::string& fileTemplate, const std::string& filename,
|
||||
bool canEndWithKey)
|
||||
{
|
||||
wxString tem(fileTemplate);
|
||||
wxString tem(StringUtil::UTF8StringToWxString(fileTemplate));
|
||||
wxString key = INDEX_TEMPLATE_KEY;
|
||||
tem = tem.Trim(true).Trim(false); // both sides
|
||||
|
||||
@@ -141,7 +133,7 @@ static wxString ApplyTemplate(const wxString& name, const wxDirName& base,
|
||||
return L"";
|
||||
}
|
||||
|
||||
wxString fname(filename);
|
||||
wxString fname(StringUtil::UTF8StringToWxString(filename));
|
||||
if (first > 0)
|
||||
fname = Path::GetFilename(fname); // without path
|
||||
|
||||
@@ -179,13 +171,13 @@ static void TestTemplate(const wxDirName &base, const wxString &fname, bool canE
|
||||
}
|
||||
*/
|
||||
|
||||
static wxString iso2indexname(const wxString& isoname)
|
||||
static std::string iso2indexname(const std::string& isoname)
|
||||
{
|
||||
//testTemplate(isoname);
|
||||
wxDirName appRoot = // TODO: have only one of this in PCSX2. Right now have few...
|
||||
(wxDirName)(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath());
|
||||
//TestTemplate(appRoot, isoname, false);
|
||||
return ApplyTemplate(L"gzip index", appRoot, fromUTF8(EmuConfig.GzipIsoIndexTemplate), isoname, false);
|
||||
return StringUtil::wxStringToUTF8String(ApplyTemplate(L"gzip index", appRoot, EmuConfig.GzipIsoIndexTemplate, isoname, false));
|
||||
}
|
||||
|
||||
GzippedFileReader::GzippedFileReader(void)
|
||||
@@ -218,7 +210,7 @@ void GzippedFileReader::InitZstates()
|
||||
void GzippedFileReader::AsyncPrefetchReset(){};
|
||||
void GzippedFileReader::AsyncPrefetchOpen(){};
|
||||
void GzippedFileReader::AsyncPrefetchClose(){};
|
||||
void GzippedFileReader::AsyncPrefetchChunk(PX_off_t dummy){};
|
||||
void GzippedFileReader::AsyncPrefetchChunk(s64 dummy){};
|
||||
void GzippedFileReader::AsyncPrefetchCancel(){};
|
||||
#else
|
||||
// AsyncPrefetch works as follows:
|
||||
@@ -239,7 +231,7 @@ void GzippedFileReader::AsyncPrefetchReset()
|
||||
void GzippedFileReader::AsyncPrefetchOpen()
|
||||
{
|
||||
hOverlappedFile = CreateFile(
|
||||
m_filename,
|
||||
StringUtil::UTF8StringToWideString(m_filename).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
@@ -258,7 +250,7 @@ void GzippedFileReader::AsyncPrefetchClose()
|
||||
AsyncPrefetchReset();
|
||||
};
|
||||
|
||||
void GzippedFileReader::AsyncPrefetchChunk(PX_off_t start)
|
||||
void GzippedFileReader::AsyncPrefetchChunk(s64 start)
|
||||
{
|
||||
if (hOverlappedFile == INVALID_HANDLE_VALUE || asyncInProgress)
|
||||
{
|
||||
@@ -296,9 +288,9 @@ void GzippedFileReader::AsyncPrefetchCancel()
|
||||
#endif /* _WIN32 */
|
||||
|
||||
// TODO: do better than just checking existance and extension
|
||||
bool GzippedFileReader::CanHandle(const wxString& fileName)
|
||||
bool GzippedFileReader::CanHandle(const std::string& fileName, const std::string& displayName)
|
||||
{
|
||||
return wxFileName::FileExists(fileName) && fileName.Lower().EndsWith(L".gz");
|
||||
return StringUtil::EndsWith(fileName, ".gz");
|
||||
}
|
||||
|
||||
bool GzippedFileReader::OkIndex()
|
||||
@@ -307,29 +299,29 @@ bool GzippedFileReader::OkIndex()
|
||||
return true;
|
||||
|
||||
// Try to read index from disk
|
||||
wxString indexfile = iso2indexname(m_filename);
|
||||
if (indexfile.length() == 0)
|
||||
const std::string indexfile(iso2indexname(m_filename));
|
||||
if (indexfile.empty())
|
||||
return false; // iso2indexname(...) will print errors if it can't apply the template
|
||||
|
||||
if (wxFileName::FileExists(indexfile) && (m_pIndex = ReadIndexFromFile(indexfile)))
|
||||
if (FileSystem::FileExists(indexfile.c_str()) && (m_pIndex = ReadIndexFromFile(indexfile.c_str())))
|
||||
{
|
||||
Console.WriteLn(Color_Green, L"OK: Gzip quick access index read from disk: '%s'", WX_STR(indexfile));
|
||||
Console.WriteLn(Color_Green, "OK: Gzip quick access index read from disk: '%s'", indexfile.c_str());
|
||||
if (m_pIndex->span != GZFILE_SPAN_DEFAULT)
|
||||
{
|
||||
Console.Warning(L"Note: This index has %1.1f MB intervals, while the current default for new indexes is %1.1f MB.",
|
||||
Console.Warning("Note: This index has %1.1f MB intervals, while the current default for new indexes is %1.1f MB.",
|
||||
(float)m_pIndex->span / 1024 / 1024, (float)GZFILE_SPAN_DEFAULT / 1024 / 1024);
|
||||
Console.Warning(L"It will work fine, but if you want to generate a new index with default intervals, delete this index file.");
|
||||
Console.Warning(L"(smaller intervals mean bigger index file and quicker but more frequent decompressions)");
|
||||
Console.Warning("It will work fine, but if you want to generate a new index with default intervals, delete this index file.");
|
||||
Console.Warning("(smaller intervals mean bigger index file and quicker but more frequent decompressions)");
|
||||
}
|
||||
InitZstates();
|
||||
return true;
|
||||
}
|
||||
|
||||
// No valid index file. Generate an index
|
||||
Console.Warning(L"This may take a while (but only once). Scanning compressed file to generate a quick access index...");
|
||||
Console.Warning("This may take a while (but only once). Scanning compressed file to generate a quick access index...");
|
||||
|
||||
Access* index;
|
||||
FILE* infile = PX_fopen_rb(m_filename);
|
||||
FILE* infile = FileSystem::OpenCFile(m_filename.c_str(), "rb");
|
||||
int len = build_index(infile, GZFILE_SPAN_DEFAULT, &index);
|
||||
printf("\n"); // build_index prints progress without \n's
|
||||
fclose(infile);
|
||||
@@ -337,11 +329,11 @@ bool GzippedFileReader::OkIndex()
|
||||
if (len >= 0)
|
||||
{
|
||||
m_pIndex = index;
|
||||
WriteIndexToFile((Access*)m_pIndex, indexfile);
|
||||
WriteIndexToFile((Access*)m_pIndex, indexfile.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(L"ERROR (%d): index could not be generated for file '%s'", len, WX_STR(m_filename));
|
||||
Console.Error("ERROR (%d): index could not be generated for file '%s'", len, m_filename.c_str());
|
||||
free_index(index);
|
||||
InitZstates();
|
||||
return false;
|
||||
@@ -351,11 +343,11 @@ bool GzippedFileReader::OkIndex()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GzippedFileReader::Open(const wxString& fileName)
|
||||
bool GzippedFileReader::Open(std::string fileName)
|
||||
{
|
||||
Close();
|
||||
m_filename = fileName;
|
||||
if (!(m_src = PX_fopen_rb(m_filename)) || !CanHandle(fileName) || !OkIndex())
|
||||
m_filename = std::move(fileName);
|
||||
if (!(m_src = FileSystem::OpenCFile(m_filename.c_str(), "rb")) || !OkIndex())
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
@@ -384,7 +376,7 @@ int GzippedFileReader::FinishRead(void)
|
||||
|
||||
int GzippedFileReader::ReadSync(void* pBuffer, uint sector, uint count)
|
||||
{
|
||||
PX_off_t offset = (s64)sector * m_blocksize + m_dataoffset;
|
||||
s64 offset = (s64)sector * m_blocksize + m_dataoffset;
|
||||
int bytesToRead = count * m_blocksize;
|
||||
int res = _ReadSync(pBuffer, offset, bytesToRead);
|
||||
if (res < 0)
|
||||
@@ -393,11 +385,11 @@ int GzippedFileReader::ReadSync(void* pBuffer, uint sector, uint count)
|
||||
}
|
||||
|
||||
// If we have a valid and adequate zstate for this span, use it, else, use the index
|
||||
PX_off_t GzippedFileReader::GetOptimalExtractionStart(PX_off_t offset)
|
||||
s64 GzippedFileReader::GetOptimalExtractionStart(s64 offset)
|
||||
{
|
||||
int span = m_pIndex->span;
|
||||
Czstate& cstate = m_zstates[offset / span];
|
||||
PX_off_t stateOffset = cstate.state.isValid ? cstate.state.out_offset : 0;
|
||||
s64 stateOffset = cstate.state.isValid ? cstate.state.out_offset : 0;
|
||||
if (stateOffset && stateOffset <= offset)
|
||||
return stateOffset; // state is faster than indexed
|
||||
|
||||
@@ -409,7 +401,7 @@ PX_off_t GzippedFileReader::GetOptimalExtractionStart(PX_off_t offset)
|
||||
return span * (offset / span); // index direct access boundaries
|
||||
}
|
||||
|
||||
int GzippedFileReader::_ReadSync(void* pBuffer, PX_off_t offset, uint bytesToRead)
|
||||
int GzippedFileReader::_ReadSync(void* pBuffer, s64 offset, uint bytesToRead)
|
||||
{
|
||||
if (!OkIndex())
|
||||
return -1;
|
||||
@@ -441,7 +433,7 @@ int GzippedFileReader::_ReadSync(void* pBuffer, PX_off_t offset, uint bytesToRea
|
||||
// Not available from cache. Decompress from optimal starting
|
||||
// point in GZFILE_READ_CHUNK_SIZE chunks and cache each chunk.
|
||||
PTT s = NOW();
|
||||
PX_off_t extractOffset = GetOptimalExtractionStart(offset); // guaranteed in GZFILE_READ_CHUNK_SIZE boundaries
|
||||
s64 extractOffset = GetOptimalExtractionStart(offset); // guaranteed in GZFILE_READ_CHUNK_SIZE boundaries
|
||||
int size = offset + maxInChunk - extractOffset;
|
||||
unsigned char* extracted = (unsigned char*)malloc(size);
|
||||
|
||||
@@ -501,7 +493,7 @@ int GzippedFileReader::_ReadSync(void* pBuffer, PX_off_t offset, uint bytesToRea
|
||||
|
||||
void GzippedFileReader::Close()
|
||||
{
|
||||
m_filename.Empty();
|
||||
m_filename.clear();
|
||||
if (m_pIndex)
|
||||
{
|
||||
free_index((Access*)m_pIndex);
|
||||
|
||||
@@ -34,8 +34,8 @@ public:
|
||||
|
||||
virtual ~GzippedFileReader(void) { Close(); };
|
||||
|
||||
static bool CanHandle(const wxString& fileName);
|
||||
virtual bool Open(const wxString& fileName);
|
||||
static bool CanHandle(const std::string& fileName, const std::string& displayName);
|
||||
virtual bool Open(std::string fileName);
|
||||
|
||||
virtual int ReadSync(void* pBuffer, uint sector, uint count);
|
||||
|
||||
@@ -71,8 +71,8 @@ private:
|
||||
};
|
||||
|
||||
bool OkIndex(); // Verifies that we have an index, or try to create one
|
||||
PX_off_t GetOptimalExtractionStart(PX_off_t offset);
|
||||
int _ReadSync(void* pBuffer, PX_off_t offset, uint bytesToRead);
|
||||
s64 GetOptimalExtractionStart(s64 offset);
|
||||
int _ReadSync(void* pBuffer, s64 offset, uint bytesToRead);
|
||||
void InitZstates();
|
||||
|
||||
int mBytesRead; // Temp sync read result when simulating async read
|
||||
@@ -93,6 +93,6 @@ private:
|
||||
void AsyncPrefetchReset();
|
||||
void AsyncPrefetchOpen();
|
||||
void AsyncPrefetchClose();
|
||||
void AsyncPrefetchChunk(PX_off_t dummy);
|
||||
void AsyncPrefetchChunk(s64 dummy);
|
||||
void AsyncPrefetchCancel();
|
||||
};
|
||||
|
||||
@@ -194,19 +194,16 @@ void InputIsoFile::_init()
|
||||
//
|
||||
// Note that this is a member method, and that it will clobber any existing ISO state.
|
||||
// (assertions are generated in debug mode if the object state is not already closed).
|
||||
bool InputIsoFile::Test(const wxString& srcfile)
|
||||
bool InputIsoFile::Test(std::string srcfile)
|
||||
{
|
||||
Close();
|
||||
m_filename = srcfile;
|
||||
|
||||
return Open(srcfile, true);
|
||||
return Open(std::move(srcfile), true);
|
||||
}
|
||||
|
||||
bool InputIsoFile::Open(const wxString& srcfile, bool testOnly)
|
||||
bool InputIsoFile::Open(std::string srcfile, bool testOnly)
|
||||
{
|
||||
Close();
|
||||
m_filename = srcfile;
|
||||
m_reader = NULL;
|
||||
m_filename = std::move(srcfile);
|
||||
|
||||
bool isBlockdump = false;
|
||||
bool isCompressed = false;
|
||||
@@ -224,7 +221,8 @@ bool InputIsoFile::Open(const wxString& srcfile, bool testOnly)
|
||||
m_reader = new FlatFileReader(EmuConfig.CdvdShareWrite);
|
||||
}
|
||||
|
||||
m_reader->Open(m_filename);
|
||||
if (!m_reader->Open(m_filename))
|
||||
return false;
|
||||
|
||||
// It might actually be a blockdump file.
|
||||
// Check that before continuing with the FlatFileReader.
|
||||
@@ -270,7 +268,7 @@ bool InputIsoFile::Open(const wxString& srcfile, bool testOnly)
|
||||
|
||||
m_blocks = m_reader->GetBlockCount();
|
||||
|
||||
Console.WriteLn(Color_StrongBlue, L"isoFile open ok: %s", WX_STR(m_filename));
|
||||
Console.WriteLn(Color_StrongBlue, "isoFile open ok: %s", m_filename.c_str());
|
||||
|
||||
ConsoleIndentScope indent;
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "CDVD.h"
|
||||
#include "wx/wfstream.h"
|
||||
#include "AsyncFileReader.h"
|
||||
#include "CompressedFileReader.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
enum isoType
|
||||
{
|
||||
@@ -45,7 +45,7 @@ protected:
|
||||
uint ReadUnit;
|
||||
|
||||
protected:
|
||||
wxString m_filename;
|
||||
std::string m_filename;
|
||||
AsyncFileReader* m_reader;
|
||||
|
||||
u32 m_current_lsn;
|
||||
@@ -75,13 +75,13 @@ public:
|
||||
uint GetBlockCount() const { return m_blocks; }
|
||||
int GetBlockOffset() const { return m_blockofs; }
|
||||
|
||||
const wxString& GetFilename() const
|
||||
const std::string& GetFilename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
bool Test(const wxString& srcfile);
|
||||
bool Open(const wxString& srcfile, bool testOnly = false);
|
||||
bool Test(std::string srcfile);
|
||||
bool Open(std::string srcfile, bool testOnly = false);
|
||||
void Close();
|
||||
bool Detect(bool readType = true);
|
||||
|
||||
@@ -102,7 +102,7 @@ class OutputIsoFile
|
||||
DeclareNoncopyableObject(OutputIsoFile);
|
||||
|
||||
protected:
|
||||
wxString m_filename;
|
||||
std::string m_filename;
|
||||
|
||||
u32 m_version;
|
||||
|
||||
@@ -116,7 +116,7 @@ protected:
|
||||
// dtable is used when reading blockdumps
|
||||
std::vector<u32> m_dtable;
|
||||
|
||||
std::unique_ptr<wxFileOutputStream> m_outstream;
|
||||
std::FILE* m_outstream = nullptr;
|
||||
|
||||
public:
|
||||
OutputIsoFile();
|
||||
@@ -125,12 +125,12 @@ public:
|
||||
bool IsOpened() const;
|
||||
u32 GetBlockSize() const;
|
||||
|
||||
const wxString& GetFilename() const
|
||||
const std::string& GetFilename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
void Create(const wxString& filename, int mode);
|
||||
void Create(std::string filename, int mode);
|
||||
void Close();
|
||||
|
||||
void WriteHeader(int blockofs, uint blocksize, uint blocks);
|
||||
|
||||
@@ -17,15 +17,17 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "IopCommon.h"
|
||||
#include "IsoFileFormats.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
void pxStream_OpenCheck(const wxStreamBase& stream, const wxString& fname, const wxString& mode)
|
||||
void pxStream_OpenCheck(std::FILE* stream, const std::string& fname, const wxString& mode)
|
||||
{
|
||||
if (stream.IsOk())
|
||||
if (stream)
|
||||
return;
|
||||
|
||||
ScopedExcept ex(Exception::FromErrno(fname, errno));
|
||||
ScopedExcept ex(Exception::FromErrno(StringUtil::UTF8StringToWxString(fname), errno));
|
||||
ex->SetDiagMsg(pxsFmt(L"Unable to open the file for %s: %s", WX_STR(mode), WX_STR(ex->DiagMsg())));
|
||||
ex->Rethrow();
|
||||
}
|
||||
@@ -50,20 +52,20 @@ void OutputIsoFile::_init()
|
||||
m_blocks = 0;
|
||||
}
|
||||
|
||||
void OutputIsoFile::Create(const wxString& filename, int version)
|
||||
void OutputIsoFile::Create(std::string filename, int version)
|
||||
{
|
||||
Close();
|
||||
m_filename = filename;
|
||||
m_filename = std::move(filename);
|
||||
|
||||
m_version = version;
|
||||
m_offset = 0;
|
||||
m_blockofs = 24;
|
||||
m_blocksize = 2048;
|
||||
|
||||
m_outstream = std::make_unique<wxFileOutputStream>(m_filename);
|
||||
pxStream_OpenCheck(*m_outstream, m_filename, L"writing");
|
||||
m_outstream = FileSystem::OpenCFile(m_filename.c_str(), "wb");
|
||||
pxStream_OpenCheck(m_outstream, m_filename, L"writing");
|
||||
|
||||
Console.WriteLn("isoFile create ok: %s ", WX_STR(m_filename));
|
||||
Console.WriteLn("isoFile create ok: %s ", m_filename.c_str());
|
||||
}
|
||||
|
||||
// Generates format header information for blockdumps.
|
||||
@@ -100,9 +102,8 @@ void OutputIsoFile::WriteSector(const u8* src, uint lsn)
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFileOffset ofs = (wxFileOffset)lsn * m_blocksize + m_offset;
|
||||
|
||||
m_outstream->SeekO(ofs);
|
||||
const s64 ofs = (s64)lsn * m_blocksize + m_offset;
|
||||
FileSystem::FSeek64(m_outstream, ofs, SEEK_SET);
|
||||
}
|
||||
|
||||
WriteBuffer(src + m_blockofs, m_blocksize);
|
||||
@@ -112,19 +113,27 @@ void OutputIsoFile::Close()
|
||||
{
|
||||
m_dtable.clear();
|
||||
|
||||
if (m_outstream)
|
||||
{
|
||||
std::fclose(m_outstream);
|
||||
m_outstream = nullptr;
|
||||
}
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void OutputIsoFile::WriteBuffer(const void* src, size_t size)
|
||||
{
|
||||
m_outstream->Write(src, size);
|
||||
if (m_outstream->GetLastError() == wxSTREAM_WRITE_ERROR)
|
||||
if (std::fwrite(src, size, 1, m_outstream) != 1)
|
||||
{
|
||||
int err = errno;
|
||||
if (!err)
|
||||
throw Exception::BadStream(m_filename).SetDiagMsg(pxsFmt(L"An error occurred while writing %u bytes to file", size));
|
||||
{
|
||||
throw Exception::BadStream(StringUtil::UTF8StringToWxString(m_filename))
|
||||
.SetDiagMsg(pxsFmt(L"An error occurred while writing %u bytes to file", size));
|
||||
}
|
||||
|
||||
ScopedExcept ex(Exception::FromErrno(m_filename, err));
|
||||
ScopedExcept ex(Exception::FromErrno(StringUtil::UTF8StringToWxString(m_filename), err));
|
||||
ex->SetDiagMsg(pxsFmt(L"An error occurred while writing %u bytes to file: %s", size, WX_STR(ex->DiagMsg())));
|
||||
ex->Rethrow();
|
||||
}
|
||||
@@ -132,7 +141,7 @@ void OutputIsoFile::WriteBuffer(const void* src, size_t size)
|
||||
|
||||
bool OutputIsoFile::IsOpened() const
|
||||
{
|
||||
return m_outstream && m_outstream->IsOk();
|
||||
return m_outstream != nullptr;
|
||||
}
|
||||
|
||||
u32 OutputIsoFile::GetBlockSize() const
|
||||
|
||||
@@ -239,10 +239,10 @@ bool ThreadedFileReader::TryCachedRead(void*& buffer, u64& offset, u32& size, co
|
||||
return allDone;
|
||||
}
|
||||
|
||||
bool ThreadedFileReader::Open(const wxString& fileName)
|
||||
bool ThreadedFileReader::Open(std::string fileName)
|
||||
{
|
||||
CancelAndWaitUntilStopped();
|
||||
return Open2(fileName);
|
||||
return Open2(std::move(fileName));
|
||||
}
|
||||
|
||||
int ThreadedFileReader::ReadSync(void* pBuffer, uint sector, uint count)
|
||||
|
||||
@@ -47,7 +47,7 @@ protected:
|
||||
/// Synchronously read the given block into `dst`
|
||||
virtual int ReadChunk(void* dst, s64 chunkID) = 0;
|
||||
/// AsyncFileReader open but ThreadedFileReader needs prep work first
|
||||
virtual bool Open2(const wxString& fileName) = 0;
|
||||
virtual bool Open2(std::string fileName) = 0;
|
||||
/// AsyncFileReader close but ThreadedFileReader needs prep work first
|
||||
virtual void Close2(void) = 0;
|
||||
|
||||
@@ -109,7 +109,7 @@ private:
|
||||
bool TryCachedRead(void*& buffer, u64& offset, u32& size, const std::lock_guard<std::mutex>&);
|
||||
|
||||
public:
|
||||
bool Open(const wxString& fileName) final override;
|
||||
bool Open(std::string fileName) final override;
|
||||
int ReadSync(void* pBuffer, uint sector, uint count) final override;
|
||||
void BeginRead(void* pBuffer, uint sector, uint count) final override;
|
||||
int FinishRead(void) final override;
|
||||
|
||||
+15
-15
@@ -107,7 +107,7 @@ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
||||
#include <zlib/zlib.h>
|
||||
#endif
|
||||
|
||||
#include "CompressedFileReaderUtils.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#define local static
|
||||
|
||||
@@ -122,8 +122,8 @@ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
||||
/* access point entry */
|
||||
struct point
|
||||
{
|
||||
PX_off_t out; /* corresponding offset in uncompressed data */
|
||||
PX_off_t in; /* offset in input file of first full byte */
|
||||
s64 out; /* corresponding offset in uncompressed data */
|
||||
s64 in; /* offset in input file of first full byte */
|
||||
int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
|
||||
unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */
|
||||
}
|
||||
@@ -142,7 +142,7 @@ struct access
|
||||
struct point* list; /* allocated list */
|
||||
|
||||
s32 span; /* once the index is built, holds the span size used to build it */
|
||||
PX_off_t uncompressed_size; /* filled by build_index */
|
||||
s64 uncompressed_size; /* filled by build_index */
|
||||
}
|
||||
#ifndef _WIN32
|
||||
__attribute__((packed))
|
||||
@@ -168,7 +168,7 @@ local void free_index(struct access* index)
|
||||
/* Add an entry to the access point list. If out of memory, deallocate the
|
||||
existing list and return NULL. */
|
||||
local struct access* addpoint(struct access* index, int bits,
|
||||
PX_off_t in, PX_off_t out, unsigned left, unsigned char* window)
|
||||
s64 in, s64 out, unsigned left, unsigned char* window)
|
||||
{
|
||||
struct point* next;
|
||||
|
||||
@@ -224,11 +224,11 @@ local struct access* addpoint(struct access* index, int bits,
|
||||
returns the number of access points on success (>= 1), Z_MEM_ERROR for out
|
||||
of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
|
||||
file read error. On success, *built points to the resulting index. */
|
||||
local int build_index(FILE* in, PX_off_t span, struct access** built)
|
||||
local int build_index(FILE* in, s64 span, struct access** built)
|
||||
{
|
||||
int ret;
|
||||
PX_off_t totin, totout, totPrinted; /* our own total counters to avoid 4GB limit */
|
||||
PX_off_t last; /* totout value of last access point */
|
||||
s64 totin, totout, totPrinted; /* our own total counters to avoid 4GB limit */
|
||||
s64 last; /* totout value of last access point */
|
||||
struct access* index; /* access points being generated */
|
||||
z_stream strm;
|
||||
unsigned char input[CHUNK];
|
||||
@@ -344,13 +344,13 @@ build_index_error:
|
||||
|
||||
typedef struct zstate
|
||||
{
|
||||
PX_off_t out_offset;
|
||||
PX_off_t in_offset;
|
||||
s64 out_offset;
|
||||
s64 in_offset;
|
||||
z_stream strm;
|
||||
int isValid;
|
||||
} Zstate;
|
||||
|
||||
static inline PX_off_t getInOffset(zstate* state)
|
||||
static inline s64 getInOffset(zstate* state)
|
||||
{
|
||||
return state->in_offset;
|
||||
}
|
||||
@@ -362,7 +362,7 @@ static inline PX_off_t getInOffset(zstate* state)
|
||||
should not return a data error unless the file was modified since the index
|
||||
was generated. extract() may also return Z_ERRNO if there is an error on
|
||||
reading or seeking the input file. */
|
||||
local int extract(FILE* in, struct access* index, PX_off_t offset,
|
||||
local int extract(FILE* in, struct access* index, s64 offset,
|
||||
unsigned char* buf, int len, zstate* state)
|
||||
{
|
||||
int ret, skip;
|
||||
@@ -386,7 +386,7 @@ local int extract(FILE* in, struct access* index, PX_off_t offset,
|
||||
if (state->isValid)
|
||||
{
|
||||
state->isValid = 0; // we took control over strm. revalidate when/if we give it back
|
||||
PX_fseeko(in, state->in_offset, SEEK_SET);
|
||||
FileSystem::FSeek64(in, state->in_offset, SEEK_SET);
|
||||
state->strm.avail_in = 0;
|
||||
offset = 0;
|
||||
skip = 1;
|
||||
@@ -408,7 +408,7 @@ local int extract(FILE* in, struct access* index, PX_off_t offset,
|
||||
ret = inflateInit2(&state->strm, -15); /* raw inflate */
|
||||
if (ret != Z_OK)
|
||||
return ret;
|
||||
ret = PX_fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
|
||||
ret = FileSystem::FSeek64(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
|
||||
if (ret == -1)
|
||||
goto extract_ret;
|
||||
if (here->bits)
|
||||
@@ -456,7 +456,7 @@ local int extract(FILE* in, struct access* index, PX_off_t offset,
|
||||
{
|
||||
if (state->strm.avail_in == 0)
|
||||
{
|
||||
state->in_offset = PX_ftello(in);
|
||||
state->in_offset = FileSystem::FTell64(in);
|
||||
state->strm.avail_in = fread(input, 1, CHUNK, in);
|
||||
if (ferror(in))
|
||||
{
|
||||
|
||||
+34
-69
@@ -70,10 +70,6 @@ if(TARGET SDL::SDL)
|
||||
target_link_libraries(PCSX2_FLAGS INTERFACE SDL::SDL)
|
||||
endif()
|
||||
|
||||
if(WAYLAND_CLIENT_FOUND)
|
||||
target_compile_definitions(PCSX2 PRIVATE WAYLAND_BUILD)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Resources
|
||||
target_sources(PCSX2 PRIVATE
|
||||
@@ -130,7 +126,7 @@ set(pcsx2Sources
|
||||
IopIrq.cpp
|
||||
IopMem.cpp
|
||||
IopSio2.cpp
|
||||
IPC.cpp
|
||||
PINE.cpp
|
||||
Mdec.cpp
|
||||
Memory.cpp
|
||||
MemoryCardFile.cpp
|
||||
@@ -142,6 +138,7 @@ set(pcsx2Sources
|
||||
Patch.cpp
|
||||
Patch_Memory.cpp
|
||||
Pcsx2Config.cpp
|
||||
PerformanceMetrics.cpp
|
||||
PrecompiledHeader.cpp
|
||||
R3000A.cpp
|
||||
R3000AInterpreter.cpp
|
||||
@@ -194,6 +191,7 @@ set(pcsx2Headers
|
||||
Gif_Unit.h
|
||||
GS.h
|
||||
Hardware.h
|
||||
Host.h
|
||||
Hw.h
|
||||
IopBios.h
|
||||
IopCommon.h
|
||||
@@ -203,7 +201,7 @@ set(pcsx2Headers
|
||||
IopHw.h
|
||||
IopMem.h
|
||||
IopSio2.h
|
||||
IPC.h
|
||||
PINE.h
|
||||
Mdec.h
|
||||
MTVU.h
|
||||
Memory.h
|
||||
@@ -213,6 +211,7 @@ set(pcsx2Headers
|
||||
Patch.h
|
||||
PathDefs.h
|
||||
PCSX2Base.h
|
||||
PerformanceMetrics.h
|
||||
PrecompiledHeader.h
|
||||
R3000A.h
|
||||
R5900Exceptions.h
|
||||
@@ -267,7 +266,6 @@ set(pcsx2CDVDHeaders
|
||||
CDVD/CDVDisoReader.h
|
||||
CDVD/ChunksCache.h
|
||||
CDVD/CompressedFileReader.h
|
||||
CDVD/CompressedFileReaderUtils.h
|
||||
CDVD/ChdFileReader.h
|
||||
CDVD/CsoFileReader.h
|
||||
CDVD/GzippedFileReader.h
|
||||
@@ -530,18 +528,6 @@ else(WIN32)
|
||||
COMMAND ${BIN2CPP} "${PADImg}/${result_file}.png" "${PADImgHeader}/${result_file}" )
|
||||
endforeach()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.h
|
||||
COMMAND glib-compile-resources --sourcedir "${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res" --generate-header
|
||||
--c-name PAD_res "${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/PAD-res.xml" --target=${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.h
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/PAD-res.xml ${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/game_controller_db.txt)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.cpp
|
||||
COMMAND glib-compile-resources --sourcedir "${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res" --generate-source
|
||||
--c-name PAD_res "${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/PAD-res.xml" --target=${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.cpp
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/PAD-res.xml ${CMAKE_SOURCE_DIR}/pcsx2/PAD/Linux/res/game_controller_db.txt)
|
||||
|
||||
# PAD/Linux sources
|
||||
set(pcsx2PADSources
|
||||
PAD/Linux/Device.cpp
|
||||
@@ -556,7 +542,6 @@ else(WIN32)
|
||||
PAD/Linux/wx_dialog/GamepadConfiguration.cpp
|
||||
PAD/Linux/wx_dialog/JoystickConfiguration.cpp
|
||||
PAD/Linux/Config.cpp
|
||||
${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.cpp
|
||||
)
|
||||
|
||||
# PAD/Linux headers
|
||||
@@ -574,7 +559,6 @@ else(WIN32)
|
||||
PAD/Linux/wx_dialog/opPanel.h
|
||||
PAD/Linux/wx_dialog/GamepadConfiguration.h
|
||||
PAD/Linux/wx_dialog/JoystickConfiguration.h
|
||||
${CMAKE_BINARY_DIR}/pcsx2/PAD/Linux/resources_pad.h
|
||||
# images
|
||||
${PADImgHeader}/analog.h
|
||||
${PADImgHeader}/circle.h
|
||||
@@ -857,42 +841,6 @@ else(WIN32)
|
||||
USB/usb-pad/evdev/evdev.h
|
||||
USB/usb-pad/evdev/shared.h
|
||||
)
|
||||
|
||||
|
||||
# GS resources
|
||||
set(pcsx2GSResources
|
||||
GS/res/fxaa.fx
|
||||
GS/res/glsl/common_header.glsl
|
||||
GS/res/glsl/convert.glsl
|
||||
GS/res/glsl/interlace.glsl
|
||||
GS/res/glsl/merge.glsl
|
||||
GS/res/glsl/shadeboost.glsl
|
||||
GS/res/glsl/tfx_fs.glsl
|
||||
GS/res/glsl/tfx_vgs.glsl)
|
||||
|
||||
set(GSBin "${CMAKE_BINARY_DIR}/pcsx2/GS")
|
||||
target_include_directories(PCSX2_FLAGS INTERFACE "${GSBin}")
|
||||
file(MAKE_DIRECTORY "${GSBin}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/pcsx2/GS/GS_res.h
|
||||
COMMAND glib-compile-resources --sourcedir "${CMAKE_SOURCE_DIR}/pcsx2/GS/res" --generate-header
|
||||
--c-name GS_res "${CMAKE_SOURCE_DIR}/pcsx2/GS/res/GS-res.xml" --target=${CMAKE_BINARY_DIR}/pcsx2/GS/GS_res.h
|
||||
DEPENDS ${pcsx2GSResources})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/pcsx2/GS/GS_res.cpp
|
||||
COMMAND glib-compile-resources --sourcedir "${CMAKE_SOURCE_DIR}/pcsx2/GS/res" --generate-source
|
||||
--c-name GS_res "${CMAKE_SOURCE_DIR}/pcsx2/GS/res/GS-res.xml" --target=${CMAKE_BINARY_DIR}/pcsx2/GS/GS_res.cpp
|
||||
DEPENDS ${pcsx2GSResources})
|
||||
|
||||
list(APPEND pcsx2GSSources
|
||||
GS/GS_res.cpp
|
||||
)
|
||||
|
||||
list(APPEND pcsx2GSHeaders
|
||||
GS/GS_res.h
|
||||
)
|
||||
endif(WIN32)
|
||||
|
||||
|
||||
@@ -936,14 +884,13 @@ set(pcsx2GuiSources
|
||||
gui/AppConfig.cpp
|
||||
gui/AppCoreThread.cpp
|
||||
gui/AppEventSources.cpp
|
||||
gui/AppGameDatabase.cpp
|
||||
gui/AppHost.cpp
|
||||
gui/AppUserMode.cpp
|
||||
gui/AppInit.cpp
|
||||
gui/AppMain.cpp
|
||||
gui/AppRes.cpp
|
||||
gui/CheckedStaticBox.cpp
|
||||
gui/ConsoleLogger.cpp
|
||||
gui/CpuUsageProvider.cpp
|
||||
gui/Dialogs/AboutBoxDialog.cpp
|
||||
gui/Dialogs/GSDumpDialog.cpp
|
||||
gui/Dialogs/AssertionDialog.cpp
|
||||
@@ -957,7 +904,7 @@ set(pcsx2GuiSources
|
||||
gui/Dialogs/McdConfigDialog.cpp
|
||||
gui/Dialogs/PickUserModeDialog.cpp
|
||||
gui/Dialogs/SysConfigDialog.cpp
|
||||
gui/Dialogs/IPCDialog.cpp
|
||||
gui/Dialogs/PINEDialog.cpp
|
||||
gui/Debugger/BreakpointWindow.cpp
|
||||
gui/Debugger/CtrlDisassemblyView.cpp
|
||||
gui/Debugger/CtrlRegisterList.cpp
|
||||
@@ -1012,13 +959,11 @@ set(pcsx2GuiHeaders
|
||||
gui/AppCoreThread.h
|
||||
gui/AppEventListeners.h
|
||||
gui/AppForwardDefs.h
|
||||
gui/AppGameDatabase.h
|
||||
gui/App.h
|
||||
gui/ApplyState.h
|
||||
gui/AppSaveStates.h
|
||||
gui/CheckedStaticBox.h
|
||||
gui/ConsoleLogger.h
|
||||
gui/CpuUsageProvider.h
|
||||
gui/Debugger/BreakpointWindow.h
|
||||
gui/Debugger/CtrlDisassemblyView.h
|
||||
gui/Debugger/CtrlMemView.h
|
||||
@@ -1395,7 +1340,7 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
if(WAYLAND_API)
|
||||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||
${WAYLAND_CLIENT_LIBRARIES}
|
||||
Wayland::Client
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1465,7 +1410,6 @@ if(WIN32)
|
||||
)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||
PkgConfig::GIO
|
||||
PCAP::PCAP
|
||||
LibXml2::LibXml2
|
||||
)
|
||||
@@ -1520,6 +1464,31 @@ if(COMMAND target_precompile_headers)
|
||||
target_precompile_headers(PCSX2_FLAGS INTERFACE PrecompiledHeader.h)
|
||||
endif()
|
||||
|
||||
# Copy resource files if needed
|
||||
function(pcsx2_resource path basedir)
|
||||
get_filename_component(dir ${path} DIRECTORY)
|
||||
file(RELATIVE_PATH subdir ${basedir} ${dir})
|
||||
if(APPLE)
|
||||
target_sources(PCSX2 PRIVATE ${path})
|
||||
set_source_files_properties(${path} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/${subdir})
|
||||
elseif(PACKAGE_MODE)
|
||||
install(FILES ${path} DESTINATION ${CMAKE_INSTALL_DATADIR}/PCSX2/resources/${subdir})
|
||||
endif()
|
||||
source_group(Resources/${subdir} FILES ${path})
|
||||
endfunction()
|
||||
|
||||
file(GLOB_RECURSE RESOURCE_FILES ${CMAKE_SOURCE_DIR}/bin/resources/*)
|
||||
foreach(path IN LISTS RESOURCE_FILES)
|
||||
get_filename_component(file ${path} NAME)
|
||||
if("${file}" MATCHES "^\\.") # Don't copy macOS garbage (mainly Finder's .DS_Store files) into application
|
||||
continue()
|
||||
endif()
|
||||
if (NOT WIN32 AND "${path}" MATCHES "/dx11/") # Don't include unneccessary stuff
|
||||
continue()
|
||||
endif()
|
||||
pcsx2_resource(${path} ${CMAKE_SOURCE_DIR}/bin/resources/)
|
||||
endforeach()
|
||||
|
||||
if (APPLE)
|
||||
# MacOS defaults to having a maximum protection of the __DATA segment of rw (non-executable)
|
||||
# We have a bunch of page-sized arrays in bss that we use for jit
|
||||
@@ -1532,10 +1501,7 @@ if (APPLE)
|
||||
OUTPUT_NAME PCSX2
|
||||
)
|
||||
|
||||
target_sources(PCSX2 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gui/Resources/PCSX2.icns")
|
||||
target_sources(PCSX2 PRIVATE "${CMAKE_SOURCE_DIR}/bin/GameIndex.yaml")
|
||||
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/gui/Resources/PCSX2.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/bin/GameIndex.yaml" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
pcsx2_resource(${CMAKE_CURRENT_SOURCE_DIR}/gui/Resources/PCSX2.icns ${CMAKE_CURRENT_SOURCE_DIR}/gui/Resources)
|
||||
|
||||
# If they say to skip postprocess bundle, leave the target in but make it so they have
|
||||
# to manually run it
|
||||
@@ -1580,5 +1546,4 @@ source_group(System/Ps2/USB REGULAR_EXPRESSION USB/*)
|
||||
source_group(Resources/GUI FILES ${pcsx2GuiResources})
|
||||
source_group(Resources/PAD FILES ${pcsx2PADResources})
|
||||
source_group(Resources/Recording FILES ${pcsx2RecordingVirtualPadResources})
|
||||
source_group(Resources FILES ../bin/GameIndex.yaml gui/Resources/PCSX2.icns)
|
||||
source_group(Resources REGULAR_EXPRESSION ${CMAKE_CURRENT_BINARY_DIR}/*)
|
||||
|
||||
+4
-2
@@ -545,7 +545,7 @@ struct Pcsx2Config
|
||||
CdvdShareWrite : 1, // allows the iso to be modified while it's loaded
|
||||
EnablePatches : 1, // enables patch detection and application
|
||||
EnableCheats : 1, // enables cheat detection and application
|
||||
EnableIPC : 1, // enables inter-process communication
|
||||
EnablePINE : 1, // enables inter-process communication
|
||||
EnableWideScreenPatches : 1,
|
||||
#ifndef DISABLE_RECORDING
|
||||
EnableRecordingTools : 1,
|
||||
@@ -598,7 +598,7 @@ struct Pcsx2Config
|
||||
void LoadSaveMemcards(SettingsWrapper& wrap);
|
||||
|
||||
// TODO: Make these std::string when we remove wxFile...
|
||||
wxString FullpathToBios() const;
|
||||
std::string FullpathToBios() const;
|
||||
wxString FullpathToMcd(uint slot) const;
|
||||
|
||||
bool MultitapEnabled(uint port) const;
|
||||
@@ -627,6 +627,8 @@ namespace EmuFolders
|
||||
extern wxDirName Logs;
|
||||
extern wxDirName Cheats;
|
||||
extern wxDirName CheatsWS;
|
||||
extern wxDirName Resources;
|
||||
extern wxDirName Cache;
|
||||
} // namespace EmuFolders
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "GS.h"
|
||||
#include "VUmicro.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
|
||||
#include "ps2/HwInternal.h"
|
||||
#include "Sio.h"
|
||||
@@ -400,6 +401,8 @@ u32 UpdateVSyncRate()
|
||||
cpuRcntSet();
|
||||
}
|
||||
|
||||
PerformanceMetrics::SetVerticalFrequency(vertical_frequency);
|
||||
|
||||
if (m_iTicks != ticks)
|
||||
m_iTicks = ticks;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "AsyncFileReader.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
// The aio module has been reported to cause issues with FreeBSD 10.3, so let's
|
||||
// disable it for 10.3 and earlier and hope FreeBSD 11 and onwards is fine.
|
||||
@@ -36,11 +37,11 @@ FlatFileReader::~FlatFileReader(void)
|
||||
Close();
|
||||
}
|
||||
|
||||
bool FlatFileReader::Open(const wxString& fileName)
|
||||
bool FlatFileReader::Open(std::string fileName)
|
||||
{
|
||||
m_filename = fileName;
|
||||
m_filename = std::move(fileName);
|
||||
|
||||
m_fd = wxOpen(fileName, O_RDONLY, 0);
|
||||
m_fd = FileSystem::OpenFDFile(m_filename.c_str(), O_RDONLY, 0);
|
||||
|
||||
return (m_fd != -1);
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Common.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#include "GS.h" // for sending game crc to mtgs
|
||||
#include "Elfheader.h"
|
||||
@@ -152,7 +153,7 @@ void ElfObject::readIso(IsoFile& file)
|
||||
void ElfObject::readFile()
|
||||
{
|
||||
int rsize = 0;
|
||||
FILE *f = wxFopen( filename, "rb" );
|
||||
FILE *f = FileSystem::OpenCFile( filename.ToUTF8(), "rb" );
|
||||
if (f == NULL) throw Exception::FileNotFound( filename );
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
@@ -912,91 +912,6 @@ void fifo_free(void* ptr, size_t size, size_t repeat)
|
||||
|
||||
#endif
|
||||
|
||||
static void* s_hModule;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
bool GSApp::LoadResource(int id, std::vector<char>& buff, const wchar_t* type)
|
||||
{
|
||||
buff.clear();
|
||||
HRSRC hRsrc = FindResource((HMODULE)s_hModule, MAKEINTRESOURCE(id), type != NULL ? type : (LPWSTR)RT_RCDATA);
|
||||
if (!hRsrc)
|
||||
return false;
|
||||
HGLOBAL hGlobal = ::LoadResource((HMODULE)s_hModule, hRsrc);
|
||||
if (!hGlobal)
|
||||
return false;
|
||||
DWORD size = SizeofResource((HMODULE)s_hModule, hRsrc);
|
||||
if (!size)
|
||||
return false;
|
||||
// On Linux resources are always NULL terminated
|
||||
// Add + 1 on size to do the same for compatibility sake (required by GSDeviceOGL)
|
||||
buff.resize(size + 1);
|
||||
memcpy(buff.data(), LockResource(hGlobal), size);
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "GS_res.h"
|
||||
|
||||
bool GSApp::LoadResource(int id, std::vector<char>& buff, const char* type)
|
||||
{
|
||||
std::string path;
|
||||
switch (id)
|
||||
{
|
||||
case IDR_COMMON_GLSL:
|
||||
path = "/GS/res/glsl/common_header.glsl";
|
||||
break;
|
||||
case IDR_CONVERT_GLSL:
|
||||
path = "/GS/res/glsl/convert.glsl";
|
||||
break;
|
||||
case IDR_FXAA_FX:
|
||||
path = "/GS/res/fxaa.fx";
|
||||
break;
|
||||
case IDR_INTERLACE_GLSL:
|
||||
path = "/GS/res/glsl/interlace.glsl";
|
||||
break;
|
||||
case IDR_MERGE_GLSL:
|
||||
path = "/GS/res/glsl/merge.glsl";
|
||||
break;
|
||||
case IDR_SHADEBOOST_GLSL:
|
||||
path = "/GS/res/glsl/shadeboost.glsl";
|
||||
break;
|
||||
case IDR_TFX_VGS_GLSL:
|
||||
path = "/GS/res/glsl/tfx_vgs.glsl";
|
||||
break;
|
||||
case IDR_TFX_FS_GLSL:
|
||||
path = "/GS/res/glsl/tfx_fs.glsl";
|
||||
break;
|
||||
case IDR_FONT_ROBOTO:
|
||||
path = "/GS/res/fonts-roboto/Roboto-Regular.ttf";
|
||||
break;
|
||||
default:
|
||||
printf("LoadResource not implemented for id %d\n", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
GBytes* bytes = g_resource_lookup_data(GS_res_get_resource(), path.c_str(), G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
|
||||
|
||||
size_t size = 0;
|
||||
const void* data = g_bytes_get_data(bytes, &size);
|
||||
|
||||
if (data == nullptr || size == 0)
|
||||
{
|
||||
printf("Failed to get data for resource: %d\n", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
buff.clear();
|
||||
buff.resize(size + 1);
|
||||
memcpy(buff.data(), data, size + 1);
|
||||
|
||||
g_bytes_unref(bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t GSApp::GetIniString(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, size_t nSize, const char* lpFileName)
|
||||
{
|
||||
BuildConfigurationMap(lpFileName);
|
||||
@@ -1219,7 +1134,6 @@ void GSApp::Init()
|
||||
m_default_configuration["extrathreads"] = "2";
|
||||
m_default_configuration["extrathreads_height"] = "4";
|
||||
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
||||
m_default_configuration["force_texture_clear"] = "0";
|
||||
m_default_configuration["fxaa"] = "0";
|
||||
m_default_configuration["interlace"] = "7";
|
||||
m_default_configuration["conservative_framebuffer"] = "1";
|
||||
@@ -1361,11 +1275,6 @@ void GSApp::BuildConfigurationMap(const char* lpFileName)
|
||||
}
|
||||
}
|
||||
|
||||
void* GSApp::GetModuleHandlePtr()
|
||||
{
|
||||
return s_hModule;
|
||||
}
|
||||
|
||||
void GSApp::SetConfigDir()
|
||||
{
|
||||
// we need to initialize the ini folder later at runtime than at theApp init, as
|
||||
|
||||
@@ -149,14 +149,6 @@ public:
|
||||
GSApp();
|
||||
|
||||
void Init();
|
||||
void* GetModuleHandlePtr();
|
||||
|
||||
#ifdef _WIN32
|
||||
HMODULE GetModuleHandle()
|
||||
{
|
||||
return (HMODULE)GetModuleHandlePtr();
|
||||
}
|
||||
#endif
|
||||
|
||||
void BuildConfigurationMap(const char* lpFileName);
|
||||
void ReloadConfig();
|
||||
@@ -165,12 +157,6 @@ public:
|
||||
bool WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName);
|
||||
int GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName);
|
||||
|
||||
#ifdef _WIN32
|
||||
bool LoadResource(int id, std::vector<char>& buff, const wchar_t* type = nullptr);
|
||||
#else
|
||||
bool LoadResource(int id, std::vector<char>& buff, const char* type = nullptr);
|
||||
#endif
|
||||
|
||||
void SetConfig(const char* entry, const char* value);
|
||||
void SetConfig(const char* entry, int value);
|
||||
// Avoid issue with overloading
|
||||
|
||||
@@ -44,54 +44,9 @@ BEGIN
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""res/tfx.fx""\r\n"
|
||||
"#include ""res/convert.fx""\r\n"
|
||||
"#include ""res/interlace.fx""\r\n"
|
||||
"#include ""res/merge.fx""\r\n"
|
||||
"#include ""res/fxaa.fx""\r\n"
|
||||
"#include ""res/shadeboost.fx""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RCDATA
|
||||
//
|
||||
|
||||
IDR_TFX_FX RCDATA "res\\tfx.fx"
|
||||
|
||||
IDR_CONVERT_FX RCDATA "res\\convert.fx"
|
||||
|
||||
IDR_INTERLACE_FX RCDATA "res\\interlace.fx"
|
||||
|
||||
IDR_MERGE_FX RCDATA "res\\merge.fx"
|
||||
|
||||
IDR_FXAA_FX RCDATA "res\\fxaa.fx"
|
||||
|
||||
IDR_SHADEBOOST_FX RCDATA "res\\shadeboost.fx"
|
||||
|
||||
IDR_CONVERT_GLSL RCDATA "res\\glsl\\convert.glsl";
|
||||
|
||||
IDR_INTERLACE_GLSL RCDATA "res\\glsl\\interlace.glsl";
|
||||
|
||||
IDR_MERGE_GLSL RCDATA "res\\glsl\\merge.glsl";
|
||||
|
||||
IDR_SHADEBOOST_GLSL RCDATA "res\\glsl\\shadeboost.glsl";
|
||||
|
||||
IDR_COMMON_GLSL RCDATA "res\\glsl\\common_header.glsl";
|
||||
|
||||
IDR_TFX_VGS_GLSL RCDATA "res\\glsl\\tfx_vgs.glsl";
|
||||
|
||||
IDR_TFX_FS_GLSL RCDATA "res\\glsl\\tfx_fs.glsl";
|
||||
|
||||
IDR_FONT_ROBOTO RCDATA "res\\fonts-roboto\\Roboto-Regular.ttf";
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
@@ -139,18 +94,4 @@ END
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#include "res/tfx.fx"
|
||||
#include "res/convert.fx"
|
||||
#include "res/interlace.fx"
|
||||
#include "res/merge.fx"
|
||||
#include "res/fxaa.fx"
|
||||
#include "res/shadeboost.fx"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
// clang-format on
|
||||
|
||||
+4
-3
@@ -148,16 +148,17 @@ bool GSClut::WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
||||
__assume(0);
|
||||
}
|
||||
|
||||
return m_write.IsDirty(TEX0, TEXCLUT);
|
||||
// CLUT only reloads if PSM is a valid index type, avoid unnecessary flushes
|
||||
return ((TEX0.PSM & 0x7) >= 3) && m_write.IsDirty(TEX0, TEXCLUT);
|
||||
}
|
||||
|
||||
void GSClut::Write(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
||||
{
|
||||
m_write.TEX0 = TEX0;
|
||||
m_write.TEXCLUT = TEXCLUT;
|
||||
m_write.dirty = false;
|
||||
m_read.dirty = true;
|
||||
|
||||
m_write.dirty = false;
|
||||
|
||||
(this->*m_wc[TEX0.CSM][TEX0.CPSM][TEX0.PSM])(TEX0, TEXCLUT);
|
||||
}
|
||||
|
||||
|
||||
@@ -2546,8 +2546,13 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
__assume(0);
|
||||
}
|
||||
|
||||
if (auto_flush && PRIM->TME && (m_context->FRAME.Block() == m_context->TEX0.TBP0))
|
||||
FlushPrim();
|
||||
if (m_context->FRAME.FBMSK != 0xFFFFFFFF)
|
||||
{
|
||||
m_mem.m_clut.Invalidate(m_context->FRAME.Block());
|
||||
|
||||
if (auto_flush && PRIM->TME && (m_context->FRAME.Block() == m_context->TEX0.TBP0))
|
||||
FlushPrim();
|
||||
}
|
||||
}
|
||||
|
||||
void GSState::GetTextureMinMax(GSVector4i& r, const GIFRegTEX0& TEX0, const GIFRegCLAMP& CLAMP, bool linear)
|
||||
|
||||
@@ -66,9 +66,7 @@ GSDevice::GSDevice()
|
||||
|
||||
GSDevice::~GSDevice()
|
||||
{
|
||||
for (auto t : m_pool)
|
||||
delete t;
|
||||
|
||||
PurgePool();
|
||||
delete m_backbuffer;
|
||||
delete m_merge;
|
||||
delete m_weavebob;
|
||||
@@ -83,10 +81,7 @@ bool GSDevice::Create(const WindowInfo& wi)
|
||||
|
||||
bool GSDevice::Reset(int w, int h)
|
||||
{
|
||||
for (auto t : m_pool)
|
||||
delete t;
|
||||
|
||||
m_pool.clear();
|
||||
PurgePool();
|
||||
|
||||
delete m_backbuffer;
|
||||
delete m_merge;
|
||||
@@ -94,13 +89,13 @@ bool GSDevice::Reset(int w, int h)
|
||||
delete m_blend;
|
||||
delete m_target_tmp;
|
||||
|
||||
m_backbuffer = NULL;
|
||||
m_merge = NULL;
|
||||
m_weavebob = NULL;
|
||||
m_blend = NULL;
|
||||
m_target_tmp = NULL;
|
||||
m_backbuffer = nullptr;
|
||||
m_merge = nullptr;
|
||||
m_weavebob = nullptr;
|
||||
m_blend = nullptr;
|
||||
m_target_tmp = nullptr;
|
||||
|
||||
m_current = NULL; // current is special, points to other textures, no need to delete
|
||||
m_current = nullptr; // current is special, points to other textures, no need to delete
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -139,19 +134,44 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture:
|
||||
{
|
||||
const GSVector2i size(w, h);
|
||||
|
||||
GSTexture* t = nullptr;
|
||||
|
||||
for (auto i = m_pool.begin(); i != m_pool.end(); ++i)
|
||||
{
|
||||
GSTexture* t = *i;
|
||||
t = *i;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (t->GetType() == type && t->GetFormat() == format && t->GetSize() == size)
|
||||
{
|
||||
m_pool.erase(i);
|
||||
|
||||
return t;
|
||||
break;
|
||||
}
|
||||
|
||||
t = nullptr;
|
||||
}
|
||||
|
||||
return CreateSurface(type, w, h, format);
|
||||
if (!t)
|
||||
t = CreateSurface(type, w, h, format);
|
||||
|
||||
if (!t)
|
||||
throw std::bad_alloc();
|
||||
|
||||
t->Commit(); // Clear won't be done if the texture isn't committed.
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
ClearRenderTarget(t, 0);
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
ClearDepth(t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void GSDevice::PrintMemoryUsage()
|
||||
@@ -214,13 +234,9 @@ void GSDevice::AgePool()
|
||||
|
||||
void GSDevice::PurgePool()
|
||||
{
|
||||
// OOM emergency. Let's free this useless pool
|
||||
while (!m_pool.empty())
|
||||
{
|
||||
delete m_pool.back();
|
||||
|
||||
m_pool.pop_back();
|
||||
}
|
||||
for (auto t : m_pool)
|
||||
delete t;
|
||||
m_pool.clear();
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateSparseRenderTarget(int w, int h, GSTexture::Format format)
|
||||
@@ -261,7 +277,7 @@ GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type)
|
||||
return GSTexture::Format::Color;
|
||||
}
|
||||
|
||||
bool GSDevice::DownloadTextureConvert(GSTexture* src, const GSVector4& sRect, const GSVector2i& dSize, GSTexture::Format format, ShaderConvert ps_shader, GSTexture::GSMap& out_map)
|
||||
bool GSDevice::DownloadTextureConvert(GSTexture* src, const GSVector4& sRect, const GSVector2i& dSize, GSTexture::Format format, ShaderConvert ps_shader, GSTexture::GSMap& out_map, const bool linear)
|
||||
{
|
||||
ASSERT(src);
|
||||
ASSERT(format == GSTexture::Format::Color || format == GSTexture::Format::UInt16 || format == GSTexture::Format::UInt32);
|
||||
@@ -271,9 +287,9 @@ bool GSDevice::DownloadTextureConvert(GSTexture* src, const GSVector4& sRect, co
|
||||
return false;
|
||||
|
||||
GSVector4i dRect(0, 0, dSize.x, dSize.y);
|
||||
StretchRect(src, sRect, dst, GSVector4(dRect), ps_shader);
|
||||
StretchRect(src, sRect, dst, GSVector4(dRect), ps_shader, linear);
|
||||
|
||||
bool ret = DownloadTexture(src, dRect, out_map);
|
||||
bool ret = DownloadTexture(dst, dRect, out_map);
|
||||
Recycle(dst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ public:
|
||||
|
||||
/// Scale the region `sRect` of `src` to the size `dSize` using `ps_shader` and store the result in `out_map`
|
||||
/// `out_map` will be valid a call to `DownloadTextureComplete`
|
||||
virtual bool DownloadTextureConvert(GSTexture* src, const GSVector4& sRect, const GSVector2i& dSize, GSTexture::Format format, ShaderConvert ps_shader, GSTexture::GSMap& out_map);
|
||||
virtual bool DownloadTextureConvert(GSTexture* src, const GSVector4& sRect, const GSVector2i& dSize, GSTexture::Format format, ShaderConvert ps_shader, GSTexture::GSMap& out_map, bool linear);
|
||||
|
||||
/// Must be called to free resources after calling `DownloadTexture` or `DownloadTextureConvert`
|
||||
virtual void DownloadTextureComplete() {}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "GSOsdManager.h"
|
||||
#include "GS/GS.h"
|
||||
#ifdef _WIN32
|
||||
#include "GS/resource.h"
|
||||
#endif
|
||||
#include "Host.h"
|
||||
|
||||
#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 4)
|
||||
#include <codecvt>
|
||||
@@ -31,8 +29,12 @@ void GSOsdManager::LoadFont()
|
||||
if (error)
|
||||
{
|
||||
FT_Error error_load_res = 1;
|
||||
if (theApp.LoadResource(IDR_FONT_ROBOTO, resource_data_buffer))
|
||||
error_load_res = FT_New_Memory_Face(m_library, (const FT_Byte*)resource_data_buffer.data(), resource_data_buffer.size(), 0, &m_face);
|
||||
auto buffer = Host::ReadResourceFile("fonts/Roboto-Regular.ttf");
|
||||
if (buffer.has_value())
|
||||
{
|
||||
resource_data_buffer = std::move(*buffer);
|
||||
error_load_res = FT_New_Memory_Face(m_library, resource_data_buffer.data(), resource_data_buffer.size(), 0, &m_face);
|
||||
}
|
||||
|
||||
if (error_load_res)
|
||||
{
|
||||
|
||||
@@ -94,5 +94,5 @@ public:
|
||||
size_t GeneratePrimitives(GSVertexPT1* dst, size_t count);
|
||||
|
||||
private:
|
||||
std::vector<char> resource_data_buffer;
|
||||
std::vector<u8> resource_data_buffer;
|
||||
};
|
||||
|
||||
@@ -517,7 +517,7 @@ void GSRenderer::VSync(int field)
|
||||
if (size == current->GetSize())
|
||||
res = m_dev->DownloadTexture(current, GSVector4i(0, 0, size.x, size.y), m);
|
||||
else
|
||||
res = m_dev->DownloadTextureConvert(current, GSVector4(0, 0, 1, 1), size, GSTexture::Format::Color, ShaderConvert::COPY, m);
|
||||
res = m_dev->DownloadTextureConvert(current, GSVector4(0, 0, 1, 1), size, GSTexture::Format::Color, ShaderConvert::COPY, m, true);
|
||||
|
||||
if (res)
|
||||
{
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
virtual bool CanUpscale() { return false; }
|
||||
virtual int GetUpscaleMultiplier() { return 1; }
|
||||
virtual GSVector2i GetCustomResolution() { return GSVector2i(0, 0); }
|
||||
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
||||
GSVector2i GetInternalResolution();
|
||||
void SetVSync(int vsync);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "GS/Renderers/DX11/D3D.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "GS/resource.h"
|
||||
#include "Host.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <VersionHelpers.h>
|
||||
@@ -218,6 +218,11 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
m_hack_topleft_offset = (m_upscale_multiplier != 1 && D3D::IsNvidia(adapter.get()) && !disable_safe_features) ? -0.01f : 0.0f;
|
||||
}
|
||||
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/dx11/tfx.fx");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
m_tfx_source = std::move(*shader);
|
||||
|
||||
// convert
|
||||
|
||||
D3D11_INPUT_ELEMENT_DESC il_convert[] =
|
||||
@@ -229,9 +234,10 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
|
||||
ShaderMacro sm_model(m_shader.model);
|
||||
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_CONVERT_FX, shader);
|
||||
CreateShader(shader, "convert.fx", nullptr, "vs_main", sm_model.GetPtr(), &m_convert.vs, il_convert, std::size(il_convert), m_convert.il.put());
|
||||
shader = Host::ReadResourceFileToString("shaders/dx11/convert.fx");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
CreateShader(*shader, "convert.fx", nullptr, "vs_main", sm_model.GetPtr(), &m_convert.vs, il_convert, std::size(il_convert), m_convert.il.put());
|
||||
|
||||
ShaderMacro sm_convert(m_shader.model);
|
||||
sm_convert.AddMacro("PS_SCALE_FACTOR", std::max(1, m_upscale_multiplier));
|
||||
@@ -240,7 +246,7 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
|
||||
for (size_t i = 0; i < std::size(m_convert.ps); i++)
|
||||
{
|
||||
CreateShader(shader, "convert.fx", nullptr, shaderName(static_cast<ShaderConvert>(i)), sm_convert_ptr, m_convert.ps[i].put());
|
||||
CreateShader(*shader, "convert.fx", nullptr, shaderName(static_cast<ShaderConvert>(i)), sm_convert_ptr, m_convert.ps[i].put());
|
||||
}
|
||||
|
||||
memset(&dsd, 0, sizeof(dsd));
|
||||
@@ -269,10 +275,13 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
|
||||
m_dev->CreateBuffer(&bd, nullptr, m_merge.cb.put());
|
||||
|
||||
theApp.LoadResource(IDR_MERGE_FX, shader);
|
||||
shader = Host::ReadResourceFileToString("shaders/dx11/merge.fx");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < std::size(m_merge.ps); i++)
|
||||
{
|
||||
CreateShader(shader, "merge.fx", nullptr, format("ps_main%d", i).c_str(), sm_model.GetPtr(), m_merge.ps[i].put());
|
||||
CreateShader(*shader, "merge.fx", nullptr, format("ps_main%d", i).c_str(), sm_model.GetPtr(), m_merge.ps[i].put());
|
||||
}
|
||||
|
||||
memset(&bsd, 0, sizeof(bsd));
|
||||
@@ -298,10 +307,12 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
|
||||
m_dev->CreateBuffer(&bd, nullptr, m_interlace.cb.put());
|
||||
|
||||
theApp.LoadResource(IDR_INTERLACE_FX, shader);
|
||||
shader = Host::ReadResourceFileToString("shaders/dx11/interlace.fx");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
for (size_t i = 0; i < std::size(m_interlace.ps); i++)
|
||||
{
|
||||
CreateShader(shader, "interlace.fx", nullptr, format("ps_main%d", i).c_str(), sm_model.GetPtr(), m_interlace.ps[i].put());
|
||||
CreateShader(*shader, "interlace.fx", nullptr, format("ps_main%d", i).c_str(), sm_model.GetPtr(), m_interlace.ps[i].put());
|
||||
}
|
||||
|
||||
// Shade Boost
|
||||
@@ -320,8 +331,10 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||
|
||||
m_dev->CreateBuffer(&bd, nullptr, m_shadeboost.cb.put());
|
||||
|
||||
theApp.LoadResource(IDR_SHADEBOOST_FX, shader);
|
||||
CreateShader(shader, "shadeboost.fx", nullptr, "ps_main", sm_sboost.GetPtr(), m_shadeboost.ps.put());
|
||||
shader = Host::ReadResourceFileToString("shaders/dx11/shadeboost.fx");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
CreateShader(*shader, "shadeboost.fx", nullptr, "ps_main", sm_sboost.GetPtr(), m_shadeboost.ps.put());
|
||||
|
||||
// External fx shader
|
||||
|
||||
@@ -616,7 +629,7 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int w, int h, GSTextu
|
||||
break;
|
||||
}
|
||||
|
||||
GSTexture11* t = NULL;
|
||||
GSTexture11* t = nullptr;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11Texture2D> texture;
|
||||
HRESULT hr = m_dev->CreateTexture2D(&desc, nullptr, texture.put());
|
||||
@@ -624,16 +637,7 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int w, int h, GSTextu
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
t = new GSTexture11(std::move(texture), format);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
ClearRenderTarget(t, 0);
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
ClearDepth(t);
|
||||
break;
|
||||
}
|
||||
assert(type == t->GetType());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -643,11 +647,6 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int w, int h, GSTextu
|
||||
return t;
|
||||
}
|
||||
|
||||
GSTexture* GSDevice11::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
||||
{
|
||||
return __super::FetchSurface(type, w, h, format);
|
||||
}
|
||||
|
||||
bool GSDevice11::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map)
|
||||
{
|
||||
ASSERT(src);
|
||||
@@ -655,7 +654,7 @@ bool GSDevice11::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTextu
|
||||
m_download_tex.reset(static_cast<GSTexture11*>(CreateOffscreen(rect.width(), rect.height(), src->GetFormat())));
|
||||
if (!m_download_tex)
|
||||
return false;
|
||||
m_ctx->CopyResource(*m_download_tex, *static_cast<GSTexture11*>(src));
|
||||
CopyRect(src, m_download_tex.get(), rect);
|
||||
return m_download_tex->Map(out_map);
|
||||
}
|
||||
|
||||
@@ -925,9 +924,8 @@ void GSDevice11::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
|
||||
|
||||
shader << fshader.rdbuf();
|
||||
const std::string& s = shader.str();
|
||||
std::vector<char> buff(s.begin(), s.end());
|
||||
ShaderMacro sm(m_shader.model);
|
||||
CreateShader(buff, shader_name.c_str(), D3D_COMPILE_STANDARD_FILE_INCLUDE, "ps_main", sm.GetPtr(), m_shaderfx.ps.put());
|
||||
CreateShader(s, shader_name.c_str(), D3D_COMPILE_STANDARD_FILE_INCLUDE, "ps_main", sm.GetPtr(), m_shaderfx.ps.put());
|
||||
}
|
||||
catch (GSRecoverableError)
|
||||
{
|
||||
@@ -957,10 +955,12 @@ void GSDevice11::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_FXAA_FX, shader);
|
||||
ShaderMacro sm(m_shader.model);
|
||||
CreateShader(shader, "fxaa.fx", nullptr, "ps_main", sm.GetPtr(), m_fxaa.ps.put());
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/common/fxaa.fx");
|
||||
if (shader.has_value())
|
||||
{
|
||||
ShaderMacro sm(m_shader.model);
|
||||
CreateShader(*shader, "fxaa.fx", nullptr, "ps_main", sm.GetPtr(), m_fxaa.ps.put());
|
||||
}
|
||||
}
|
||||
catch (GSRecoverableError)
|
||||
{
|
||||
@@ -1399,7 +1399,7 @@ D3D_SHADER_MACRO* GSDevice11::ShaderMacro::GetPtr(void)
|
||||
return (D3D_SHADER_MACRO*)mout.data();
|
||||
}
|
||||
|
||||
void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il)
|
||||
void GSDevice11::CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
@@ -1422,7 +1422,7 @@ void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, I
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11GeometryShader** gs)
|
||||
void GSDevice11::CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11GeometryShader** gs)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> shader;
|
||||
|
||||
@@ -1436,7 +1436,7 @@ void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, I
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11PixelShader** ps)
|
||||
void GSDevice11::CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11PixelShader** ps)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> shader;
|
||||
|
||||
@@ -1450,7 +1450,7 @@ void GSDevice11::CreateShader(const std::vector<char>& source, const char* fn, I
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::CompileShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3DBlob** shader, const std::string& shader_model)
|
||||
void GSDevice11::CompileShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3DBlob** shader, const std::string& shader_model)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> error;
|
||||
|
||||
@@ -1461,7 +1461,7 @@ void GSDevice11::CompileShader(const std::vector<char>& source, const char* fn,
|
||||
#endif
|
||||
|
||||
const HRESULT hr = D3DCompile(
|
||||
source.data(), source.size(), fn, macro,
|
||||
source.c_str(), source.size(), fn, macro,
|
||||
include, entry, shader_model.c_str(),
|
||||
flags, 0, shader, error.put());
|
||||
|
||||
|
||||
@@ -397,7 +397,6 @@ private:
|
||||
int m_d3d_texsize;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
||||
GSTexture* FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
|
||||
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final;
|
||||
@@ -515,6 +514,8 @@ private:
|
||||
std::unique_ptr<GSTexture> m_font;
|
||||
std::unique_ptr<GSTexture11> m_download_tex;
|
||||
|
||||
std::string m_tfx_source;
|
||||
|
||||
protected:
|
||||
struct
|
||||
{
|
||||
@@ -590,9 +591,9 @@ public:
|
||||
operator ID3D11Device*() { return m_dev.get(); }
|
||||
operator ID3D11DeviceContext*() { return m_ctx.get(); }
|
||||
|
||||
void CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il);
|
||||
void CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11GeometryShader** gs);
|
||||
void CreateShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11PixelShader** ps);
|
||||
void CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il);
|
||||
void CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11GeometryShader** gs);
|
||||
void CreateShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3D11PixelShader** ps);
|
||||
|
||||
void CompileShader(const std::vector<char>& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3DBlob** shader, const std::string& shader_model);
|
||||
void CompileShader(const std::string& source, const char* fn, ID3DInclude* include, const char* entry, D3D_SHADER_MACRO* macro, ID3DBlob** shader, const std::string& shader_model);
|
||||
};
|
||||
|
||||
@@ -168,7 +168,7 @@ bool GSTexture11::Save(const std::string& fn)
|
||||
|
||||
res->GetDesc(&desc);
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
GSPng::Format format = GSPng::RGB_A_PNG;
|
||||
#else
|
||||
GSPng::Format format = GSPng::RGB_PNG;
|
||||
|
||||
@@ -114,10 +114,7 @@ void GSDevice11::SetupVS(VSSelector sel, const VSConstantBuffer* cb)
|
||||
};
|
||||
|
||||
GSVertexShader11 vs;
|
||||
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_TFX_FX, shader);
|
||||
CreateShader(shader, "tfx.fx", nullptr, "vs_main", sm.GetPtr(), &vs.vs, layout, std::size(layout), vs.il.put());
|
||||
CreateShader(m_tfx_source, "tfx.fx", nullptr, "vs_main", sm.GetPtr(), &vs.vs, layout, std::size(layout), vs.il.put());
|
||||
|
||||
m_vs[sel] = vs;
|
||||
|
||||
@@ -157,9 +154,7 @@ void GSDevice11::SetupGS(GSSelector sel, const GSConstantBuffer* cb)
|
||||
sm.AddMacro("GS_POINT", sel.point);
|
||||
sm.AddMacro("GS_LINE", sel.line);
|
||||
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_TFX_FX, shader);
|
||||
CreateShader(shader, "tfx.fx", nullptr, "gs_main", sm.GetPtr(), gs.put());
|
||||
CreateShader(m_tfx_source, "tfx.fx", nullptr, "gs_main", sm.GetPtr(), gs.put());
|
||||
|
||||
m_gs[sel] = gs;
|
||||
}
|
||||
@@ -218,10 +213,7 @@ void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSe
|
||||
sm.AddMacro("PS_ZCLAMP", sel.zclamp);
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps;
|
||||
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_TFX_FX, shader);
|
||||
CreateShader(shader, "tfx.fx", nullptr, "ps_main", sm.GetPtr(), ps.put());
|
||||
CreateShader(m_tfx_source, "tfx.fx", nullptr, "ps_main", sm.GetPtr(), ps.put());
|
||||
|
||||
i = m_ps.try_emplace(sel, std::move(ps)).first;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include "GSRendererHW.h"
|
||||
#include "GS/GSGL.h"
|
||||
|
||||
const float GSRendererHW::SSR_UV_TOLERANCE = 1e-3f;
|
||||
|
||||
GSRendererHW::GSRendererHW()
|
||||
: m_width(default_rt_size.x)
|
||||
, m_height(default_rt_size.y)
|
||||
@@ -775,6 +773,29 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
|
||||
}
|
||||
}
|
||||
|
||||
GSVector2 GSRendererHW::GetTextureScaleFactor()
|
||||
{
|
||||
GSVector2 scale_factor{ 1.0f, 1.0f };
|
||||
if (CanUpscale())
|
||||
{
|
||||
const int multiplier = GetUpscaleMultiplier();
|
||||
if (multiplier == 0)
|
||||
{
|
||||
// Custom resolution.
|
||||
const GSVector4i display_rect = GetDisplayRect();
|
||||
const GSVector2i requested_resolution = GetCustomResolution();
|
||||
scale_factor.x = static_cast<float>(requested_resolution.x) / display_rect.width();
|
||||
scale_factor.y = static_cast<float>(requested_resolution.y) / display_rect.height();
|
||||
}
|
||||
else
|
||||
{
|
||||
scale_factor.x = multiplier;
|
||||
scale_factor.y = multiplier;
|
||||
}
|
||||
}
|
||||
return scale_factor;
|
||||
}
|
||||
|
||||
void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r)
|
||||
{
|
||||
// printf("[%d] InvalidateVideoMem %d,%d - %d,%d %05x (%d)\n", (int)m_perfmon.GetFrame(), r.left, r.top, r.right, r.bottom, (int)BITBLTBUF.DBP, (int)BITBLTBUF.DPSM);
|
||||
@@ -840,36 +861,32 @@ void GSRendererHW::SwSpriteRender()
|
||||
|
||||
const bool texture_mapping_enabled = PRIM->TME;
|
||||
|
||||
// Setup registers for SW rendering
|
||||
GIFRegBITBLTBUF bitbltbuf = {};
|
||||
const GSVector4i r = m_r;
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
{
|
||||
bitbltbuf.SBP = m_context->TEX0.TBP0;
|
||||
bitbltbuf.SBW = m_context->TEX0.TBW;
|
||||
bitbltbuf.SPSM = m_context->TEX0.PSM;
|
||||
}
|
||||
|
||||
bitbltbuf.DBP = m_context->FRAME.Block();
|
||||
bitbltbuf.DBW = m_context->FRAME.FBW;
|
||||
bitbltbuf.DPSM = m_context->FRAME.PSM;
|
||||
|
||||
ASSERT(m_r.x == 0 && m_r.y == 0); // No rendering region offset
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_min.t.x) <= SSR_UV_TOLERANCE && abs(m_vt.m_min.t.y) <= SSR_UV_TOLERANCE)); // No input texture offset, if any
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_max.t.x - m_r.z) <= SSR_UV_TOLERANCE && abs(m_vt.m_max.t.y - m_r.w) <= SSR_UV_TOLERANCE)); // No input texture min/mag, if any
|
||||
ASSERT(!PRIM->TME || (m_vt.m_max.t.x <= (1 << m_context->TEX0.TW) && m_vt.m_max.t.y <= (1 << m_context->TEX0.TH))); // No texture UV wrap, if any
|
||||
#ifndef NDEBUG
|
||||
const int tw = 1 << m_context->TEX0.TW;
|
||||
const int th = 1 << m_context->TEX0.TH;
|
||||
const float meas_tw = m_vt.m_max.t.x - m_vt.m_min.t.x;
|
||||
const float meas_th = m_vt.m_max.t.y - m_vt.m_min.t.y;
|
||||
ASSERT(!PRIM->TME || (abs(meas_tw - r.width()) <= SSR_UV_TOLERANCE && abs(meas_th - r.height()) <= SSR_UV_TOLERANCE)); // No input texture min/mag, if any.
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_min.t.x) <= SSR_UV_TOLERANCE && abs(m_vt.m_min.t.y) <= SSR_UV_TOLERANCE && abs(meas_tw - tw) <= SSR_UV_TOLERANCE && abs(meas_th - th) <= SSR_UV_TOLERANCE)); // No texture UV wrap, if any.
|
||||
#endif
|
||||
|
||||
GIFRegTRXPOS trxpos = {};
|
||||
|
||||
trxpos.DSAX = 0;
|
||||
trxpos.DSAY = 0;
|
||||
trxpos.SSAX = 0;
|
||||
trxpos.SSAY = 0;
|
||||
trxpos.DSAX = r.x;
|
||||
trxpos.DSAY = r.y;
|
||||
trxpos.SSAX = static_cast<int>(m_vt.m_min.t.x / 2) * 2; // Rounded down to closest even integer.
|
||||
trxpos.SSAY = static_cast<int>(m_vt.m_min.t.y / 2) * 2;
|
||||
|
||||
ASSERT(r.x % 2 == 0 && r.y % 2 == 0);
|
||||
|
||||
GIFRegTRXREG trxreg = {};
|
||||
|
||||
trxreg.RRW = m_r.z;
|
||||
trxreg.RRH = m_r.w;
|
||||
trxreg.RRW = r.width();
|
||||
trxreg.RRH = r.height();
|
||||
|
||||
ASSERT(r.width() % 2 == 0 && r.height() % 2 == 0);
|
||||
|
||||
// SW rendering code, mainly taken from GSState::Move(), TRXPOS.DIR{X,Y} management excluded
|
||||
|
||||
@@ -880,20 +897,16 @@ void GSRendererHW::SwSpriteRender()
|
||||
const int w = trxreg.RRW;
|
||||
const int h = trxreg.RRH;
|
||||
|
||||
GL_INS("SwSpriteRender: Dest 0x%x W:%d F:%s, size(%d %d)", bitbltbuf.DBP, bitbltbuf.DBW, psm_str(bitbltbuf.DPSM), w, h);
|
||||
GL_INS("SwSpriteRender: Dest 0x%x W:%d F:%s, size(%d %d)", m_context->FRAME.Block(), m_context->FRAME.FBW, psm_str(m_context->FRAME.PSM), w, h);
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
InvalidateLocalMem(bitbltbuf, GSVector4i(sx, sy, sx + w, sy + h));
|
||||
InvalidateVideoMem(bitbltbuf, GSVector4i(dx, dy, dx + w, dy + h));
|
||||
|
||||
GSOffset spo = texture_mapping_enabled ? m_mem.GetOffset(bitbltbuf.SBP, bitbltbuf.SBW, bitbltbuf.SPSM) : GSOffset();
|
||||
GSOffset dpo = m_mem.GetOffset(bitbltbuf.DBP, bitbltbuf.DBW, bitbltbuf.DPSM);
|
||||
const GSOffset& spo = m_context->offset.tex;
|
||||
const GSOffset& dpo = m_context->offset.fb;
|
||||
|
||||
const bool alpha_blending_enabled = PRIM->ABE;
|
||||
|
||||
const GSVertex& v = m_vertex.buff[m_index.buff[m_index.tail - 1]]; // Last vertex.
|
||||
const GSVector4i vc = GSVector4i(v.RGBAQ.R, v.RGBAQ.G, v.RGBAQ.B, v.RGBAQ.A) // 0x000000AA000000BB000000GG000000RR
|
||||
.ps32(); // 0x00AA00BB00GG00RR00AA00BB00GG00RR
|
||||
.ps32(); // 0x00AA00BB00GG00RR00AA00BB00GG00RR
|
||||
|
||||
const GSVector4i a_mask = GSVector4i::xff000000().u8to16(); // 0x00FF00000000000000FF000000000000
|
||||
|
||||
@@ -902,28 +915,36 @@ void GSRendererHW::SwSpriteRender()
|
||||
|
||||
const u8 tex0_tfx = m_context->TEX0.TFX;
|
||||
const u8 tex0_tcc = m_context->TEX0.TCC;
|
||||
const u8 alpha_a = m_context->ALPHA.A;
|
||||
const u8 alpha_b = m_context->ALPHA.B;
|
||||
const u8 alpha_c = m_context->ALPHA.C;
|
||||
const u8 alpha_d = m_context->ALPHA.D;
|
||||
const u8 alpha_fix = m_context->ALPHA.FIX;
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
m_tc->InvalidateLocalMem(spo, GSVector4i(sx, sy, sx + w, sy + h));
|
||||
constexpr bool invalidate_local_mem_before_fb_read = false;
|
||||
if (invalidate_local_mem_before_fb_read && (alpha_blending_enabled || fb_mask_enabled))
|
||||
m_tc->InvalidateLocalMem(dpo, m_r);
|
||||
|
||||
for (int y = 0; y < h; y++, ++sy, ++dy)
|
||||
{
|
||||
auto spa = texture_mapping_enabled ? spo.paMulti(m_mem.m_vm32, sx, sy) : GSOffset::PAPtrHelper<u32>();
|
||||
auto dpa = dpo.paMulti(m_mem.m_vm32, dx, dy);
|
||||
const auto& spa = spo.paMulti(m_mem.m_vm32, sx, sy);
|
||||
const auto& dpa = dpo.paMulti(m_mem.m_vm32, dx, dy);
|
||||
|
||||
ASSERT(w % 2 == 0);
|
||||
|
||||
for (int x = 0; x < w; x += 2)
|
||||
{
|
||||
u32* di = dpa.value(x);
|
||||
ASSERT(*di + 1 == *dpa.value(x + 1)); // Destination pixel pair is adjacent in memory
|
||||
ASSERT(di + 1 == dpa.value(x + 1)); // Destination pixel pair is adjacent in memory
|
||||
|
||||
GSVector4i sc;
|
||||
if (texture_mapping_enabled)
|
||||
{
|
||||
u32* si = spa.value(x);
|
||||
const u32* si = spa.value(x);
|
||||
// Read 2 source pixel colors
|
||||
ASSERT((*si + 1) == *spa.value(x + 1)); // Source pixel pair is adjacent in memory
|
||||
ASSERT(si + 1 == spa.value(x + 1)); // Source pixel pair is adjacent in memory
|
||||
sc = GSVector4i::loadl(si).u8to16(); // 0x00AA00BB00GG00RR00aa00bb00gg00rr
|
||||
|
||||
// Apply TFX
|
||||
@@ -951,31 +972,14 @@ void GSRendererHW::SwSpriteRender()
|
||||
if (alpha_blending_enabled)
|
||||
{
|
||||
// Blending
|
||||
ASSERT(m_context->ALPHA.A == 0);
|
||||
ASSERT(alpha_b == 1 || alpha_b == 2);
|
||||
ASSERT(m_context->ALPHA.D == 1);
|
||||
|
||||
// Flag C
|
||||
GSVector4i sc_alpha_vec;
|
||||
|
||||
if (alpha_c == 2)
|
||||
sc_alpha_vec = GSVector4i(alpha_fix).xxxx().ps32();
|
||||
else
|
||||
sc_alpha_vec = (alpha_c == 0 ? sc : dc0)
|
||||
.yyww() // 0x00AA00BB00AA00BB00aa00bb00aa00bb
|
||||
.srl32(16) // 0x000000AA000000AA000000aa000000aa
|
||||
.ps32() // 0x00AA00AA00aa00aa00AA00AA00aa00aa
|
||||
.xxyy(); // 0x00AA00AA00AA00AA00aa00aa00aa00aa
|
||||
|
||||
switch (alpha_b)
|
||||
{
|
||||
case 1:
|
||||
dc = sc.sub16(dc0).mul16l(sc_alpha_vec).sra16(7).add16(dc0); // (((Cs - Cd) * C) >> 7) + Cd, must use sra16 due to signed 16 bit values
|
||||
break;
|
||||
default:
|
||||
dc = sc.mul16l(sc_alpha_vec).sra16(7).add16(dc0); // (((Cs - 0) * C) >> 7) + Cd, must use sra16 due to signed 16 bit values
|
||||
break;
|
||||
}
|
||||
const GSVector4i A = alpha_a == 0 ? sc : alpha_a == 1 ? dc0 : GSVector4i::zero();
|
||||
const GSVector4i B = alpha_b == 0 ? sc : alpha_b == 1 ? dc0 : GSVector4i::zero();
|
||||
const GSVector4i C = alpha_c == 2 ? GSVector4i(alpha_fix).xxxx().ps32() : (alpha_c == 0 ? sc : dc0).yyww() // 0x00AA00BB00AA00BB00aa00bb00aa00bb
|
||||
.srl32(16) // 0x000000AA000000AA000000aa000000aa
|
||||
.ps32() // 0x00AA00AA00aa00aa00AA00AA00aa00aa
|
||||
.xxyy(); // 0x00AA00AA00AA00AA00aa00aa00aa00aa
|
||||
const GSVector4i D = alpha_d == 0 ? sc : alpha_d == 1 ? dc0 : GSVector4i::zero();
|
||||
dc = A.sub16(B).mul16l(C).sra16(7).add16(D); // (((A - B) * C) >> 7) + D, must use sra16 due to signed 16 bit values.
|
||||
// dc alpha channels (dc.u16[3], dc.u16[7]) dirty
|
||||
}
|
||||
else
|
||||
@@ -1003,16 +1007,21 @@ void GSRendererHW::SwSpriteRender()
|
||||
GSVector4i::storel(di, dc);
|
||||
}
|
||||
}
|
||||
|
||||
m_tc->InvalidateVideoMem(dpo, m_r);
|
||||
}
|
||||
|
||||
bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
||||
bool GSRendererHW::CanUseSwSpriteRender()
|
||||
{
|
||||
const bool r_0_0_64_64 = allow_64x64_sprite ? (m_r == GSVector4i(0, 0, 64, 64)).alltrue() : false;
|
||||
if (r_0_0_64_64 && !allow_64x64_sprite) // Rendering region 64x64 support is enabled via parameter
|
||||
return false;
|
||||
const bool r_0_0_16_16 = (m_r == GSVector4i(0, 0, 16, 16)).alltrue();
|
||||
if (!r_0_0_16_16 && !r_0_0_64_64) // Rendering region is 16x16 or 64x64, without offset
|
||||
return false;
|
||||
const GSVector4i r = m_r;
|
||||
if (r.x % 2 != 0 || r.y % 2 != 0)
|
||||
return false; // Even offset.
|
||||
const int w = r.width();
|
||||
const int h = r.height();
|
||||
if (w % 2 != 0 || h % 2 != 0)
|
||||
return false; // Even size.
|
||||
if (w > 64 || h > 64)
|
||||
return false; // Small draw.
|
||||
if (PRIM->PRIM != GS_SPRITE
|
||||
&& ((PRIM->IIP && m_vt.m_eq.rgba != 0xffff)
|
||||
|| (PRIM->TME && !PRIM->FST && m_vt.m_eq.q != 0x1)
|
||||
@@ -1020,7 +1029,7 @@ bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
||||
return false;
|
||||
if (m_vt.m_primclass != GS_TRIANGLE_CLASS && m_vt.m_primclass != GS_SPRITE_CLASS) // Triangle or sprite class prims
|
||||
return false;
|
||||
if (PRIM->PRIM != GS_TRIANGLESTRIP && PRIM->PRIM != GS_SPRITE) // Triangle strip or sprite draw
|
||||
if (PRIM->PRIM != GS_TRIANGLESTRIP && PRIM->PRIM != GS_SPRITE) // Triangle strip or sprite draw
|
||||
return false;
|
||||
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && (PRIM->PRIM != GS_TRIANGLESTRIP || m_vertex.tail != 4)) // If triangle class, strip draw with 4 vertices (two prims, emulating single sprite prim)
|
||||
return false;
|
||||
@@ -1037,15 +1046,18 @@ bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
||||
|
||||
if (m_context->TEX0.PSM != PSM_PSMCT32) // Input texture format is 32 bit color
|
||||
return false;
|
||||
if (IsMipMapDraw()) // No mipmapping
|
||||
return false;
|
||||
if (abs(m_vt.m_min.t.x) > SSR_UV_TOLERANCE || abs(m_vt.m_min.t.y) > SSR_UV_TOLERANCE) // No horizontal nor vertical offset
|
||||
return false;
|
||||
if (abs(m_vt.m_max.t.x - m_r.z) > SSR_UV_TOLERANCE || abs(m_vt.m_max.t.y - m_r.w) > SSR_UV_TOLERANCE) // No texture width or height mag/min
|
||||
if (IsMipMapDraw()) // No mipmapping.
|
||||
return false;
|
||||
const int tw = 1 << m_context->TEX0.TW;
|
||||
const int th = 1 << m_context->TEX0.TH;
|
||||
if (m_vt.m_max.t.x > tw || m_vt.m_max.t.y > th) // No UV wrapping
|
||||
const float meas_tw = m_vt.m_max.t.x - m_vt.m_min.t.x;
|
||||
const float meas_th = m_vt.m_max.t.y - m_vt.m_min.t.y;
|
||||
if (abs(m_vt.m_min.t.x) > SSR_UV_TOLERANCE ||
|
||||
abs(m_vt.m_min.t.y) > SSR_UV_TOLERANCE ||
|
||||
abs(meas_tw - tw) > SSR_UV_TOLERANCE ||
|
||||
abs(meas_th - th) > SSR_UV_TOLERANCE) // No UV wrapping.
|
||||
return false;
|
||||
if (abs(meas_tw - w) > SSR_UV_TOLERANCE || abs(meas_th - h) > SSR_UV_TOLERANCE) // No texture width or height mag/min.
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1272,8 +1284,8 @@ void GSRendererHW::Draw()
|
||||
TEX0.TBW = context->FRAME.FBW;
|
||||
TEX0.PSM = context->FRAME.PSM;
|
||||
|
||||
GSTextureCache::Target* rt = NULL;
|
||||
GSTexture* rt_tex = NULL;
|
||||
GSTextureCache::Target* rt = nullptr;
|
||||
GSTexture* rt_tex = nullptr;
|
||||
if (!no_rt)
|
||||
{
|
||||
rt = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::RenderTarget, true, fm);
|
||||
@@ -1284,8 +1296,8 @@ void GSRendererHW::Draw()
|
||||
TEX0.TBW = context->FRAME.FBW;
|
||||
TEX0.PSM = context->ZBUF.PSM;
|
||||
|
||||
GSTextureCache::Target* ds = NULL;
|
||||
GSTexture* ds_tex = NULL;
|
||||
GSTextureCache::Target* ds = nullptr;
|
||||
GSTexture* ds_tex = nullptr;
|
||||
if (!no_ds)
|
||||
{
|
||||
ds = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::DepthStencil, context->DepthWrite());
|
||||
@@ -1468,6 +1480,41 @@ void GSRendererHW::Draw()
|
||||
rt->m_32_bits_fmt = m_texture_shuffle || (GSLocalMemory::m_psm[context->FRAME.PSM].bpp != 16);
|
||||
}
|
||||
|
||||
// The rectangle of the draw
|
||||
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
|
||||
{
|
||||
const GSVector2 up_s = GetTextureScaleFactor();
|
||||
const int up_w = static_cast<int>(std::ceil(static_cast<float>(m_r.z) * up_s.x));
|
||||
const int up_h = static_cast<int>(std::ceil(static_cast<float>(m_r.w) * up_s.y));
|
||||
const int new_w = std::max(up_w, std::max(rt_tex ? rt_tex->GetWidth() : 0, ds_tex ? ds_tex->GetWidth() : 0));
|
||||
const int new_h = std::max(up_h, std::max(rt_tex ? rt_tex->GetHeight() : 0, ds_tex ? ds_tex->GetHeight() : 0));
|
||||
std::array<GSTextureCache::Target*, 2> ts{ rt, ds };
|
||||
for (GSTextureCache::Target* t : ts)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
// Adjust texture size to fit current draw if necessary.
|
||||
GSTexture* tex = t->m_texture;
|
||||
assert(up_s == tex->GetScale());
|
||||
const int w = tex->GetWidth();
|
||||
const int h = tex->GetHeight();
|
||||
if (w != new_w || h != new_h)
|
||||
{
|
||||
const bool is_rt = t == rt;
|
||||
t->m_texture = is_rt ?
|
||||
m_dev->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
||||
m_dev->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
||||
const GSVector4i r{ 0, 0, w, h };
|
||||
m_dev->CopyRect(tex, t->m_texture, r);
|
||||
m_dev->Recycle(tex);
|
||||
t->m_texture->SetScale(up_s);
|
||||
(is_rt ? rt_tex : ds_tex) = t->m_texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s_dump)
|
||||
{
|
||||
const u64 frame = m_perfmon.GetFrame();
|
||||
@@ -1505,8 +1552,8 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
s = format("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
|
||||
|
||||
if (rt)
|
||||
rt->m_texture->Save(m_dump_root + s);
|
||||
if (rt_tex)
|
||||
rt_tex->Save(m_dump_root + s);
|
||||
}
|
||||
|
||||
if (s_savez && s_n >= s_saven)
|
||||
@@ -1518,9 +1565,6 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
|
||||
// The rectangle of the draw
|
||||
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
|
||||
if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt_tex, ds_tex, m_src))
|
||||
{
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
@@ -1553,7 +1597,7 @@ void GSRendererHW::Draw()
|
||||
// A couple of hack to avoid upscaling issue. So far it seems to impacts mostly sprite
|
||||
// Note: first hack corrects both position and texture coordinate
|
||||
// Note: second hack corrects only the texture coordinate
|
||||
if ((m_upscale_multiplier > 1) && (m_vt.m_primclass == GS_SPRITE_CLASS))
|
||||
if (CanUpscale() && (m_vt.m_primclass == GS_SPRITE_CLASS))
|
||||
{
|
||||
const size_t count = m_vertex.next;
|
||||
GSVertex* v = &m_vertex.buff[0];
|
||||
@@ -1611,18 +1655,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
//
|
||||
|
||||
// Help to detect rendering outside of the framebuffer
|
||||
#if _DEBUG
|
||||
if (m_upscale_multiplier * m_r.z > m_width)
|
||||
{
|
||||
GL_INS("ERROR: RT width is too small only %d but require %d", m_width, m_upscale_multiplier * m_r.z);
|
||||
}
|
||||
if (m_upscale_multiplier * m_r.w > m_height)
|
||||
{
|
||||
GL_INS("ERROR: RT height is too small only %d but require %d", m_height, m_upscale_multiplier * m_r.w);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fm != 0xffffffff && rt)
|
||||
{
|
||||
//rt->m_valid = rt->m_valid.runion(r);
|
||||
@@ -1660,8 +1692,8 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
s = format("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
|
||||
|
||||
if (rt)
|
||||
rt->m_texture->Save(m_dump_root + s);
|
||||
if (rt_tex)
|
||||
rt_tex->Save(m_dump_root + s);
|
||||
}
|
||||
|
||||
if (s_savez && s_n >= s_saven)
|
||||
@@ -1708,7 +1740,14 @@ GSRendererHW::Hacks::Hacks()
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::JakX, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutTakedown, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutRevenge, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutDominator, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::OO_MajokkoALaMode2));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutTakedown, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutRevenge, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutDominator, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
|
||||
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::CU_MajokkoALaMode2));
|
||||
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::TalesOfAbyss, CRC::RegionCount, &GSRendererHW::CU_TalesOfAbyss));
|
||||
@@ -1968,8 +2007,11 @@ bool GSRendererHW::OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::S
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!((m_r == GSVector4i(0, 0, 16, 16)).alltrue() || (m_r == GSVector4i(0, 0, 64, 64)).alltrue()))
|
||||
return true; // Only 16x16 or 64x64 draws.
|
||||
|
||||
// Sprite rendering
|
||||
if (!CanUseSwSpriteRender(true))
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
SwSpriteRender();
|
||||
@@ -2192,60 +2234,52 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
|
||||
|
||||
bool GSRendererHW::OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS && !PRIM->TME)
|
||||
const size_t n_vertices = m_vertex.next;
|
||||
const int w = m_r.width();
|
||||
const int h = m_r.height();
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
|
||||
&& h <= 64 // Small draws.
|
||||
&& n_vertices <= 256 // Small draws.
|
||||
&& PRIM->ABE // Alpha blending.
|
||||
&& !PRIM->TME // No texturing please.
|
||||
&& m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
|
||||
&& !PRIM->FGE // No FOG.
|
||||
&& !PRIM->AA1 // No antialiasing.
|
||||
&& !PRIM->FIX // Normal fragment value control.
|
||||
&& !m_env.DTHE.DTHE // No dithering.
|
||||
&& !m_context->TEST.ATE // No alpha test.
|
||||
&& !m_context->TEST.DATE // No destination alpha test.
|
||||
&& (!m_context->DepthRead() && !m_context->DepthWrite()) // No depth handling.
|
||||
&& !m_context->TEX0.CSM // No CLUT usage.
|
||||
&& !m_env.PABE.PABE // No PABE.
|
||||
&& m_context->FBA.FBA == 0 // No Alpha Correction.
|
||||
&& m_context->FRAME.FBMSK == 0 // No frame buffer masking.
|
||||
&& m_context->ALPHA.A == m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
|
||||
&& m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
|
||||
)
|
||||
{
|
||||
const u32 FBP = m_context->FRAME.Block();
|
||||
const u32 FBW = m_context->FRAME.FBW;
|
||||
|
||||
if (FBP >= 0x03f40 && (FBP & 0x1f) == 0)
|
||||
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", m_r.x, m_r.y, m_r.z, m_r.w, n_vertices, FBP, FBW);
|
||||
const GSVertex* RESTRICT v = m_vertex.buff;
|
||||
const int ox(m_context->XYOFFSET.OFX);
|
||||
const int oy(m_context->XYOFFSET.OFY);
|
||||
for (size_t i = 0; i < n_vertices; ++i)
|
||||
{
|
||||
if (m_vertex.next == 16)
|
||||
{
|
||||
GSVertex* RESTRICT v = m_vertex.buff;
|
||||
|
||||
for (int i = 0; i < 16; i++, v++)
|
||||
{
|
||||
u32 c = v->RGBAQ.U32[0];
|
||||
const u32 a = c >> 24;
|
||||
|
||||
c = (a >= 0x80 ? 0xff000000 : (a << 25)) | (c & 0x00ffffff);
|
||||
|
||||
v->RGBAQ.U32[0] = c;
|
||||
|
||||
m_mem.WritePixel32(i & 7, i >> 3, c, FBP, FBW);
|
||||
}
|
||||
|
||||
m_mem.m_clut.Invalidate();
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (m_vertex.next == 256)
|
||||
{
|
||||
GSVertex* RESTRICT v = m_vertex.buff;
|
||||
|
||||
for (int i = 0; i < 256; i++, v++)
|
||||
{
|
||||
u32 c = v->RGBAQ.U32[0];
|
||||
const u32 a = c >> 24;
|
||||
|
||||
c = (a >= 0x80 ? 0xff000000 : (a << 25)) | (c & 0x00ffffff);
|
||||
|
||||
v->RGBAQ.U32[0] = c;
|
||||
|
||||
m_mem.WritePixel32(i & 15, i >> 4, c, FBP, FBW);
|
||||
}
|
||||
|
||||
m_mem.m_clut.Invalidate();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
}
|
||||
const GSVertex& vi = v[i];
|
||||
const GIFRegXYZ& xyz = vi.XYZ;
|
||||
const int x = (int(xyz.X) - ox) / 16;
|
||||
const int y = (int(xyz.Y) - oy) / 16;
|
||||
if (x < m_r.x || x > m_r.z)
|
||||
continue;
|
||||
if (y < m_r.y || y > m_r.w)
|
||||
continue;
|
||||
const u32 c = vi.RGBAQ.U32[0];
|
||||
m_mem.WritePixel32(x, y, c, FBP, FBW);
|
||||
}
|
||||
m_tc->InvalidateVideoMem(m_context->offset.fb, m_r);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2315,7 +2349,10 @@ bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::
|
||||
|
||||
bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!CanUseSwSpriteRender(false))
|
||||
if (!(m_r == GSVector4i(0, 0, 16, 16)).alltrue())
|
||||
return true; // Only 16x16 draws.
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render 16x16 palette via CPU.
|
||||
@@ -2324,6 +2361,23 @@ bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Sou
|
||||
return false; // Skip current draw.
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!OI_PointListPalette(rt, ds, t))
|
||||
return false; // Render point list palette.
|
||||
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render palette via CPU.
|
||||
SwSpriteRender();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OO (others output?) hacks: invalidate extra local memory after the draw call
|
||||
|
||||
void GSRendererHW::OO_MajokkoALaMode2()
|
||||
@@ -2344,6 +2398,41 @@ void GSRendererHW::OO_MajokkoALaMode2()
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::OO_BurnoutGames()
|
||||
{
|
||||
const GIFRegTEX0& TEX0 = m_context->TEX0;
|
||||
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
||||
const GIFRegFRAME& FRAME = m_context->FRAME;
|
||||
if (PRIM->PRIM == GS_SPRITE
|
||||
&& !PRIM->IIP
|
||||
&& PRIM->TME
|
||||
&& !PRIM->FGE
|
||||
&& PRIM->ABE
|
||||
&& !PRIM->AA1
|
||||
&& !PRIM->FST
|
||||
&& !PRIM->FIX
|
||||
&& TEX0.TBW == 16
|
||||
&& TEX0.TW == 10
|
||||
&& TEX0.TCC
|
||||
&& !TEX0.TFX
|
||||
&& TEX0.PSM == PSM_PSMT8
|
||||
&& TEX0.CPSM == PSM_PSMCT32
|
||||
&& !TEX0.CSM
|
||||
&& TEX0.TH == 8
|
||||
&& ALPHA.A == ALPHA.B
|
||||
&& ALPHA.D == 0
|
||||
&& FRAME.FBW == 16
|
||||
&& FRAME.PSM == PSM_PSMCT32)
|
||||
{
|
||||
// Readback clouds being rendered during level loading.
|
||||
// Later the alpha channel from the 32 bit frame buffer is used as an 8 bit indexed texture to draw
|
||||
// the clouds on top of the sky at each frame.
|
||||
// Burnout 3 PAL 50Hz: 0x3ba0 => 0x1e80.
|
||||
GL_INS("OO_BurnoutGames - Readback clouds renderered from TEX0.TBP0 = 0x%04x (TEX0.CBP = 0x%04x) to FBP = 0x%04x", TEX0.TBP0, TEX0.CBP, FRAME.Block());
|
||||
m_tc->InvalidateLocalMem(m_context->offset.fb, m_r);
|
||||
}
|
||||
}
|
||||
|
||||
// Can Upscale hacks: disable upscaling for some draw calls
|
||||
|
||||
bool GSRendererHW::CU_MajokkoALaMode2()
|
||||
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
bool m_userhacks_enabled_gs_mem_clear;
|
||||
bool m_userHacks_merge_sprite;
|
||||
|
||||
static const float SSR_UV_TOLERANCE;
|
||||
static constexpr float SSR_UV_TOLERANCE = 1.0f;
|
||||
|
||||
#pragma region hacks
|
||||
|
||||
@@ -59,8 +59,10 @@ private:
|
||||
bool OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
void OO_MajokkoALaMode2();
|
||||
void OO_BurnoutGames();
|
||||
|
||||
bool CU_MajokkoALaMode2();
|
||||
bool CU_TalesOfAbyss();
|
||||
@@ -136,7 +138,7 @@ private:
|
||||
float alpha0(int L, int X0, int X1);
|
||||
float alpha1(int L, int X0, int X1);
|
||||
void SwSpriteRender();
|
||||
bool CanUseSwSpriteRender(bool allow_64x64_sprite);
|
||||
bool CanUseSwSpriteRender();
|
||||
|
||||
template <bool linear>
|
||||
void RoundSpriteOffset();
|
||||
@@ -179,6 +181,7 @@ public:
|
||||
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
||||
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
|
||||
void MergeSprite(GSTextureCache::Source* tex);
|
||||
GSVector2 GetTextureScaleFactor();
|
||||
|
||||
void Reset();
|
||||
void VSync(int field);
|
||||
|
||||
@@ -498,24 +498,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
|
||||
void GSTextureCache::ScaleTexture(GSTexture* texture)
|
||||
{
|
||||
if (!m_renderer->CanUpscale())
|
||||
return;
|
||||
|
||||
float multiplier = static_cast<float>(m_renderer->GetUpscaleMultiplier());
|
||||
bool custom_resolution = (multiplier == 0);
|
||||
GSVector2 scale_factor(multiplier);
|
||||
|
||||
if (custom_resolution)
|
||||
{
|
||||
int width = m_renderer->GetDisplayRect().width();
|
||||
int height = m_renderer->GetDisplayRect().height();
|
||||
|
||||
GSVector2i requested_resolution = m_renderer->GetCustomResolution();
|
||||
scale_factor.x = static_cast<float>(requested_resolution.x) / width;
|
||||
scale_factor.y = static_cast<float>(requested_resolution.y) / height;
|
||||
}
|
||||
|
||||
texture->SetScale(scale_factor);
|
||||
if (texture)
|
||||
texture->SetScale(m_renderer->GetTextureScaleFactor());
|
||||
}
|
||||
|
||||
bool GSTextureCache::ShallSearchTextureInsideRt()
|
||||
@@ -582,22 +566,31 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
||||
|
||||
if (dst_match)
|
||||
{
|
||||
GSVector4 sRect(0, 0, 1, 1);
|
||||
GSVector4 dRect(0, 0, w, h);
|
||||
const GSVector2& new_s = m_renderer->GetTextureScaleFactor();
|
||||
const GSVector2& old_s = dst_match->m_texture->GetScale();
|
||||
const GSVector2 ratio{ new_s.x / old_s.x, new_s.y / old_s.y };
|
||||
const int old_w = dst_match->m_texture->GetWidth();
|
||||
const int old_h = dst_match->m_texture->GetHeight();
|
||||
const float res_w = static_cast<float>(old_w) * ratio.x;
|
||||
const float res_h = static_cast<float>(old_h) * ratio.y;
|
||||
const int new_w = std::max(static_cast<int>(std::ceil(res_w)), w);
|
||||
const int new_h = std::max(static_cast<int>(std::ceil(res_h)), h);
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0.0f, 0.0f, res_w, res_h);
|
||||
|
||||
dst = CreateTarget(TEX0, w, h, type);
|
||||
dst = CreateTarget(TEX0, new_w, new_h, type);
|
||||
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
|
||||
|
||||
ShaderConvert shader;
|
||||
bool fmt_16_bits = (psm_s.bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16);
|
||||
if (type == DepthStencil)
|
||||
{
|
||||
GL_CACHE("TC: Lookup Target(Depth) %dx%d, hit Color (0x%x, %s was %s)", w, h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
||||
GL_CACHE("TC: Lookup Target(Depth) %dx%d, hit Color (0x%x, %s was %s)", new_w, new_h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
||||
shader = (fmt_16_bits) ? ShaderConvert::RGB5A1_TO_FLOAT16 : (ShaderConvert)((int)ShaderConvert::RGBA8_TO_FLOAT32 + psm_s.fmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, %s was %s)", w, h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
||||
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, %s was %s)", new_w, new_h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
||||
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
|
||||
}
|
||||
m_renderer->m_dev->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
|
||||
@@ -1700,7 +1693,7 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||
GL_PERF("TC: Read Back Target: %d (0x%x)[fmt: 0x%x]. Size %dx%d",
|
||||
t->m_texture->GetID(), TEX0.TBP0, TEX0.PSM, r.width(), r.height());
|
||||
|
||||
GSVector4 src = GSVector4(r) * GSVector4(t->m_texture->GetScale()).xyxy() / GSVector4(t->m_texture->GetSize()).xyxy();
|
||||
const GSVector4 src = GSVector4(r) * GSVector4(t->m_texture->GetScale()).xyxy() / GSVector4(t->m_texture->GetSize()).xyxy();
|
||||
|
||||
bool res;
|
||||
GSTexture::GSMap m;
|
||||
@@ -1708,11 +1701,11 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||
if (t->m_texture->GetScale() == GSVector2(1, 1) && ps_shader == ShaderConvert::COPY)
|
||||
res = m_renderer->m_dev->DownloadTexture(t->m_texture, r, m);
|
||||
else
|
||||
res = m_renderer->m_dev->DownloadTextureConvert(t->m_texture, src, GSVector2i(r.width(), r.height()), fmt, ps_shader, m);
|
||||
res = m_renderer->m_dev->DownloadTextureConvert(t->m_texture, src, GSVector2i(r.width(), r.height()), fmt, ps_shader, m, false);
|
||||
|
||||
if (res)
|
||||
{
|
||||
GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||
const GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||
|
||||
switch (TEX0.PSM)
|
||||
{
|
||||
|
||||
@@ -18,17 +18,12 @@
|
||||
#include "GSDeviceOGL.h"
|
||||
#include "GLState.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
//#define ONLY_LINES
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "GS/resource.h"
|
||||
#else
|
||||
#include "GS_res.h"
|
||||
#endif
|
||||
|
||||
// TODO port those value into PerfMon API
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
u64 g_real_texture_upload_byte = 0;
|
||||
@@ -54,8 +49,7 @@ int GSDeviceOGL::m_shader_reg = 0;
|
||||
FILE* GSDeviceOGL::m_debug_gl_file = NULL;
|
||||
|
||||
GSDeviceOGL::GSDeviceOGL()
|
||||
: m_force_texture_clear(0)
|
||||
, m_fbo(0)
|
||||
: m_fbo(0)
|
||||
, m_fbo_read(0)
|
||||
, m_apitrace(0)
|
||||
, m_palette_ss(0)
|
||||
@@ -243,75 +237,14 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int w, int h, GSText
|
||||
{
|
||||
GL_PUSH("Create surface");
|
||||
|
||||
// A wrapper to call GSTextureOGL, with the different kind of parameter
|
||||
// A wrapper to call GSTextureOGL, with the different kind of parameters.
|
||||
GSTextureOGL* t = new GSTextureOGL(type, w, h, fmt, m_fbo_read, m_mipmap > 1 || m_filter != TriFiltering::None);
|
||||
|
||||
// NOTE: I'm not sure RenderTarget always need to be cleared. It could be costly for big upscale.
|
||||
// FIXME: it will be more logical to do it in FetchSurface. This code is only called at first creation
|
||||
// of the texture. However we could reuse a deleted texture.
|
||||
if (m_force_texture_clear == 0)
|
||||
{
|
||||
// Clear won't be done if the texture isn't committed. Commit the full texture to ensure
|
||||
// correct behavior of force clear option (debug option)
|
||||
t->Commit();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
ClearRenderTarget(t, 0);
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
ClearDepth(t);
|
||||
// No need to clear the stencil now.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
GSTexture* GSDeviceOGL::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
||||
{
|
||||
GSTexture* t = GSDevice::FetchSurface(type, w, h, format);
|
||||
|
||||
|
||||
if (m_force_texture_clear)
|
||||
{
|
||||
// Clear won't be done if the texture isn't committed. Commit the full texture to ensure
|
||||
// correct behavior of force clear option (debug option)
|
||||
t->Commit();
|
||||
|
||||
const GSVector4 red(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
switch (type)
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
ClearRenderTarget(t, 0);
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
ClearDepth(t);
|
||||
// No need to clear the stencil now.
|
||||
break;
|
||||
case GSTexture::Type::Texture:
|
||||
if (m_force_texture_clear > 1)
|
||||
static_cast<GSTextureOGL*>(t)->Clear((void*)&red);
|
||||
else if (m_force_texture_clear)
|
||||
static_cast<GSTextureOGL*>(t)->Clear(NULL);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
{
|
||||
std::vector<char> shader;
|
||||
|
||||
m_gl_context = GL::Context::Create(wi, GL::Context::GetAllVersionsList());
|
||||
if (!m_gl_context || !m_gl_context->MakeCurrent())
|
||||
return false;
|
||||
@@ -329,6 +262,14 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto shader = Host::ReadResourceFileToString("shaders/opengl/common_header.glsl");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
m_shader_common_header = std::move(*shader);
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
// Debug helper
|
||||
// ****************************************************************
|
||||
@@ -345,8 +286,6 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
}
|
||||
#endif
|
||||
|
||||
m_force_texture_clear = theApp.GetConfigI("force_texture_clear");
|
||||
|
||||
// WARNING it must be done after the control setup (at least on MESA)
|
||||
GL_PUSH("GSDeviceOGL::Create");
|
||||
|
||||
@@ -443,15 +382,17 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
m_misc_cb_cache.ScalingFactor = GSVector4i(std::max(1, theApp.GetConfigI("upscale_multiplier")));
|
||||
m_convert.cb->cache_upload(&m_misc_cb_cache);
|
||||
|
||||
theApp.LoadResource(IDR_CONVERT_GLSL, shader);
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/convert.glsl");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
vs = m_shader->Compile("convert.glsl", "vs_main", GL_VERTEX_SHADER, shader.data());
|
||||
vs = m_shader->Compile("convert.glsl", "vs_main", GL_VERTEX_SHADER, m_shader_common_header, shader->c_str());
|
||||
|
||||
m_convert.vs = vs;
|
||||
for (size_t i = 0; i < std::size(m_convert.ps); i++)
|
||||
{
|
||||
const char* name = shaderName(static_cast<ShaderConvert>(i));
|
||||
ps = m_shader->Compile("convert.glsl", name, GL_FRAGMENT_SHADER, shader.data());
|
||||
ps = m_shader->Compile("convert.glsl", name, GL_FRAGMENT_SHADER, m_shader_common_header, shader->c_str());
|
||||
std::string pretty_name = std::string("Convert pipe ") + name;
|
||||
m_convert.ps[i] = m_shader->LinkPipeline(pretty_name, vs, 0, ps);
|
||||
}
|
||||
@@ -477,11 +418,13 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
|
||||
m_merge_obj.cb = new GSUniformBufferOGL("Merge UBO", g_merge_cb_index, sizeof(MergeConstantBuffer));
|
||||
|
||||
theApp.LoadResource(IDR_MERGE_GLSL, shader);
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/merge.glsl");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < std::size(m_merge_obj.ps); i++)
|
||||
{
|
||||
ps = m_shader->Compile("merge.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, shader.data());
|
||||
ps = m_shader->Compile("merge.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, m_shader_common_header, shader->c_str());
|
||||
std::string pretty_name = "Merge pipe " + std::to_string(i);
|
||||
m_merge_obj.ps[i] = m_shader->LinkPipeline(pretty_name, vs, 0, ps);
|
||||
}
|
||||
@@ -495,11 +438,13 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
|
||||
m_interlace.cb = new GSUniformBufferOGL("Interlace UBO", g_interlace_cb_index, sizeof(InterlaceConstantBuffer));
|
||||
|
||||
theApp.LoadResource(IDR_INTERLACE_GLSL, shader);
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/interlace.glsl");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < std::size(m_interlace.ps); i++)
|
||||
{
|
||||
ps = m_shader->Compile("interlace.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, shader.data());
|
||||
ps = m_shader->Compile("interlace.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, m_shader_common_header, shader->c_str());
|
||||
std::string pretty_name = "Interlace pipe " + std::to_string(i);
|
||||
m_interlace.ps[i] = m_shader->LinkPipeline(pretty_name, vs, 0, ps);
|
||||
}
|
||||
@@ -518,9 +463,11 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
+ format("#define SB_BRIGHTNESS %d.0\n", ShadeBoost_Brightness)
|
||||
+ format("#define SB_CONTRAST %d.0\n", ShadeBoost_Contrast);
|
||||
|
||||
theApp.LoadResource(IDR_SHADEBOOST_GLSL, shader);
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
|
||||
if (!shader.has_value())
|
||||
return false;
|
||||
|
||||
ps = m_shader->Compile("shadeboost.glsl", "ps_main", GL_FRAGMENT_SHADER, shader.data(), shade_macro);
|
||||
ps = m_shader->Compile("shadeboost.glsl", "ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, shader->c_str(), shade_macro);
|
||||
m_shadeboost.ps = m_shader->LinkPipeline("ShadeBoost pipe", vs, 0, ps);
|
||||
}
|
||||
|
||||
@@ -569,7 +516,8 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
// ****************************************************************
|
||||
// HW renderer shader
|
||||
// ****************************************************************
|
||||
CreateTextureFX();
|
||||
if (!CreateTextureFX())
|
||||
return false;
|
||||
|
||||
// ****************************************************************
|
||||
// Pbo Pool allocation
|
||||
@@ -640,12 +588,17 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::CreateTextureFX()
|
||||
bool GSDeviceOGL::CreateTextureFX()
|
||||
{
|
||||
GL_PUSH("GSDeviceOGL::CreateTextureFX");
|
||||
|
||||
theApp.LoadResource(IDR_TFX_VGS_GLSL, m_shader_tfx_vgs);
|
||||
theApp.LoadResource(IDR_TFX_FS_GLSL, m_shader_tfx_fs);
|
||||
auto vertex_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_vgs.glsl");
|
||||
auto fragment_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_fs.glsl");
|
||||
if (!vertex_shader.has_value() || !fragment_shader.has_value())
|
||||
return false;
|
||||
|
||||
m_shader_tfx_vgs = std::move(*vertex_shader);
|
||||
m_shader_tfx_fs = std::move(*fragment_shader);
|
||||
|
||||
// warning 1 sampler by image unit. So you cannot reuse m_ps_ss...
|
||||
m_palette_ss = CreateSampler(PSSamplerSelector(0));
|
||||
@@ -672,6 +625,7 @@ void GSDeviceOGL::CreateTextureFX()
|
||||
|
||||
// Help to debug FS in apitrace
|
||||
m_apitrace = CompilePS(PSSelector());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::Reset(int w, int h)
|
||||
@@ -993,9 +947,9 @@ GLuint GSDeviceOGL::CompileVS(VSSelector sel)
|
||||
std::string macro = format("#define VS_INT_FST %d\n", sel.int_fst);
|
||||
|
||||
if (GLLoader::buggy_sso_dual_src)
|
||||
return m_shader->CompileShader("tfx_vgs.glsl", "vs_main", GL_VERTEX_SHADER, m_shader_tfx_vgs.data(), macro);
|
||||
return m_shader->CompileShader("tfx_vgs.glsl", "vs_main", GL_VERTEX_SHADER, m_shader_common_header, m_shader_tfx_vgs.data(), macro);
|
||||
else
|
||||
return m_shader->Compile("tfx_vgs.glsl", "vs_main", GL_VERTEX_SHADER, m_shader_tfx_vgs.data(), macro);
|
||||
return m_shader->Compile("tfx_vgs.glsl", "vs_main", GL_VERTEX_SHADER, m_shader_common_header, m_shader_tfx_vgs.data(), macro);
|
||||
}
|
||||
|
||||
GLuint GSDeviceOGL::CompileGS(GSSelector sel)
|
||||
@@ -1004,9 +958,9 @@ GLuint GSDeviceOGL::CompileGS(GSSelector sel)
|
||||
+ format("#define GS_LINE %d\n", sel.line);
|
||||
|
||||
if (GLLoader::buggy_sso_dual_src)
|
||||
return m_shader->CompileShader("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, m_shader_tfx_vgs.data(), macro);
|
||||
return m_shader->CompileShader("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, m_shader_common_header, m_shader_tfx_vgs.data(), macro);
|
||||
else
|
||||
return m_shader->Compile("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, m_shader_tfx_vgs.data(), macro);
|
||||
return m_shader->Compile("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, m_shader_common_header, m_shader_tfx_vgs.data(), macro);
|
||||
}
|
||||
|
||||
GLuint GSDeviceOGL::CompilePS(PSSelector sel)
|
||||
@@ -1052,9 +1006,9 @@ GLuint GSDeviceOGL::CompilePS(PSSelector sel)
|
||||
;
|
||||
|
||||
if (GLLoader::buggy_sso_dual_src)
|
||||
return m_shader->CompileShader("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, m_shader_tfx_fs.data(), macro);
|
||||
return m_shader->CompileShader("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, m_shader_tfx_fs.data(), macro);
|
||||
else
|
||||
return m_shader->Compile("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, m_shader_tfx_fs.data(), macro);
|
||||
return m_shader->Compile("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, m_shader_tfx_fs.data(), macro);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::SelfShaderTestRun(const std::string& dir, const std::string& file, const PSSelector& sel, int& nb_shader)
|
||||
@@ -1285,8 +1239,7 @@ bool GSDeviceOGL::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSText
|
||||
// Copy a sub part of texture (same as below but force a conversion)
|
||||
void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear)
|
||||
{
|
||||
const GLuint sid = static_cast<GSTextureOGL*>(sTex)->GetID();
|
||||
GL_PUSH(format("CopyRectConv from %d", sid).c_str());
|
||||
GL_PUSH(format("CopyRectConv from %d", static_cast<GSTextureOGL*>(sTex)->GetID()).c_str());
|
||||
|
||||
// NOTE: This previously used glCopyTextureSubImage2D(), but this appears to leak memory in
|
||||
// the loading screens of Evolution Snowboarding in Intel/NVIDIA drivers.
|
||||
@@ -1596,10 +1549,11 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
std::string fxaa_macro = "#define FXAA_GLSL_130 1\n";
|
||||
fxaa_macro += "#extension GL_ARB_gpu_shader5 : enable\n";
|
||||
|
||||
std::vector<char> shader;
|
||||
theApp.LoadResource(IDR_FXAA_FX, shader);
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/common/fxaa.fx");
|
||||
if (!shader.has_value())
|
||||
return;
|
||||
|
||||
GLuint ps = m_shader->Compile("fxaa.fx", "ps_main", GL_FRAGMENT_SHADER, shader.data(), fxaa_macro);
|
||||
GLuint ps = m_shader->Compile("fxaa.fx", "ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, shader->c_str(), fxaa_macro);
|
||||
m_fxaa.ps = m_shader->LinkPipeline("FXAA pipe", m_convert.vs, 0, ps);
|
||||
}
|
||||
|
||||
@@ -1646,7 +1600,7 @@ void GSDeviceOGL::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
|
||||
|
||||
|
||||
m_shaderfx.cb = new GSUniformBufferOGL("eFX UBO", g_fx_cb_index, sizeof(ExternalFXConstantBuffer));
|
||||
GLuint ps = m_shader->Compile("Extra", "ps_main", GL_FRAGMENT_SHADER, shader.str().c_str(), config.str());
|
||||
GLuint ps = m_shader->Compile("Extra", "ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, shader.str().c_str(), config.str());
|
||||
m_shaderfx.ps = m_shader->LinkPipeline("eFX pipie", m_convert.vs, 0, ps);
|
||||
}
|
||||
|
||||
|
||||
@@ -462,7 +462,6 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<GL::Context> m_gl_context;
|
||||
int m_force_texture_clear;
|
||||
int m_mipmap;
|
||||
TriFiltering m_filter;
|
||||
|
||||
@@ -472,8 +471,9 @@ private:
|
||||
bool m_disable_hw_gl_draw;
|
||||
|
||||
// Place holder for the GLSL shader code (to avoid useless reload)
|
||||
std::vector<char> m_shader_tfx_vgs;
|
||||
std::vector<char> m_shader_tfx_fs;
|
||||
std::string m_shader_common_header;
|
||||
std::string m_shader_tfx_vgs;
|
||||
std::string m_shader_tfx_fs;
|
||||
|
||||
GLuint m_fbo; // frame buffer container
|
||||
GLuint m_fbo_read; // frame buffer container only for reading
|
||||
@@ -558,7 +558,6 @@ private:
|
||||
AlignedBuffer<u8, 32> m_download_buffer;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
||||
GSTexture* FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
|
||||
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final;
|
||||
@@ -633,7 +632,7 @@ public:
|
||||
bool HasColorSparse() final { return GLLoader::found_compatible_GL_ARB_sparse_texture2; }
|
||||
bool HasDepthSparse() final { return GLLoader::found_compatible_sparse_depth; }
|
||||
|
||||
void CreateTextureFX();
|
||||
bool CreateTextureFX();
|
||||
GLuint CompileVS(VSSelector sel);
|
||||
GLuint CompileGS(GSSelector sel);
|
||||
GLuint CompilePS(PSSelector sel);
|
||||
|
||||
@@ -18,18 +18,11 @@
|
||||
#include "GLState.h"
|
||||
#include "GS/GS.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "GS/resource.h"
|
||||
#else
|
||||
#include "GS_res.h"
|
||||
#endif
|
||||
|
||||
GSShaderOGL::GSShaderOGL(bool debug)
|
||||
: m_pipeline(0)
|
||||
, m_debug_shader(debug)
|
||||
{
|
||||
theApp.LoadResource(IDR_COMMON_GLSL, m_common_header);
|
||||
|
||||
// Create a default pipeline
|
||||
m_pipeline = LinkPipeline("HW pipe", 0, 0, 0);
|
||||
BindPipeline(m_pipeline);
|
||||
@@ -272,7 +265,7 @@ std::string GSShaderOGL::GenGlslHeader(const std::string& entry, GLenum type, co
|
||||
return header;
|
||||
}
|
||||
|
||||
GLuint GSShaderOGL::Compile(const std::string& glsl_file, const std::string& entry, GLenum type, const char* glsl_h_code, const std::string& macro_sel)
|
||||
GLuint GSShaderOGL::Compile(const char* glsl_file, const std::string& entry, GLenum type, const std::string& common_header, const char* glsl_h_code, const std::string& macro_sel /* = "" */)
|
||||
{
|
||||
ASSERT(glsl_h_code != NULL);
|
||||
|
||||
@@ -286,7 +279,7 @@ GLuint GSShaderOGL::Compile(const std::string& glsl_file, const std::string& ent
|
||||
std::string header = GenGlslHeader(entry, type, macro_sel);
|
||||
|
||||
sources[0] = header.c_str();
|
||||
sources[1] = m_common_header.data();
|
||||
sources[1] = common_header.c_str();
|
||||
sources[2] = glsl_h_code;
|
||||
|
||||
program = glCreateShaderProgramv(type, shader_nb, sources);
|
||||
@@ -296,7 +289,7 @@ GLuint GSShaderOGL::Compile(const std::string& glsl_file, const std::string& ent
|
||||
if (!status)
|
||||
{
|
||||
// print extra info
|
||||
fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file.c_str(), entry.c_str(), program);
|
||||
fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file, entry.c_str(), program);
|
||||
fprintf(stderr, "\n%s", macro_sel.c_str());
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
@@ -307,7 +300,7 @@ GLuint GSShaderOGL::Compile(const std::string& glsl_file, const std::string& ent
|
||||
}
|
||||
|
||||
// Same as above but for not-separated build
|
||||
GLuint GSShaderOGL::CompileShader(const std::string& glsl_file, const std::string& entry, GLenum type, const char* glsl_h_code, const std::string& macro_sel)
|
||||
GLuint GSShaderOGL::CompileShader(const char* glsl_file, const std::string& entry, GLenum type, const std::string& common_header, const char* glsl_h_code, const std::string& macro_sel /* = "" */)
|
||||
{
|
||||
ASSERT(glsl_h_code != NULL);
|
||||
|
||||
@@ -321,7 +314,7 @@ GLuint GSShaderOGL::CompileShader(const std::string& glsl_file, const std::strin
|
||||
std::string header = GenGlslHeader(entry, type, macro_sel);
|
||||
|
||||
sources[0] = header.c_str();
|
||||
sources[1] = m_common_header.data();
|
||||
sources[1] = common_header.data();
|
||||
sources[2] = glsl_h_code;
|
||||
|
||||
shader = glCreateShader(type);
|
||||
@@ -333,7 +326,7 @@ GLuint GSShaderOGL::CompileShader(const std::string& glsl_file, const std::strin
|
||||
if (!status)
|
||||
{
|
||||
// print extra info
|
||||
fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file.c_str(), entry.c_str(), shader);
|
||||
fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file, entry.c_str(), shader);
|
||||
fprintf(stderr, "\n%s", macro_sel.c_str());
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ class GSShaderOGL
|
||||
bool ValidatePipeline(GLuint p);
|
||||
|
||||
std::string GenGlslHeader(const std::string& entry, GLenum type, const std::string& macro);
|
||||
std::vector<char> m_common_header;
|
||||
|
||||
public:
|
||||
GSShaderOGL(bool debug);
|
||||
@@ -42,14 +41,14 @@ public:
|
||||
void BindPipeline(GLuint vs, GLuint gs, GLuint ps);
|
||||
void BindPipeline(GLuint pipe);
|
||||
|
||||
GLuint Compile(const std::string& glsl_file, const std::string& entry, GLenum type, const char* glsl_h_code, const std::string& macro_sel = "");
|
||||
GLuint Compile(const char* glsl_file, const std::string& entry, GLenum type, const std::string& common_header, const char* glsl_h_code, const std::string& macro_sel = "");
|
||||
GLuint LinkPipeline(const std::string& pretty_print, GLuint vs, GLuint gs, GLuint ps);
|
||||
|
||||
// Same as above but for not separated build
|
||||
void BindProgram(GLuint vs, GLuint gs, GLuint ps);
|
||||
void BindProgram(GLuint p);
|
||||
|
||||
GLuint CompileShader(const std::string& glsl_file, const std::string& entry, GLenum type, const char* glsl_h_code, const std::string& macro_sel = "");
|
||||
GLuint CompileShader(const char* glsl_file, const std::string& entry, GLenum type, const std::string& common_header, const char* glsl_h_code, const std::string& macro_sel = "");
|
||||
GLuint LinkProgram(GLuint vs, GLuint gs, GLuint ps);
|
||||
|
||||
int DumpAsm(const std::string& file, GLuint p);
|
||||
|
||||
@@ -573,7 +573,7 @@ bool GSTextureOGL::Save(const std::string& fn)
|
||||
u32 pitch = 4 * m_committed_size.x;
|
||||
u32 buf_size = pitch * m_committed_size.y * 2; // Note *2 for security (depth/stencil)
|
||||
std::unique_ptr<u8[]> image(new u8[buf_size]);
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
||||
#else
|
||||
GSPng::Format fmt = GSPng::RGB_PNG;
|
||||
|
||||
@@ -578,8 +578,6 @@ void GSRendererSW::Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item)
|
||||
if (sd->global.sel.fwrite)
|
||||
{
|
||||
m_tc->InvalidatePages(sd->m_fb_pages, sd->m_fpsm);
|
||||
|
||||
m_mem.m_clut.Invalidate(m_context->FRAME.Block());
|
||||
}
|
||||
|
||||
if (sd->global.sel.zwrite)
|
||||
|
||||
@@ -81,7 +81,7 @@ void GSTextureSW::Unmap()
|
||||
|
||||
bool GSTextureSW::Save(const std::string& fn)
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
||||
#else
|
||||
GSPng::Format fmt = GSPng::RGB_PNG;
|
||||
|
||||
@@ -29,7 +29,7 @@ GSDialog::GSDialog(UINT id)
|
||||
|
||||
INT_PTR GSDialog::DoModal()
|
||||
{
|
||||
return DialogBoxParam(theApp.GetModuleHandle(), MAKEINTRESOURCE(m_id), GetActiveWindow(), DialogProc, (LPARAM)this);
|
||||
return DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(m_id), GetActiveWindow(), DialogProc, (LPARAM)this);
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK GSDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
@@ -324,7 +324,7 @@ void GSDialog::AddTooltip(UINT id)
|
||||
HWND hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
|
||||
TTS_ALWAYSTIP | TTS_NOPREFIX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
m_hWnd, NULL, theApp.GetModuleHandle(), NULL);
|
||||
m_hWnd, NULL, GetModuleHandle(nullptr), NULL);
|
||||
if (hwndTip == NULL)
|
||||
return;
|
||||
|
||||
|
||||
@@ -98,18 +98,4 @@ enum
|
||||
IDC_SHADER_FX,
|
||||
IDC_FXAA,
|
||||
IDC_LINEAR_PRESENT,
|
||||
#ifndef _WIN32
|
||||
// Shader
|
||||
IDR_CONVERT_GLSL,
|
||||
IDR_FXAA_FX,
|
||||
IDR_INTERLACE_GLSL,
|
||||
IDR_MERGE_GLSL,
|
||||
IDR_SHADEBOOST_GLSL,
|
||||
IDR_COMMON_GLSL,
|
||||
IDR_TFX_VGS_GLSL,
|
||||
IDR_TFX_FS_GLSL,
|
||||
IDR_TFX_CL,
|
||||
// Fonts
|
||||
IDR_FONT_ROBOTO,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/common_header.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/convert.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>fxaa.fx</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/interlace.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/merge.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/shadeboost.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/tfx_vgs.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>glsl/tfx_fs.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/GS/res/">
|
||||
<file>fonts-roboto/Roboto-Regular.ttf</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
@@ -14,24 +14,9 @@
|
||||
#define IDC_CONFIGURE 2096
|
||||
#define IDC_COLORSPACE 2097
|
||||
|
||||
#define IDR_CONVERT_FX 10000
|
||||
#define IDR_TFX_FX 10001
|
||||
#define IDR_MERGE_FX 10002
|
||||
#define IDR_INTERLACE_FX 10003
|
||||
#define IDR_FXAA_FX 10004
|
||||
#define IDD_SHADER 10006
|
||||
#define IDR_SHADEBOOST_FX 10007
|
||||
#define IDR_TFX_CL 10008
|
||||
#define IDD_HACKS 10009
|
||||
#define IDD_OSD 10010
|
||||
#define IDR_CONVERT_GLSL 10011
|
||||
#define IDR_INTERLACE_GLSL 10012
|
||||
#define IDR_MERGE_GLSL 10013
|
||||
#define IDR_SHADEBOOST_GLSL 10014
|
||||
#define IDR_COMMON_GLSL 10015
|
||||
#define IDR_TFX_VGS_GLSL 10016
|
||||
#define IDR_TFX_FS_GLSL 10017
|
||||
#define IDR_FONT_ROBOTO 10018
|
||||
#define IDC_STATIC -1
|
||||
|
||||
// Next default values for new objects
|
||||
|
||||
+136
-84
@@ -16,54 +16,83 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GameDatabase.h"
|
||||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#include "fmt/core.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
std::string strToLower(std::string str)
|
||||
static constexpr char GAMEDB_YAML_FILE_NAME[] = "GameIndex.yaml";
|
||||
|
||||
static std::unordered_map<std::string, GameDatabaseSchema::GameEntry> s_game_db;
|
||||
static std::once_flag s_load_once_flag;
|
||||
|
||||
static std::string strToLower(std::string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return str;
|
||||
}
|
||||
|
||||
bool compareStrNoCase(const std::string str1, const std::string str2)
|
||||
{
|
||||
return std::equal(str1.begin(), str1.end(), str2.begin(),
|
||||
[](char a, char b) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
|
||||
std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() const
|
||||
std::string GameDatabaseSchema::GameEntry::MemcardFiltersAsString() const
|
||||
{
|
||||
return fmt::to_string(fmt::join(memcardFilters, "/"));
|
||||
}
|
||||
|
||||
bool GameDatabaseSchema::GameEntry::findPatch(const std::string crc, Patch& patch) const
|
||||
const GameDatabaseSchema::Patch* GameDatabaseSchema::GameEntry::FindPatch(const std::string& crc) const
|
||||
{
|
||||
std::string crcLower = strToLower(crc);
|
||||
const std::string crcLower(strToLower(crc));
|
||||
Console.WriteLn(fmt::format("[GameDB] Searching for patch with CRC '{}'", crc));
|
||||
if (patches.count(crcLower) == 1)
|
||||
|
||||
auto it = patches.find(crcLower);
|
||||
if (it != patches.end())
|
||||
{
|
||||
Console.WriteLn(fmt::format("[GameDB] Found patch with CRC '{}'", crc));
|
||||
patch = patches.at(crcLower);
|
||||
return true;
|
||||
return &it->second;
|
||||
}
|
||||
else if (patches.count("default") == 1)
|
||||
|
||||
it = patches.find("default");
|
||||
if (it != patches.end())
|
||||
{
|
||||
Console.WriteLn("[GameDB] Found and falling back to default patch");
|
||||
patch = patches.at("default");
|
||||
return true;
|
||||
return &it->second;
|
||||
}
|
||||
Console.WriteLn("[GameDB] No CRC-specific patch or default patch found");
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> YamlGameDatabaseImpl::convertMultiLineStringToVector(const std::string multiLineString)
|
||||
const char* GameDatabaseSchema::compatToString(Compatibility compat)
|
||||
{
|
||||
switch (compat)
|
||||
{
|
||||
case GameDatabaseSchema::Compatibility::Perfect:
|
||||
return "Perfect";
|
||||
case GameDatabaseSchema::Compatibility::Playable:
|
||||
return "Playable";
|
||||
case GameDatabaseSchema::Compatibility::InGame:
|
||||
return "In-Game";
|
||||
case GameDatabaseSchema::Compatibility::Menu:
|
||||
return "Menu";
|
||||
case GameDatabaseSchema::Compatibility::Intro:
|
||||
return "Intro";
|
||||
case GameDatabaseSchema::Compatibility::Nothing:
|
||||
return "Nothing";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<std::string> convertMultiLineStringToVector(const std::string& multiLineString)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream stream(multiLineString);
|
||||
@@ -77,7 +106,7 @@ std::vector<std::string> YamlGameDatabaseImpl::convertMultiLineStringToVector(co
|
||||
return lines;
|
||||
}
|
||||
|
||||
GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::string serial, const YAML::Node& node)
|
||||
static bool parseAndInsert(std::string serial, const YAML::Node& node)
|
||||
{
|
||||
GameDatabaseSchema::GameEntry gameEntry;
|
||||
try
|
||||
@@ -98,51 +127,63 @@ GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::str
|
||||
}
|
||||
|
||||
// Validate game fixes, invalid ones will be dropped!
|
||||
for (std::string& fix : node["gameFixes"].as<std::vector<std::string>>(std::vector<std::string>()))
|
||||
if (auto gameFixes = node["gameFixes"])
|
||||
{
|
||||
bool fixValidated = false;
|
||||
for (GamefixId id = GamefixId_FIRST; id < pxEnumEnd; id++)
|
||||
gameEntry.gameFixes.reserve(gameFixes.size());
|
||||
for (std::string& fix : gameFixes.as<std::vector<std::string>>(std::vector<std::string>()))
|
||||
{
|
||||
std::string validFix = fmt::format("{}Hack", wxString(EnumToString(id)).ToUTF8());
|
||||
if (validFix == fix)
|
||||
// Enum values don't end with Hack, but gamedb does, so remove it before comparing.
|
||||
bool fixValidated = false;
|
||||
if (StringUtil::EndsWith(fix, "Hack"))
|
||||
{
|
||||
fixValidated = true;
|
||||
break;
|
||||
fix.erase(fix.size() - 4);
|
||||
for (GamefixId id = GamefixId_FIRST; id < pxEnumEnd; id++)
|
||||
{
|
||||
if (fix.compare(EnumToString(id)) == 0 &&
|
||||
std::find(gameEntry.gameFixes.begin(), gameEntry.gameFixes.end(), id) == gameEntry.gameFixes.end())
|
||||
{
|
||||
gameEntry.gameFixes.push_back(id);
|
||||
fixValidated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fixValidated)
|
||||
{
|
||||
Console.Error("[GameDB] Invalid gamefix: '%s', specified for serial: '%s'. Dropping!", fix.c_str(), serial.c_str());
|
||||
}
|
||||
}
|
||||
if (fixValidated)
|
||||
{
|
||||
gameEntry.gameFixes.push_back(fix);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Invalid gamefix: '{}', specified for serial: '{}'. Dropping!", fix, serial));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate speed hacks, invalid ones will be dropped!
|
||||
if (YAML::Node speedHacksNode = node["speedHacks"])
|
||||
if (auto speedHacksNode = node["speedHacks"])
|
||||
{
|
||||
gameEntry.speedHacks.reserve(speedHacksNode.size());
|
||||
for (const auto& entry : speedHacksNode)
|
||||
{
|
||||
std::string speedHack = entry.first.as<std::string>();
|
||||
bool speedHackValidated = false;
|
||||
for (SpeedhackId id = SpeedhackId_FIRST; id < pxEnumEnd; id++)
|
||||
std::string speedHack(entry.first.as<std::string>());
|
||||
|
||||
// Same deal with SpeedHacks
|
||||
if (StringUtil::EndsWith(speedHack, "SpeedHack"))
|
||||
{
|
||||
std::string validSpeedHack = fmt::format("{}SpeedHack", wxString(EnumToString(id)).ToUTF8());
|
||||
if (validSpeedHack == speedHack)
|
||||
speedHack.erase(speedHack.size() - 9);
|
||||
for (SpeedhackId id = SpeedhackId_FIRST; id < pxEnumEnd; id++)
|
||||
{
|
||||
speedHackValidated = true;
|
||||
break;
|
||||
if (speedHack.compare(EnumToString(id)) == 0 &&
|
||||
std::none_of(gameEntry.speedHacks.begin(), gameEntry.speedHacks.end(), [id](const auto& it) { return it.first == id; }))
|
||||
{
|
||||
gameEntry.speedHacks.emplace_back(id, entry.second.as<int>());
|
||||
speedHackValidated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (speedHackValidated)
|
||||
|
||||
if (!speedHackValidated)
|
||||
{
|
||||
gameEntry.speedHacks[speedHack] = entry.second.as<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Invalid speedhack: '{}', specified for serial: '{}'. Dropping!", speedHack, serial));
|
||||
Console.Error("[GameDB] Invalid speedhack: '%s', specified for serial: '%s'. Dropping!", speedHack.c_str(), serial.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,60 +202,38 @@ GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::str
|
||||
}
|
||||
YAML::Node patchNode = entry.second;
|
||||
|
||||
GameDatabaseSchema::Patch patchCol;
|
||||
|
||||
patchCol.author = patchNode["author"].as<std::string>("");
|
||||
patchCol.patchLines = convertMultiLineStringToVector(patchNode["content"].as<std::string>(""));
|
||||
gameEntry.patches[crc] = patchCol;
|
||||
gameEntry.patches[crc] = convertMultiLineStringToVector(patchNode["content"].as<std::string>(""));
|
||||
}
|
||||
}
|
||||
|
||||
s_game_db.emplace(std::move(serial), std::move(gameEntry));
|
||||
return true;
|
||||
}
|
||||
catch (const YAML::RepresentationException& e)
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Invalid GameDB syntax detected on serial: '{}'. Error Details - {}", serial, e.msg));
|
||||
gameEntry.isValid = false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Unexpected error occurred when reading serial: '{}'. Error Details - {}", serial, e.what()));
|
||||
gameEntry.isValid = false;
|
||||
}
|
||||
return gameEntry;
|
||||
}
|
||||
|
||||
GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::findGame(const std::string serial)
|
||||
{
|
||||
std::string serialLower = strToLower(serial);
|
||||
Console.WriteLn(fmt::format("[GameDB] Searching for '{}' in GameDB", serialLower));
|
||||
if (gameDb.count(serialLower) == 1)
|
||||
{
|
||||
Console.WriteLn(fmt::format("[GameDB] Found '{}' in GameDB", serialLower));
|
||||
return gameDb[serialLower];
|
||||
}
|
||||
|
||||
Console.Error(fmt::format("[GameDB] Could not find '{}' in GameDB", serialLower));
|
||||
GameDatabaseSchema::GameEntry entry;
|
||||
entry.isValid = false;
|
||||
return entry;
|
||||
return false;
|
||||
}
|
||||
|
||||
int YamlGameDatabaseImpl::numGames()
|
||||
{
|
||||
return gameDb.size();
|
||||
}
|
||||
|
||||
bool YamlGameDatabaseImpl::initDatabase(std::ifstream& stream)
|
||||
static bool LoadYamlFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!stream)
|
||||
std::optional<std::string> file_data(Host::ReadResourceFileToString(GAMEDB_YAML_FILE_NAME));
|
||||
if (!file_data.has_value())
|
||||
{
|
||||
Console.Error("[GameDB] Unable to open GameDB file.");
|
||||
return false;
|
||||
}
|
||||
// yaml-cpp has memory leak issues if you persist and modify a YAML::Node
|
||||
// convert to a map and throw it away instead!
|
||||
YAML::Node data = YAML::Load(stream);
|
||||
YAML::Node data = YAML::Load(file_data.value());
|
||||
for (const auto& entry : data)
|
||||
{
|
||||
// we don't want to throw away the entire GameDB file if a single entry is made incorrectly,
|
||||
@@ -225,13 +244,13 @@ bool YamlGameDatabaseImpl::initDatabase(std::ifstream& stream)
|
||||
// this is because the application may pass a lowercase CRC or serial along
|
||||
//
|
||||
// However, YAML's keys are as expected case-sensitive, so we have to explicitly do our own duplicate checking
|
||||
std::string serial = strToLower(entry.first.as<std::string>());
|
||||
if (gameDb.count(serial) == 1)
|
||||
std::string serial(strToLower(entry.first.as<std::string>()));
|
||||
if (s_game_db.count(serial) == 1)
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Duplicate serial '{}' found in GameDB. Skipping, Serials are case-insensitive!", serial));
|
||||
continue;
|
||||
}
|
||||
gameDb[serial] = entryFromYaml(serial, entry.second);
|
||||
parseAndInsert(std::move(serial), entry.second);
|
||||
}
|
||||
catch (const YAML::RepresentationException& e)
|
||||
{
|
||||
@@ -247,3 +266,36 @@ bool YamlGameDatabaseImpl::initDatabase(std::ifstream& stream)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameDatabase::EnsureLoaded()
|
||||
{
|
||||
std::call_once(s_load_once_flag, []() {
|
||||
Common::Timer timer;
|
||||
|
||||
if (!LoadYamlFile())
|
||||
{
|
||||
Console.Error("GameDB: Failed to load YAML file");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLn("[GameDB] %zu games on record (loaded in %.2fms)", s_game_db.size(), timer.GetTimeMilliseconds());
|
||||
});
|
||||
}
|
||||
|
||||
const GameDatabaseSchema::GameEntry* GameDatabase::FindGame(const std::string& serial)
|
||||
{
|
||||
EnsureLoaded();
|
||||
|
||||
const std::string serialLower(strToLower(serial));
|
||||
Console.WriteLn("[GameDB] Searching for '%s' in GameDB", serialLower.c_str());
|
||||
|
||||
auto iter = s_game_db.find(serialLower);
|
||||
if (iter == s_game_db.end())
|
||||
{
|
||||
Console.Error("[GameDB] Could not find '%s' in GameDB", serialLower.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Console.WriteLn("[GameDB] Found '%s' in GameDB", serialLower.c_str());
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
+14
-35
@@ -15,12 +15,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
enum GamefixId;
|
||||
enum SpeedhackId;
|
||||
|
||||
// Since this is kinda yaml specific, might be a good idea to
|
||||
// relocate this into the yaml class
|
||||
// or put the serialization methods inside the yaml
|
||||
@@ -56,15 +57,10 @@ public:
|
||||
Full
|
||||
};
|
||||
|
||||
struct Patch
|
||||
{
|
||||
std::string author;
|
||||
std::vector<std::string> patchLines;
|
||||
};
|
||||
using Patch = std::vector<std::string>;
|
||||
|
||||
struct GameEntry
|
||||
{
|
||||
bool isValid = true;
|
||||
std::string name;
|
||||
std::string region;
|
||||
Compatibility compat = Compatibility::Unknown;
|
||||
@@ -72,39 +68,22 @@ public:
|
||||
RoundMode vuRoundMode = RoundMode::Undefined;
|
||||
ClampMode eeClampMode = ClampMode::Undefined;
|
||||
ClampMode vuClampMode = ClampMode::Undefined;
|
||||
std::vector<std::string> gameFixes;
|
||||
std::unordered_map<std::string, int> speedHacks;
|
||||
std::vector<GamefixId> gameFixes;
|
||||
std::vector<std::pair<SpeedhackId, int>> speedHacks;
|
||||
std::vector<std::string> memcardFilters;
|
||||
std::unordered_map<std::string, Patch> patches;
|
||||
|
||||
// Returns the list of memory card serials as a `/` delimited string
|
||||
std::string memcardFiltersAsString() const;
|
||||
bool findPatch(const std::string crc, Patch& patch) const;
|
||||
std::string MemcardFiltersAsString() const;
|
||||
const Patch* FindPatch(const std::string& crc) const;
|
||||
};
|
||||
|
||||
static const char* compatToString(GameDatabaseSchema::Compatibility compat);
|
||||
};
|
||||
|
||||
class IGameDatabase
|
||||
namespace GameDatabase
|
||||
{
|
||||
public:
|
||||
virtual bool initDatabase(std::ifstream& stream) = 0;
|
||||
virtual GameDatabaseSchema::GameEntry findGame(const std::string serial) = 0;
|
||||
virtual int numGames() = 0;
|
||||
};
|
||||
void EnsureLoaded();
|
||||
|
||||
class YamlGameDatabaseImpl : public IGameDatabase
|
||||
{
|
||||
public:
|
||||
bool initDatabase(std::ifstream& stream) override;
|
||||
GameDatabaseSchema::GameEntry findGame(const std::string serial) override;
|
||||
int numGames() override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, GameDatabaseSchema::GameEntry> gameDb;
|
||||
GameDatabaseSchema::GameEntry entryFromYaml(const std::string serial, const YAML::Node& node);
|
||||
|
||||
std::vector<std::string> convertMultiLineStringToVector(const std::string multiLineString);
|
||||
};
|
||||
|
||||
extern IGameDatabase* AppHost_GetGameDatabase();
|
||||
extern std::string strToLower(std::string str);
|
||||
extern bool compareStrNoCase(const std::string str1, const std::string str2);
|
||||
const GameDatabaseSchema::GameEntry* FindGame(const std::string& serial);
|
||||
}; // namespace GameDatabase
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct HostKeyEvent
|
||||
{
|
||||
@@ -29,3 +32,13 @@ struct HostKeyEvent
|
||||
Type type;
|
||||
u32 key;
|
||||
};
|
||||
|
||||
namespace Host
|
||||
{
|
||||
/// Reads a file from the resources directory of the application.
|
||||
/// This may be outside of the "normally" filesystem on platforms such as Mac.
|
||||
std::optional<std::vector<u8>> ReadResourceFile(const char* filename);
|
||||
|
||||
/// Reads a resource file file from the resources directory as a string.
|
||||
std::optional<std::string> ReadResourceFileToString(const char* filename);
|
||||
} // namespace Host
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user