Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/bios.cpp
4212 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]> and contributors.
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "bios.h"
5
#include "cpu_disasm.h"
6
#include "host.h"
7
#include "mips_encoder.h"
8
#include "settings.h"
9
10
#include "common/assert.h"
11
#include "common/error.h"
12
#include "common/file_system.h"
13
#include "common/log.h"
14
#include "common/md5_digest.h"
15
#include "common/path.h"
16
#include "common/string_util.h"
17
18
LOG_CHANNEL(BIOS);
19
20
namespace BIOS {
21
22
static constexpr ImageInfo::Hash MakeHashFromString(const char str[])
23
{
24
return StringUtil::ParseFixedHexString<ImageInfo::HASH_SIZE>(str);
25
}
26
27
// clang-format off
28
// Launch console BIOS is de-prioritized due to bugs.
29
// Late PAL is de-prioritized due to additional regional checks that break import booting without fast boot.
30
// PS2 is de-prioritized due to requiring a dynamic fast boot patch.
31
// PS2 PAL is further de-prioritized due to additional region checks.
32
static constexpr const ImageInfo s_image_info_by_hash[] = {
33
{"SCPH-1000, DTL-H1000 (v1.0)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 50, MakeHashFromString("239665b1a3dade1b5a52c06338011044")},
34
{"SCPH-1001, 5003, DTL-H1201, H3001 (v2.2 12-04-95 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("924e392ed05558ffdb115408c263dccf")},
35
{"SCPH-1002, DTL-H1002 (v2.0 05-10-95 E)", ConsoleRegion::PAL, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("54847e693405ffeb0359c6287434cbef")},
36
{"SCPH-1002, DTL-H1102 (v2.1 07-17-95 E)", ConsoleRegion::PAL, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("417b34706319da7cf001e76e40136c23")},
37
{"SCPH-1002, DTL-H1202, H3002 (v2.2 12-04-95 E)", ConsoleRegion::PAL, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("e2110b8a2b97a8e0b857a45d32f7e187")},
38
{"DTL-H1100 (v2.2 03-06-96 D)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 20, MakeHashFromString("ca5cfc321f916756e3f0effbfaeba13b")},
39
{"SCPH-3000, DTL-H1000H (v1.1 01-22-95)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("849515939161e62f6b866f6853006780")},
40
{"SCPH-1001, DTL-H1001 (v2.0 05-07-95 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("dc2b9bf8da62ec93e868cfd29f0d067d")},
41
{"SCPH-3500 (v2.1 07-17-95 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("cba733ceeff5aef5c32254f1d617fa62")},
42
{"SCPH-1001, DTL-H1101 (v2.1 07-17-95 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("da27e8b6dab242d8f91a9b25d80c63b8")},
43
{"SCPH-5000, DTL-H1200, H3000 (v2.2 12-04-95 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 5, MakeHashFromString("57a06303dfa9cf9351222dfcbb4a29d9")},
44
{"SCPH-5500 (v3.0 09-09-96 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 5, MakeHashFromString("8dd7d5296a650fac7319bce665a6a53c")},
45
{"SCPH-5501, 5503, 7003 (v3.0 11-18-96 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 5, MakeHashFromString("490f666e1afb15b7362b406ed1cea246")},
46
{"SCPH-5502, 5552 (v3.0 01-06-97 E)", ConsoleRegion::PAL, false, ImageInfo::FastBootPatch::Type1, 5, MakeHashFromString("32736f17079d0b2b7024407c39bd3050")},
47
{"SCPH-7000, 7500, 9000 (v4.0 08-18-97 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("8e4c14f567745eff2f0408c8129f72a6")},
48
{"SCPH-7000W (v4.1 11-14-97 A)", ConsoleRegion::NTSC_J, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("b84be139db3ee6cbd075630aa20a6553")},
49
{"SCPH-7001, 7501, 7503, 9001, 9003, 9903 (v4.1 12-16-97 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("1e68c231d0896b7eadcad1d7d8e76129")},
50
{"SCPH-7002, 7502, 9002 (v4.1 12-16-97 E)", ConsoleRegion::PAL, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("b9d9a0286c33dc6b7237bb13cd46fdee")},
51
{"SCPH-100 (v4.3 03-11-00 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("8abc1b549a4a80954addc48ef02c4521")},
52
{"SCPH-101 (v4.4 03-24-00 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("9a09ab7e49b422c007e6d54d7c49b965")},
53
{"SCPH-101 (v4.5 05-25-00 A)", ConsoleRegion::NTSC_U, false, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("6e3735ff4c7dc899ee98981385f6f3d0")},
54
{"SCPH-102 (v4.4 03-24-00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type1, 20, MakeHashFromString("b10f5e0e3d9eb60e5159690680b1e774")},
55
{"SCPH-102 (v4.5 05-25-00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type1, 20, MakeHashFromString("de93caec13d1a141a40a79f5c86168d6")},
56
{"SCPH-1000R (v4.5 05-25-00 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type1, 10, MakeHashFromString("476d68a94ccec3b9c8303bbd1daf2810")},
57
{"PS2, SCPH-18000 (v5.0 10-27-00 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("d8f485717a5237285e4d7c5f881b7f32")},
58
{"PS2, SCPH-30003 (v5.0 09-02-00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("71f50ef4f4e17c163c78908e16244f7d")},
59
{"PS2, DTL-H10000 (v5.0 01/17/00 T)", ConsoleRegion::Auto, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("32f2e4d5ff5ee11072a6bc45530f5765")},
60
{"PS2, SCPH-10000 (v5.0 01/17/00 T)", ConsoleRegion::Auto, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("acf4730ceb38ac9d8c7d8e21f2614600")},
61
{"PS2, DTL-H10000 (v5.0 02/17/00 T)", ConsoleRegion::Auto, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("acf9968c8f596d2b15f42272082513d1")},
62
{"PS2, SCPH-10000/SCPH-15000 (v5.0 02/17/00 T)", ConsoleRegion::Auto, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("b1459d7446c69e3e97e6ace3ae23dd1c")},
63
{"PS2, DTL-H10000 (v5.0 02/24/00 T)", ConsoleRegion::Auto, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("d3f1853a16c2ec18f3cd1ae655213308")},
64
{"PS2, DTL-H30001 (v5.0 07/27/00 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("63e6fd9b3c72e0d7b920e80cf76645cd")},
65
{"PS2, SCPH-30001 (v5.0 07/27/00 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("a20c97c02210f16678ca3010127caf36")},
66
{"PS2, SCPH-30001 (v5.0 09/02/00 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("8db2fbbac7413bf3e7154c1e0715e565")},
67
{"PS2, DTL-H30002 (v5.0 09/02/00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("91c87cb2f2eb6ce529a2360f80ce2457")},
68
{"PS2, DTL-H30102 (v5.0 09/02/00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("3016b3dd42148a67e2c048595ca4d7ce")},
69
{"PS2, SCPH-30002/SCPH-30003/SCPH-30004 (v5.0 09/02/00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("b7fa11e87d51752a98b38e3e691cbf17")},
70
{"PS2, SCPH-18000 (GH-003) (v5.0 10/27/00 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("f63bc530bd7ad7c026fcd6f7bd0d9525")},
71
{"PS2, SCPH-18000 (GH-008) (v5.0 10/27/00 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("cee06bd68c333fc5768244eae77e4495")},
72
{"PS2, DTL-H30101 (v5.0 12/28/00 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("0bf988e9c7aaa4c051805b0fa6eb3387")},
73
{"PS2, SCPH-30001/SCPH-35001 (v5.0 12/28/00 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("8accc3c49ac45f5ae2c5db0adc854633")},
74
{"PS2, DTL-H30102 (v5.0 12/28/00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("6f9a6feb749f0533aaae2cc45090b0ed")},
75
{"PS2, SCPH-30002/SCPH-30003/SCPH-30004/SCHP-35002/SCPH-35003/SCPH-35004 (v5.0 12/28/00 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("838544f12de9b0abc90811279ee223c8")},
76
{"PS2, DTL-H30000 (v5.0 01/18/01 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("bb6bbc850458fff08af30e969ffd0175")},
77
{"PS2, SCPH-30000/SCPH-35000 (v5.0 01/18/01 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("815ac991d8bc3b364696bead3457de7d")},
78
{"PS2, SCPH-30001R (v5.0 04/27/01 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("b107b5710042abe887c0f6175f6e94bb")},
79
{"PS2, SCPH-30000 (v5.0 04/27/01 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("ab55cceea548303c22c72570cfd4dd71")},
80
{"PS2, SCPH-30001R (v5.0 07/04/01 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("18bcaadb9ff74ed3add26cdf709fff2e")},
81
{"PS2, SCPH-30002R/SCPH-30003R/SCPH-30004R (v5.0 07/04/01 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("491209dd815ceee9de02dbbc408c06d6")},
82
{"PS2, SCPH-30001R (v5.0 10/04/01 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("7200a03d51cacc4c14fcdfdbc4898431")},
83
{"PS2, SCPH-30002R/SCPH-30003R/SCPH-30004R (v5.0 10/04/01 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("8359638e857c8bc18c3c18ac17d9cc3c")},
84
{"PS2, SCPH-30005R/SCPH-30006R/SCPH-30007R (v5.0 07/30/01 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("352d2ff9b3f68be7e6fa7e6dd8389346")},
85
{"PS2, SCPH-39001 (v5.0 02/07/02 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("d5ce2c7d119f563ce04bc04dbc3a323e")},
86
{"PS2, SCPH-39002/SCPH-39003/SCPH-39004 (v5.0 03/19/02 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("0d2228e6fd4fb639c9c39d077a9ec10c")},
87
{"PS2, SCPH-37000/SCPH-39000 (v5.0 04/26/02 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("72da56fccb8fcd77bba16d1b6f479914")},
88
{"PS2, SCPH-39008 (v5.0 04/26/02 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("5b1f47fbeb277c6be2fccdd6344ff2fd")},
89
{"PS2, SCPH-39005/SCPH-39006/SCPH-39007 (v5.0 04/26/02 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("315a4003535dfda689752cb25f24785c")},
90
{"PS2, DTL-H50000 (v5.0 02/06/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("54ecde087258557e2ddb5c3ddb004028")},
91
{"PS2, SCPH-50000/SCPH-55000 (v5.0 02/06/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("312ad4816c232a9606e56f946bc0678a")},
92
{"PS2, DTL-H50002 (v5.0 02/27/03 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("666018ffec65c5c7e04796081295c6c7")},
93
{"PS2, SCPH-50002/SCPH-50003/SCPH-50004 (v5.0 02/27/03 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("6e69920fa6eef8522a1d688a11e41bc6")},
94
{"PS2, DTL-H50001 (v5.0 03/25/03 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("eb960de68f0c0f7f9fa083e9f79d0360")},
95
{"PS2, SCPH-50001 (v5.0 03/25/03 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("8aa12ce243210128c5074552d3b86251")},
96
{"PS2, DTL-H50009 (v5.0 02/24/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("240d4c5ddd4b54069bdc4a3cd2faf99d")},
97
{"PS2, DESR-5000/DESR-5100/DESR-7000/DESR-7100 (v5.0 10/28/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("1c6cd089e6c83da618fbf2a081eb4888")},
98
{"PS2, SCPH-55000 (v5.0 06/23/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("463d87789c555a4a7604e97d7db545d1")},
99
{"PS2, DTL-H50001 (v5.0 06/23/03 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("ab9d49ad40ae49f19856ad187777b1b3")},
100
{"PS2, SCPH-50001/SCPH-50010 (v5.0 06/23/03 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("35461cecaa51712b300b2d6798825048")},
101
{"PS2, SCPH-50002/SCPH-50003/SCPH-50004 (v5.0 06/23/03 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("bd6415094e1ce9e05daabe85de807666")},
102
{"PS2, SCPH-50006/SCPH-50007 (v5.0 06/23/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("2e70ad008d4ec8549aada8002fdf42fb")},
103
{"PS2, SCPH-50005 (v5.0 06/23/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("50d5b97b57d8c9b6534adcb46c2027d4")},
104
{"PS2, SCPH-50008 (v5.0 06/23/03 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("b53d51edc7fc086685e31b811dc32aad")},
105
{"PS2, SCPH-50009 (v5.0 06/23/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("1b6e631b536247756287b916f9396872")},
106
{"PS2, SCPH-50000 (v5.0 08/22/03 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("00da1b177096cfd2532c8fa22b43e667")},
107
{"PS2, SCPH-50004 (v5.0 08/22/03 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("afde410bd026c16be605a1ae4bd651fd")},
108
{"PS2, SCPH-50011 (v5.0 03/29/04 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("81f4336c1de607dd0865011c0447052e")},
109
{"PS2, SCPH-70000 (v5.0 06/14/04 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("0eee5d1c779aa50e94edd168b4ebf42e")},
110
{"PS2, SCPH-70001/SCPH-70011/SCPH-70012 (v5.0 06/14/04 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("d333558cc14561c1fdc334c75d5f37b7")},
111
{"PS2, SCPH-70002/SCPH-70003/SCPH-70004/SCPH-70008 (v5.0 06/14/04 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("dc752f160044f2ed5fc1f4964db2a095")},
112
{"PS2, SCPH-70002 (v5.0 06/14/04 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("7ebb4fc5eab6f79a27d76ac9aad392b2")},
113
{"PS2, DTL-H70002 (v5.0 06/14/04 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("63ead1d74893bf7f36880af81f68a82d")},
114
{"PS2, SCPH-70005/SCPH-70006/SCPH-70007 (v5.0 06/14/04 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("3e3e030c0f600442fa05b94f87a1e238")},
115
{"PS2, DESR-5500/DESR-5700/DESR-7500/DESR-7700 (v5.0 09/17/04 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("1ad977bb539fc9448a08ab276a836bbc")},
116
{"PS2, DTL-H75000 (v5.0 06/20/05 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("bf0078ba5e19d57eae18047407f3b6e5")},
117
{"PS2, SCPH-75000 (v5.0 06/20/05 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("eb4f40fcf4911ede39c1bbfe91e7a89a")},
118
{"PS2, DTL-H75000A (v5.0 06/20/05 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("9959ad7a8685cad66206e7752ca23f8b")},
119
{"PS2, SCPH-75001/SCPH-75010 (v5.0 06/20/05 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("929a14baca1776b00869f983aa6e14d2")},
120
{"PS2, SCPH-75002/SCPH-75003/SCPH-75004/SCPH-75008 (v5.0 06/20/05 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("573f7d4a430c32b3cc0fd0c41e104bbd")},
121
{"PS2, SCPH-75006 (v5.0 06/20/05 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("df63a604e8bff5b0599bd1a6c2721bd0")},
122
{"PS2, SCPH-77000 (v5.0 02/10/06 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("5b1ba4bb914406fae75ab8e38901684d")},
123
{"PS2, SCPH-77001/SCPH-77010 (v5.0 02/10/06 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("cb801b7920a7d536ba07b6534d2433ca")},
124
{"PS2, SCPH-77002/SCPH-77003/SCPH-77004/SCPH-77008 (v5.0 02/10/06 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("af60e6d1a939019d55e5b330d24b1c25")},
125
{"PS2, SCPH-77006/SCPH-77007 (v5.0 02/10/06 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("549a66d0c698635ca9fa3ab012da7129")},
126
{"PS2, DTL-H90000 (v5.0 09/05/06 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("5e2014472c88f74f7547d8c2c60eca45")},
127
{"PS2, SCPH-79000/SCPH-90000 (v5.0 09/05/06 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("5de9d0d730ff1e7ad122806335332524")},
128
{"PS2, DTL-H90000 (v5.0 09/05/06 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("21fe4cad111f7dc0f9af29477057f88d")},
129
{"PS2, SCPH-79001/SCPH-79010/SCPH-90001 (v5.0 09/05/06 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("40c11c063b3b9409aa5e4058e984e30c")},
130
{"PS2, SCPH-79002/SCPH-79003/SCPH-79004/SCPH-79008/SCPH-90002/SCPH-90003/SCPH-90004 (v5.0 09/05/06 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("80bbb237a6af9c611df43b16b930b683")},
131
{"PS2, SCPH-79006/SCPH-79007/SCPH-90006/SCPH-90007 (v5.0 09/05/06 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("c37bce95d32b2be480f87dd32704e664")},
132
{"PS2, SCPH-90000 (v5.0 02/20/08 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100, MakeHashFromString("80ac46fa7e77b8ab4366e86948e54f83")},
133
{"PS2, SCPH-90001/SCPH-90010 (v5.0 02/20/08 A)", ConsoleRegion::NTSC_U, true, ImageInfo::FastBootPatch::Type2, 100 , MakeHashFromString("21038400dc633070a78ad53090c53017")},
134
{"PS2, SCPH-90002/SCPH-90003/SCPH-90004/SCPH-90008 (v5.0 02/20/08 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 100 , MakeHashFromString("dc69f0643a3030aaa4797501b483d6c4")},
135
{"PS2, SCPH-90005/SCPH-90006/SCPH-90007 (v5.0 02/20/08 J)", ConsoleRegion::NTSC_J, true, ImageInfo::FastBootPatch::Type2, 100 , MakeHashFromString("30d56e79d89fbddf10938fa67fe3f34e")},
136
{"PS2, KDL-22PX300 (v5.0 04/15/10 E)", ConsoleRegion::PAL, true, ImageInfo::FastBootPatch::Type2, 150, MakeHashFromString("93ea3bcee4252627919175ff1b16a1d9")},
137
};
138
// clang-format on
139
140
// OpenBIOS is separate, because there's no fixed hash for it. So just in case something collides with a hash of zero...
141
// which would be unlikely.
142
static constexpr const ImageInfo s_openbios_info = {
143
"OpenBIOS", ConsoleRegion::Auto, false, ImageInfo::FastBootPatch::Unsupported, 200, {}};
144
static constexpr const char s_openbios_signature[] = {'O', 'p', 'e', 'n', 'B', 'I', 'O', 'S'};
145
static constexpr u32 s_openbios_signature_offset = 0x78;
146
147
} // namespace BIOS
148
149
bool BIOS::ImageInfo::CanSlowBootDisc(DiscRegion disc_region) const
150
{
151
// BIOSes without region checks can slow boot anything, those with region checks can't slow boot mismatched games.
152
if (!region_check)
153
return true;
154
155
// Boot to BIOS for non-PS1 discs, e.g. audio.
156
if (disc_region == DiscRegion::NonPS1)
157
return true;
158
159
switch (region)
160
{
161
case ConsoleRegion::NTSC_J:
162
return (disc_region == DiscRegion::NTSC_J);
163
case ConsoleRegion::NTSC_U:
164
return (disc_region == DiscRegion::NTSC_U);
165
case ConsoleRegion::PAL:
166
return (disc_region == DiscRegion::PAL);
167
default:
168
return false;
169
}
170
}
171
172
TinyString BIOS::ImageInfo::GetHashString(const BIOS::ImageInfo::Hash& hash)
173
{
174
return TinyString::from_format(
175
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", hash[0],
176
hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12],
177
hash[13], hash[14], hash[15]);
178
}
179
180
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename, Error* error)
181
{
182
std::optional<BIOS::Image> ret;
183
184
auto fp = FileSystem::OpenManagedCFile(filename, "rb", error);
185
if (!fp)
186
{
187
Error::AddPrefixFmt(error, "Failed to open BIOS '{}': ", Path::GetFileName(filename));
188
return ret;
189
}
190
191
const u64 size = static_cast<u64>(FileSystem::FSize64(fp.get()));
192
if (size != BIOS_SIZE && size != BIOS_SIZE_PS2 && size != BIOS_SIZE_PS3)
193
{
194
Error::SetStringFmt(error, "BIOS image '{}' size mismatch, expecting either {} or {} bytes but got {} bytes",
195
Path::GetFileName(filename), static_cast<unsigned>(BIOS_SIZE),
196
static_cast<unsigned>(BIOS_SIZE_PS2), size);
197
return ret;
198
}
199
200
// We want to hash the whole file. That means reading the whole thing in, if it's a larger BIOS (PS2).
201
std::optional<DynamicHeapArray<u8>> data = FileSystem::ReadBinaryFile(fp.get(), error);
202
if (!data.has_value() || data->size() < BIOS_SIZE)
203
return ret;
204
205
ret = BIOS::Image();
206
ret->hash = MD5Digest::HashData(data.value());
207
208
// But only copy the first 512KB, since that's all that's mapped.
209
ret->data = std::move(data.value());
210
ret->data.resize(BIOS_SIZE);
211
ret->info = GetInfoForHash(ret->data, ret->hash);
212
213
DEV_LOG("Hash for BIOS '{}': {}", FileSystem::GetDisplayNameFromPath(filename), ImageInfo::GetHashString(ret->hash));
214
return ret;
215
}
216
217
const BIOS::ImageInfo* BIOS::GetInfoForHash(const std::span<const u8> image, const ImageInfo::Hash& hash)
218
{
219
// check for openbios
220
if (image.size() >= (s_openbios_signature_offset + std::size(s_openbios_signature)) &&
221
std::memcmp(&image[s_openbios_signature_offset], s_openbios_signature, std::size(s_openbios_signature)) == 0)
222
{
223
return &s_openbios_info;
224
}
225
226
for (const ImageInfo& ii : s_image_info_by_hash)
227
{
228
if (ii.hash == hash)
229
return &ii;
230
}
231
232
WARNING_LOG("Unknown BIOS hash: {}", ImageInfo::GetHashString(hash));
233
return nullptr;
234
}
235
236
bool BIOS::IsValidBIOSForRegion(ConsoleRegion console_region, ConsoleRegion bios_region)
237
{
238
return (console_region == ConsoleRegion::Auto || bios_region == ConsoleRegion::Auto || bios_region == console_region);
239
}
240
241
bool BIOS::PatchBIOSFastBoot(u8* image, u32 image_size, ImageInfo::FastBootPatch type)
242
{
243
// Replace the shell entry point with a return back to the bootstrap.
244
static constexpr const u32 shell_replacement[] = {
245
// lui at, 1f80
246
// lui t2, 0300h
247
// sw t2, 1814h(at) ; turn the display on
248
// jr ra
249
// nop
250
Mips::Encoder::lui(Mips::Encoder::Reg::AT, 0x1F80),
251
Mips::Encoder::lui(Mips::Encoder::Reg::T2, 0x0300),
252
Mips::Encoder::sw(Mips::Encoder::Reg::T2, 0x1814, Mips::Encoder::Reg::AT),
253
Mips::Encoder::jr(Mips::Encoder::Reg::RA),
254
Mips::Encoder::nop(),
255
};
256
257
// Type1/Type2 use the same shell replacement patch, but for historical reasons we replace the actual shell code for
258
// Type 1, and the routine that calls the decompressor for Part 2.
259
u32 patch_offset;
260
if (type == ImageInfo::FastBootPatch::Type1)
261
{
262
// Try the "new" fast boot (Type 1B), where we patch the routine that copies/decompresses the shell.
263
// This saves approximately 2 seconds of fast boot time, because the memcpy() executes out of uncached ROM...
264
static constexpr const char* search_pattern = "e0 ff bd 27" // add sp, sp, -20
265
"1c 00 bf af" // sw ra, 0xc(sp)
266
"20 00 a4 af" // sw a0, 0x20(sp)
267
"?? ?? 05 3c" // lui a1, 0xbfc1
268
"?? ?? 06 3c" // lui a2, 0x6
269
"?? ?? c6 34" // ori a2, a2, 0x7ff0
270
"?? ?? a5 34" // ori a1, a1, 0x8000
271
"?? ?? ?? 0f"; // jal 0xbfc02b50
272
const std::optional<size_t> offset =
273
StringUtil::BytePatternSearch(std::span<const u8>(image, image_size), search_pattern);
274
if (offset.has_value())
275
{
276
patch_offset = static_cast<u32>(offset.value());
277
VERBOSE_LOG("Found Type 1B pattern at offset 0x{:08X}", patch_offset);
278
}
279
else
280
{
281
patch_offset = 0x18000;
282
INFO_LOG("Using Type 1A fast boot patch at offset 0x{:08X}.", patch_offset);
283
}
284
}
285
else if (type == ImageInfo::FastBootPatch::Type2)
286
{
287
static constexpr const char* search_pattern = "d8 ff bd 27" // add sp, sp, -28
288
"1c 00 bf af" // sw ra, 0xc(sp)
289
"28 00 a4 af" // sw a0, 0x28(sp)
290
"?? ?? 06 3c" // lui a2, 0xbfc6
291
"?? ?? c6 24" // addiu a2, -0x6bb8
292
"c0 bf 04 3c" // lui a0, 0xbfc0
293
"?? ?? ?? 0f"; // jal 0xbfc58720
294
constexpr u32 FALLBACK_OFFSET = 0x00052AFC;
295
const std::optional<size_t> offset =
296
StringUtil::BytePatternSearch(std::span<const u8>(image, image_size), search_pattern);
297
if (offset.has_value())
298
{
299
patch_offset = static_cast<u32>(offset.value());
300
VERBOSE_LOG("Found Type 2 pattern at offset 0x{:08X}", patch_offset);
301
}
302
else
303
{
304
patch_offset = FALLBACK_OFFSET;
305
WARNING_LOG("Failed to find Type 2 pattern in BIOS image. Using fallback offset of 0x{:08X}", patch_offset);
306
}
307
}
308
else [[unlikely]]
309
{
310
return false;
311
}
312
313
Assert((patch_offset + sizeof(shell_replacement)) <= image_size);
314
std::memcpy(image + patch_offset, shell_replacement, sizeof(shell_replacement));
315
return true;
316
}
317
318
bool BIOS::IsValidPSExeHeader(const PSEXEHeader& header, size_t file_size)
319
{
320
static constexpr char expected_id[] = {'P', 'S', '-', 'X', ' ', 'E', 'X', 'E'};
321
if (file_size < sizeof(expected_id) || std::memcmp(header.id, expected_id, sizeof(expected_id)) != 0)
322
return false;
323
324
if ((header.file_size + sizeof(PSEXEHeader)) > file_size)
325
{
326
WARNING_LOG("Incorrect file size in PS-EXE header: {} bytes should not be greater than {} bytes", header.file_size,
327
file_size - sizeof(PSEXEHeader));
328
}
329
330
return true;
331
}
332
333
DiscRegion BIOS::GetPSExeDiscRegion(const PSEXEHeader& header)
334
{
335
static constexpr char ntsc_u_id[] = "Sony Computer Entertainment Inc. for North America area";
336
static constexpr char ntsc_j_id[] = "Sony Computer Entertainment Inc. for Japan area";
337
static constexpr char pal_id[] = "Sony Computer Entertainment Inc. for Europe area";
338
339
if (std::memcmp(header.marker, ntsc_u_id, sizeof(ntsc_u_id) - 1) == 0)
340
return DiscRegion::NTSC_U;
341
else if (std::memcmp(header.marker, ntsc_j_id, sizeof(ntsc_j_id) - 1) == 0)
342
return DiscRegion::NTSC_J;
343
else if (std::memcmp(header.marker, pal_id, sizeof(pal_id) - 1) == 0)
344
return DiscRegion::PAL;
345
else
346
return DiscRegion::Other;
347
}
348
349
std::optional<BIOS::Image> BIOS::GetBIOSImage(ConsoleRegion region, Error* error)
350
{
351
std::string bios_name;
352
switch (region)
353
{
354
case ConsoleRegion::NTSC_J:
355
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCJ", "");
356
break;
357
358
case ConsoleRegion::PAL:
359
bios_name = Host::GetStringSettingValue("BIOS", "PathPAL", "");
360
break;
361
362
case ConsoleRegion::NTSC_U:
363
default:
364
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCU", "");
365
break;
366
}
367
368
std::optional<Image> image;
369
370
if (bios_name.empty())
371
{
372
// auto-detect
373
image = FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str(), error);
374
}
375
else
376
{
377
// try the configured path
378
image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str(), error);
379
}
380
381
// verify region
382
if (image.has_value() && (!image->info || !IsValidBIOSForRegion(region, image->info->region)))
383
{
384
WARNING_LOG("BIOS region {} does not match requested region {}. This may cause issues.",
385
image->info ? Settings::GetConsoleRegionName(image->info->region) : "UNKNOWN",
386
Settings::GetConsoleRegionName(region));
387
}
388
389
return image;
390
}
391
392
std::optional<BIOS::Image> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error)
393
{
394
INFO_LOG("Searching for a {} BIOS in '{}'...", Settings::GetConsoleRegionName(region), directory);
395
396
FileSystem::FindResultsArray results;
397
FileSystem::FindFiles(
398
directory, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
399
400
std::optional<Image> image;
401
std::string image_path;
402
bool image_region_match = false;
403
404
for (const FILESYSTEM_FIND_DATA& fd : results)
405
{
406
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
407
{
408
WARNING_LOG("Skipping '{}': incorrect size", fd.FileName.c_str());
409
continue;
410
}
411
412
std::string full_path(Path::Combine(directory, fd.FileName));
413
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
414
if (!found_image.has_value())
415
continue;
416
417
// don't let an unknown bios take precedence over a known one
418
const bool region_match = (found_image->info && IsValidBIOSForRegion(region, found_image->info->region));
419
if (image.has_value() &&
420
((image->info && !found_image->info) || (image_region_match && !region_match) ||
421
(image->info && found_image->info && image->info->priority < found_image->info->priority)))
422
{
423
continue;
424
}
425
426
image = std::move(found_image);
427
image_path = std::move(full_path);
428
image_region_match = region_match;
429
}
430
431
if (!image.has_value())
432
{
433
#ifndef __ANDROID__
434
Error::SetStringFmt(
435
error,
436
TRANSLATE_FS("System", "No BIOS image found for {} region.\n\nDuckStation requires a PS1 or PS2 BIOS in order to "
437
"run.\n\nFor legal reasons, you *must* obtain a BIOS from an actual PS1 unit that you own "
438
"(borrowing doesn't count).\n\nOnce dumped, this BIOS image should be placed in the bios "
439
"folder within the data directory (Tools Menu -> Open Data Directory)."),
440
Settings::GetConsoleRegionName(region));
441
#else
442
Error::SetStringFmt(error, TRANSLATE_FS("System", "No BIOS image found for {} region."),
443
Settings::GetConsoleRegionName(region));
444
#endif
445
return image;
446
}
447
448
if (!image->info)
449
WARNING_LOG("Using unknown BIOS '{}'. This may crash.", Path::GetFileName(image_path));
450
451
return image;
452
}
453
454
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
455
{
456
std::vector<std::pair<std::string, const ImageInfo*>> results;
457
458
FileSystem::FindResultsArray files;
459
FileSystem::FindFiles(directory, "*",
460
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files);
461
462
for (FILESYSTEM_FIND_DATA& fd : files)
463
{
464
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
465
continue;
466
467
std::string full_path(Path::Combine(directory, fd.FileName));
468
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
469
if (!found_image)
470
continue;
471
472
results.emplace_back(std::move(fd.FileName), found_image->info);
473
}
474
475
return results;
476
}
477
478
bool BIOS::HasAnyBIOSImages()
479
{
480
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str(), nullptr).has_value();
481
}
482
483