diff --git a/Aerofoil.sln b/Aerofoil.sln index 964ab52..3391fed 100644 --- a/Aerofoil.sln +++ b/Aerofoil.sln @@ -47,98 +47,196 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unpacktool", "unpacktool\un EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ReleasePackageInstaller", "ReleasePackageInstaller\ReleasePackageInstaller.wixproj", "{D26BD501-28A7-4849-8130-FB5EA0A2B82F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsUnicodeToolShim", "WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj", "{15009625-1120-405E-8BBA-69A16CD6713D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {45B1B18C-C846-4044-9206-74F58DFC5E88}.Debug|x64.ActiveCfg = Debug|x64 {45B1B18C-C846-4044-9206-74F58DFC5E88}.Debug|x64.Build.0 = Debug|x64 + {45B1B18C-C846-4044-9206-74F58DFC5E88}.Debug|x86.ActiveCfg = Debug|Win32 + {45B1B18C-C846-4044-9206-74F58DFC5E88}.Debug|x86.Build.0 = Debug|Win32 {45B1B18C-C846-4044-9206-74F58DFC5E88}.Release|x64.ActiveCfg = Release|x64 {45B1B18C-C846-4044-9206-74F58DFC5E88}.Release|x64.Build.0 = Release|x64 + {45B1B18C-C846-4044-9206-74F58DFC5E88}.Release|x86.ActiveCfg = Release|Win32 + {45B1B18C-C846-4044-9206-74F58DFC5E88}.Release|x86.Build.0 = Release|Win32 {6EC62B0F-9353-40A4-A510-3788F1368B33}.Debug|x64.ActiveCfg = Debug|x64 {6EC62B0F-9353-40A4-A510-3788F1368B33}.Debug|x64.Build.0 = Debug|x64 + {6EC62B0F-9353-40A4-A510-3788F1368B33}.Debug|x86.ActiveCfg = Debug|Win32 + {6EC62B0F-9353-40A4-A510-3788F1368B33}.Debug|x86.Build.0 = Debug|Win32 {6EC62B0F-9353-40A4-A510-3788F1368B33}.Release|x64.ActiveCfg = Release|x64 {6EC62B0F-9353-40A4-A510-3788F1368B33}.Release|x64.Build.0 = Release|x64 + {6EC62B0F-9353-40A4-A510-3788F1368B33}.Release|x86.ActiveCfg = Release|Win32 + {6EC62B0F-9353-40A4-A510-3788F1368B33}.Release|x86.Build.0 = Release|Win32 {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Debug|x64.ActiveCfg = Debug|x64 {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Debug|x64.Build.0 = Debug|x64 + {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Debug|x86.ActiveCfg = Debug|Win32 + {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Debug|x86.Build.0 = Debug|Win32 {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Release|x64.ActiveCfg = Release|x64 {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Release|x64.Build.0 = Release|x64 + {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Release|x86.ActiveCfg = Release|Win32 + {2FF15659-5C72-48B8-B55B-3C658E4125B5}.Release|x86.Build.0 = Release|Win32 {6233C3F2-5781-488E-B190-4FA8836F5A77}.Debug|x64.ActiveCfg = Debug|x64 {6233C3F2-5781-488E-B190-4FA8836F5A77}.Debug|x64.Build.0 = Debug|x64 + {6233C3F2-5781-488E-B190-4FA8836F5A77}.Debug|x86.ActiveCfg = Debug|Win32 + {6233C3F2-5781-488E-B190-4FA8836F5A77}.Debug|x86.Build.0 = Debug|Win32 {6233C3F2-5781-488E-B190-4FA8836F5A77}.Release|x64.ActiveCfg = Release|x64 {6233C3F2-5781-488E-B190-4FA8836F5A77}.Release|x64.Build.0 = Release|x64 + {6233C3F2-5781-488E-B190-4FA8836F5A77}.Release|x86.ActiveCfg = Release|Win32 + {6233C3F2-5781-488E-B190-4FA8836F5A77}.Release|x86.Build.0 = Release|Win32 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Debug|x64.ActiveCfg = Debug|x64 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Debug|x64.Build.0 = Debug|x64 + {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Debug|x86.ActiveCfg = Debug|Win32 + {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Debug|x86.Build.0 = Debug|Win32 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x64.ActiveCfg = Release|x64 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x64.Build.0 = Release|x64 + {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x86.ActiveCfg = Release|Win32 + {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x86.Build.0 = Release|Win32 {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.ActiveCfg = Debug|x64 {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.Build.0 = Debug|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x86.ActiveCfg = Debug|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x86.Build.0 = Debug|Win32 {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.ActiveCfg = Release|x64 {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.Build.0 = Release|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x86.ActiveCfg = Release|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x86.Build.0 = Release|Win32 {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.ActiveCfg = Debug|x64 {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.Build.0 = Debug|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x86.ActiveCfg = Debug|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x86.Build.0 = Debug|Win32 {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x64.ActiveCfg = Release|x64 {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x64.Build.0 = Release|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x86.ActiveCfg = Release|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x86.Build.0 = Release|Win32 {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Debug|x64.ActiveCfg = Debug|x64 {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Debug|x64.Build.0 = Debug|x64 + {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Debug|x86.ActiveCfg = Debug|Win32 + {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Debug|x86.Build.0 = Debug|Win32 {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Release|x64.ActiveCfg = Release|x64 {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Release|x64.Build.0 = Release|x64 + {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Release|x86.ActiveCfg = Release|Win32 + {A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}.Release|x86.Build.0 = Release|Win32 {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Debug|x64.ActiveCfg = Debug|x64 {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Debug|x64.Build.0 = Debug|x64 + {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Debug|x86.ActiveCfg = Debug|Win32 + {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Debug|x86.Build.0 = Debug|Win32 {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Release|x64.ActiveCfg = Release|x64 {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Release|x64.Build.0 = Release|x64 + {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Release|x86.ActiveCfg = Release|Win32 + {487216D8-16BA-4B4C-B5BF-43FEEDFEE03A}.Release|x86.Build.0 = Release|Win32 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Debug|x64.ActiveCfg = Debug|x64 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Debug|x64.Build.0 = Debug|x64 + {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Debug|x86.ActiveCfg = Debug|Win32 + {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Debug|x86.Build.0 = Debug|Win32 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x64.ActiveCfg = Release|x64 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x64.Build.0 = Release|x64 + {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x86.ActiveCfg = Release|Win32 + {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x86.Build.0 = Release|Win32 {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x64.ActiveCfg = Debug|x64 {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x64.Build.0 = Debug|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x86.ActiveCfg = Debug|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x86.Build.0 = Debug|Win32 {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x64.ActiveCfg = Release|x64 {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x64.Build.0 = Release|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x86.ActiveCfg = Release|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x86.Build.0 = Release|Win32 {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Debug|x64.ActiveCfg = Debug|x64 {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Debug|x64.Build.0 = Debug|x64 + {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Debug|x86.ActiveCfg = Debug|Win32 + {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Debug|x86.Build.0 = Debug|Win32 {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Release|x64.ActiveCfg = Release|x64 {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Release|x64.Build.0 = Release|x64 + {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Release|x86.ActiveCfg = Release|Win32 + {FFC961AC-55B4-4A38-A83E-06AE98F59ACC}.Release|x86.Build.0 = Release|Win32 {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Debug|x64.ActiveCfg = Debug|x64 {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Debug|x64.Build.0 = Debug|x64 + {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Debug|x86.ActiveCfg = Debug|Win32 + {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Debug|x86.Build.0 = Debug|Win32 {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Release|x64.ActiveCfg = Release|x64 {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Release|x64.Build.0 = Release|x64 + {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Release|x86.ActiveCfg = Release|Win32 + {17B96F07-EF92-47CD-95A5-8E6EE38AB564}.Release|x86.Build.0 = Release|Win32 {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Debug|x64.ActiveCfg = Debug|x64 {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Debug|x64.Build.0 = Debug|x64 + {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Debug|x86.ActiveCfg = Debug|Win32 + {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Debug|x86.Build.0 = Debug|Win32 {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Release|x64.ActiveCfg = Release|x64 {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Release|x64.Build.0 = Release|x64 + {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Release|x86.ActiveCfg = Release|Win32 + {0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}.Release|x86.Build.0 = Release|Win32 {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Debug|x64.ActiveCfg = Debug|x64 {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Debug|x64.Build.0 = Debug|x64 + {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Debug|x86.ActiveCfg = Debug|Win32 + {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Debug|x86.Build.0 = Debug|Win32 {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Release|x64.ActiveCfg = Release|x64 {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Release|x64.Build.0 = Release|x64 + {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Release|x86.ActiveCfg = Release|Win32 + {07351A8E-1F79-42C9-BBAB-31F071EAA99E}.Release|x86.Build.0 = Release|Win32 {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Debug|x64.ActiveCfg = Debug|x64 {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Debug|x64.Build.0 = Debug|x64 + {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Debug|x86.ActiveCfg = Debug|Win32 + {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Debug|x86.Build.0 = Debug|Win32 {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Release|x64.ActiveCfg = Release|x64 {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Release|x64.Build.0 = Release|x64 + {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Release|x86.ActiveCfg = Release|Win32 + {27B7CA46-ED23-45C2-BF5F-0C126D81AEBF}.Release|x86.Build.0 = Release|Win32 {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Debug|x64.ActiveCfg = Debug|x64 {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Debug|x64.Build.0 = Debug|x64 + {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Debug|x86.ActiveCfg = Debug|Win32 + {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Debug|x86.Build.0 = Debug|Win32 {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Release|x64.ActiveCfg = Release|x64 {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Release|x64.Build.0 = Release|x64 + {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Release|x86.ActiveCfg = Release|Win32 + {6AE5C85E-6631-4A12-97A0-A05F812FE9CA}.Release|x86.Build.0 = Release|Win32 {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Debug|x64.ActiveCfg = Debug|x64 {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Debug|x64.Build.0 = Debug|x64 + {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Debug|x86.ActiveCfg = Debug|Win32 + {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Debug|x86.Build.0 = Debug|Win32 {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Release|x64.ActiveCfg = Release|x64 {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Release|x64.Build.0 = Release|x64 + {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Release|x86.ActiveCfg = Release|Win32 + {D7BFE702-0667-4155-9B0B-2A54DF9AE60B}.Release|x86.Build.0 = Release|Win32 {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Debug|x64.ActiveCfg = Debug|x64 {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Debug|x64.Build.0 = Debug|x64 + {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Debug|x86.ActiveCfg = Debug|Win32 + {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Debug|x86.Build.0 = Debug|Win32 {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Release|x64.ActiveCfg = Release|x64 {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Release|x64.Build.0 = Release|x64 + {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Release|x86.ActiveCfg = Release|Win32 + {9023DF2F-A33D-485A-B13D-0973348B2F9B}.Release|x86.Build.0 = Release|Win32 {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Debug|x64.ActiveCfg = Debug|x64 {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Debug|x64.Build.0 = Debug|x64 + {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Debug|x86.ActiveCfg = Debug|Win32 + {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Debug|x86.Build.0 = Debug|Win32 {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x64.ActiveCfg = Release|x64 {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x64.Build.0 = Release|x64 + {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x86.ActiveCfg = Release|Win32 + {89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x86.Build.0 = Release|Win32 {A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x64.ActiveCfg = Debug|x64 {A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x64.Build.0 = Debug|x64 + {A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x86.ActiveCfg = Debug|Win32 + {A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x86.Build.0 = Debug|Win32 {A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x64.ActiveCfg = Release|x64 {A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x64.Build.0 = Release|x64 + {A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x86.ActiveCfg = Release|Win32 + {A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x86.Build.0 = Release|Win32 {D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Debug|x64.ActiveCfg = Debug|x64 + {D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Debug|x86.ActiveCfg = Debug|x64 {D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Release|x64.ActiveCfg = Release|x64 + {D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Release|x86.ActiveCfg = Release|x64 + {15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x64.ActiveCfg = Debug|x64 + {15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x64.Build.0 = Debug|x64 + {15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x86.ActiveCfg = Debug|Win32 + {15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x86.Build.0 = Debug|Win32 + {15009625-1120-405E-8BBA-69A16CD6713D}.Release|x64.ActiveCfg = Release|x64 + {15009625-1120-405E-8BBA-69A16CD6713D}.Release|x64.Build.0 = Release|x64 + {15009625-1120-405E-8BBA-69A16CD6713D}.Release|x86.ActiveCfg = Release|Win32 + {15009625-1120-405E-8BBA-69A16CD6713D}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Documentation/picterrors.txt b/Documentation/picterrors.txt new file mode 100644 index 0000000..1d2ee99 --- /dev/null +++ b/Documentation/picterrors.txt @@ -0,0 +1,27 @@ +Many Glider PRO houses, including the bundled ones, contain images in QuickDraw +PICT format in the game resource data. + +Additionally, some PICT features are impossible to support because they +require copyrighted pattern sets and fonts that are bundled with the +operating system. + +My goal is to support as many community houses as possible, but there is a +point where supporting the format is more work than just manually converting +the resources on a Macintosh machine or emulator. + +All of the remaining known unsupported features necessary to decode the +remaining cases are only used by one house that I'm aware of each. + +Known error codes: + +Code 5 subcode 25: Strange image channel layout +Code 8 subcode 145: Unsupported BitsRgn opcode. +Code 8 subcode 40960: Undocumented opcode, possibly a format parsing error. + +Code 8 subcode 34: Unsupported ShortLine opcode. (PICT contains vector graphics.) +Code 8 subcode 35: Unsupported ShortLineFrom opcode. (PICT contains vector graphics.) +Code 8 subcode 49: Unsupported PaintRect opcode. (PICT contains vector graphics.) +Code 8 subcode 51: Unsupported PaintOval opcode. (PICT contains vector graphics.) +Code 8 subcode 129: paintRgn opcode. (PICT contains vector graphics.) +Code 8 subcode 33280: Unsupported CompressedQuickTime opcode. (PICT contains a QuickTime frame.) +Code 8 subcode 33281: Unsupported UncompressedQuickTime opcode. (PICT contains a QuickTime frame.) diff --git a/Documentation/userhouses.txt b/Documentation/userhouses.txt index 4266460..b73cff1 100644 --- a/Documentation/userhouses.txt +++ b/Documentation/userhouses.txt @@ -55,4 +55,30 @@ Some old QuickTime movies contain movie tracking information in the movie resource fork, in which case you need to merge them into the data for modern utilities to read them. You can use the "flattenmov" tool to do this: Pass it the data (.gpd) and resource (.gpr) data files for a movie, and it will -merge them into a single .mov file. \ No newline at end of file +merge them into a single .mov file. + + +BATCH CONVERSION +------------------------------------------------------------------------------ +To batch-convert house resource files, use gpr2gpa with a path that ends +with an asterisk (*) and omit the output filename. + + +SECURITY CONSIDERATIONS +------------------------------------------------------------------------------ +Glider PRO didn't really do any validation of houses. Currently, Aerofoil +doesn't do any additional validation either, and it's possible that invalid +house data may lead to crashes or even remote code execution. + +I will be doing a hardening pass on the loader for the 1.1 release. Until +then, please only load houses from trusted sources. + +Also, Glider PRO houses were able to take advantage of a resource overlaying +feature of the Macintosh operating system, where a resource being present in +the house file with the same ID as an application resource would cause the +resource to override the application resource. + +Aerofoil's resource loader is much more restrictive: Currently, only +backgrounds, audio triggers, and icons may load from the house data. This +restriction will be loosened in the 1.1 release to allow resources to be +overrided if I can confirm that it's safe to override them. \ No newline at end of file diff --git a/PictChecker/PictChecker.cpp b/PictChecker/PictChecker.cpp index bd412d7..b2ddc87 100644 --- a/PictChecker/PictChecker.cpp +++ b/PictChecker/PictChecker.cpp @@ -1017,6 +1017,11 @@ public: m_outputIndexStart = firstRow * m_width + firstCol; } + + bool EmitQTContent(IOStream *stream, uint32_t dataSize, bool isCompressed) override + { + return false; + } void BlitScanlineAndAdvance(const void *data) override { diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index f190723..3ed82ba 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -87,6 +87,15 @@ Rect BERect::ToRect() const return rect; } +Point BEPoint::ToPoint() const +{ + Point point; + point.h = this->h; + point.v = this->v; + + return point; +} + void HideCursor() { PortabilityLayer::HostDisplayDriver::GetInstance()->SetStandardCursor(EGpStandardCursors::kHidden); diff --git a/PortabilityLayer/QDPictDecoder.cpp b/PortabilityLayer/QDPictDecoder.cpp index 9f95309..141c775 100644 --- a/PortabilityLayer/QDPictDecoder.cpp +++ b/PortabilityLayer/QDPictDecoder.cpp @@ -12,6 +12,28 @@ #include #include +struct QDPictDecodeState +{ + PortabilityLayer::RGBAColor m_foreColor; + PortabilityLayer::RGBAColor m_backColor; + Point m_penPos; + Point m_penSize; + Point m_origin; + uint16_t m_penMode; + uint8_t m_penPattern[8]; + + QDPictDecodeState() + { + m_foreColor = PortabilityLayer::RGBAColor::Create(0, 0, 0, 255); + m_backColor = PortabilityLayer::RGBAColor::Create(255, 255, 255, 255); + m_penPos = Point::Create(0, 0); + m_origin = Point::Create(0, 0); + m_penSize = Point::Create(1, 1); + for (int i = 0; i < 8; i++) + m_penPattern[i] = 0xff; + m_penMode = 0; + } +}; namespace PortabilityLayer { @@ -23,9 +45,13 @@ namespace PortabilityLayer bool QDPictDecoder::DecodePict(IOStream *stream, QDPictEmitContext *emitContext) { QDPictHeader header; + QDPictDecodeState decodeState; if (!header.Load(stream)) + { + emitContext->ReportError(QDPictEmitContext::kMissingHeader, 0); return false; + } emitContext->SpecifyFrame(header.GetFrame()); @@ -64,7 +90,10 @@ namespace PortabilityLayer break; case QDOpcodes::kClipRegion: if (stream->Read(scratchBytes, 10) != 10 || scratchBytes[0] != 0 || scratchBytes[1] != 10) - return false; // Unknown format region + { + emitContext->ReportError(QDPictEmitContext::kUnsupportedClipRegionFormat, 0); + return false; + } GP_STATIC_ASSERT(sizeof(scratchBERect) == 8); @@ -72,17 +101,26 @@ namespace PortabilityLayer scratchRect = scratchBERect.ToRect(); if (!scratchRect.IsValid()) + { + emitContext->ReportError(QDPictEmitContext::kInvalidRegionRect, 0); return false; + } break; case QDOpcodes::kShortComment: if (!stream->SeekCurrent(2)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); return false; + } break; case QDOpcodes::kLongComment: { if (stream->Read(scratchBytes, 4) != 4) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); return false; + } const uint16_t commentKind = (scratchBytes[0] << 8) | scratchBytes[1]; const uint16_t commentSize = (scratchBytes[2] << 8) | scratchBytes[3]; @@ -92,41 +130,156 @@ namespace PortabilityLayer } break; case QDOpcodes::kBitsRect: - rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), false, false, false, activeFrame, emitContext); + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), false, false, false, activeFrame, decodeState.m_origin, emitContext); if (rasterOpErrorCode) + { + emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode); return false; + } break; case QDOpcodes::kPackBitsRect: - rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, false, activeFrame, emitContext); + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, false, activeFrame, decodeState.m_origin, emitContext); if (rasterOpErrorCode) + { + emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode); return false; + } break; case QDOpcodes::kPackBitsRgn: - rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, false, activeFrame, emitContext); + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, false, activeFrame, decodeState.m_origin, emitContext); if (rasterOpErrorCode) + { + emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode); return false; + } break; case QDOpcodes::kDirectBitsRect: - rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, true, activeFrame, emitContext); + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, true, activeFrame, decodeState.m_origin, emitContext); if (rasterOpErrorCode) + { + emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode); return false; + } break; case QDOpcodes::kDirectBitsRgn: - rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, true, activeFrame, emitContext); + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, true, activeFrame, decodeState.m_origin, emitContext); if (rasterOpErrorCode) + { + emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode); return false; + } break; case QDOpcodes::kDefaultHilite: break; - case QDOpcodes::kOpColor: if (!stream->SeekCurrent(6)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); return false; + } break; case QDOpcodes::kEndOfPicture: finished = true; break; + case QDOpcodes::kPenSize: + { + BEPoint point; + + if (!stream->Read(&point, 4)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + decodeState.m_penSize = point.ToPoint(); + } + break; + case QDOpcodes::kPenPattern: + { + if (!stream->Read(decodeState.m_penPattern, 8)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + } + break; + case QDOpcodes::kPenMode: + { + BEUInt16_t penMode; + + if (!stream->Read(&penMode, 2)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + decodeState.m_penMode = penMode; + } + break; + case QDOpcodes::kOrigin: + { + // NOTE: This is intentionally not the same order as Point + BEInt16_t dxdy[2]; + + if (!stream->Read(&dxdy, 4)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + decodeState.m_origin.h += dxdy[0]; + decodeState.m_origin.v += dxdy[1]; + } + break; + case QDOpcodes::kOpColor: // ??? Not sure what the difference between these two is + case QDOpcodes::kRGBForeColor: + { + uint8_t rgbBytes[6]; + + if (!stream->Read(rgbBytes, 6)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + decodeState.m_foreColor = RGBAColor::Create(rgbBytes[0], rgbBytes[2], rgbBytes[4], 255); + } + break; + case QDOpcodes::kRGBBackColor: + { + uint8_t rgbBytes[6]; + + if (!stream->Read(rgbBytes, 6)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + decodeState.m_backColor = RGBAColor::Create(rgbBytes[0], rgbBytes[2], rgbBytes[4], 255); + } + break; + + case QDOpcodes::kCompressedQT: + case QDOpcodes::kUncompressedQT: + { + BEUInt32_t dataSize; + + if (!stream->Read(&dataSize, 4)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + + if (!emitContext->EmitQTContent(stream, dataSize, opcode == QDOpcodes::kCompressedQT)) + { + emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode); + return false; + } + } + break; + default: // Unknown opcode + emitContext->ReportError(QDPictEmitContext::kUnsupportedOpcode, opcode); return false; } @@ -135,7 +288,7 @@ namespace PortabilityLayer } } - int QDPictDecoder::ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &constraintRect, QDPictEmitContext *context) + int QDPictDecoder::ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &constraintRect, const Point &origin, QDPictEmitContext *context) { uint16_t rowSizeBytes = 0; @@ -415,6 +568,12 @@ namespace PortabilityLayer if (srcRect.bottom - srcRect.top != destRect.bottom - destRect.top) return 39; + // Offset by origin + destRect.left -= origin.h; + destRect.right -= origin.v; + destRect.top -= origin.v; + destRect.bottom -= origin.v; + const Rect pixMapBounds = pixMapBE.m_bounds.ToRect(); if (!pixMapBounds.IsValid()) return 40; @@ -574,6 +733,13 @@ namespace PortabilityLayer } else if (packType == 1) { + if (!isLineValid) + { + if (!stream->SeekCurrent(rowSizeBytes)) + return 59; + continue; + } + if (stream->Read(decompressedScanlineBuffer, rowSizeBytes) != rowSizeBytes) return 51; diff --git a/PortabilityLayer/QDPictDecoder.h b/PortabilityLayer/QDPictDecoder.h index cc71375..d7c210f 100644 --- a/PortabilityLayer/QDPictDecoder.h +++ b/PortabilityLayer/QDPictDecoder.h @@ -2,7 +2,8 @@ #include -struct Rect; +struct Rect; +struct Point; namespace PortabilityLayer { @@ -17,7 +18,7 @@ namespace PortabilityLayer bool DecodePict(IOStream *stream, QDPictEmitContext *emitContext); private: - int ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &drawArea, QDPictEmitContext *context); + int ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &drawArea, const Point &origin, QDPictEmitContext *context); static bool UnpackBits8(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize); static bool UnpackBits16(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize); diff --git a/PortabilityLayer/QDPictEmitContext.h b/PortabilityLayer/QDPictEmitContext.h index cbdc87b..6b9adb0 100644 --- a/PortabilityLayer/QDPictEmitContext.h +++ b/PortabilityLayer/QDPictEmitContext.h @@ -7,7 +7,8 @@ struct Rect; namespace PortabilityLayer { struct RGBAColor; - struct QDPictEmitScanlineParameters; + struct QDPictEmitScanlineParameters; + class IOStream; enum QDPictBlitSourceType { @@ -25,11 +26,27 @@ namespace PortabilityLayer class QDPictEmitContext { - public: + public: + enum ErrorCode + { + kMissingHeader, + kInvalidRegionRect, + kMalformedArguments, + kUnusedError1, + kUnusedError2, + kRasterOpFailure, + kUnsupportedClipRegionFormat, + kMalformedOpcode, + kUnsupportedOpcode, + }; + virtual bool SpecifyFrame(const Rect &rect) = 0; virtual Rect ConstrainRegion(const Rect &rect) const = 0; virtual void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) = 0; virtual void BlitScanlineAndAdvance(const void *) = 0; - virtual bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) = 0; + virtual bool EmitQTContent(IOStream *stream, uint32_t dataSize, bool isCompressed) = 0; + virtual bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) = 0; + + virtual void ReportError(int errorType, int errorSubtype) { } }; -} \ No newline at end of file +} diff --git a/PortabilityLayer/QDPictOpcodeDefs.h b/PortabilityLayer/QDPictOpcodeDefs.h index 6650228..4922885 100644 --- a/PortabilityLayer/QDPictOpcodeDefs.h +++ b/PortabilityLayer/QDPictOpcodeDefs.h @@ -3,7 +3,13 @@ PL_PICTOP(0x0001, kClipRegion, Rule_SizeTagged16) PL_PICTOP(0x0002, kBackPattern, 8) PL_PICTOP(0x0003, kTextFont, 2) PL_PICTOP(0x0004, kTextFontStyle, 1) -PL_PICTOP(0x0005, kTextSourceMode, 2) +PL_PICTOP(0x0005, kTextSourceMode, 2) +PL_PICTOP(0x0007, kPenSize, 4) +PL_PICTOP(0x0008, kPenMode, 2) +PL_PICTOP(0x0009, kPenPattern, 8) +PL_PICTOP(0x000c, kOrigin, 4) // Relative +PL_PICTOP(0x001a, kRGBForeColor, 6) +PL_PICTOP(0x001b, kRGBBackColor, 6) PL_PICTOP(0x001e, kDefaultHilite, 0) PL_PICTOP(0x001f, kOpColor, 6) PL_PICTOP(0x0030, kFrameRect, 8) @@ -15,4 +21,6 @@ PL_PICTOP(0x0098, kPackBitsRect, Rule_PackBitsRect) PL_PICTOP(0x0099, kPackBitsRgn, Rule_PackBitsRgn) PL_PICTOP(0x009a, kDirectBitsRect, Rule_DirectBitsRect) PL_PICTOP(0x009b, kDirectBitsRgn, Rule_DirectBitsRgn) +PL_PICTOP(0x8200, kCompressedQT, Rule_SizeTagged32) +PL_PICTOP(0x8201, kUncompressedQT, Rule_SizeTagged32) PL_PICTOP(0x00ff, kEndOfPicture, 0) diff --git a/PortabilityLayer/SharedTypes.h b/PortabilityLayer/SharedTypes.h index e498e40..de9554d 100644 --- a/PortabilityLayer/SharedTypes.h +++ b/PortabilityLayer/SharedTypes.h @@ -66,6 +66,14 @@ struct BERect Rect ToRect() const; }; +struct BEPoint +{ + BEInt16_t v; + BEInt16_t h; + + Point ToPoint() const; +}; + struct BERegion { BEUInt16_t recordSize; diff --git a/WindowsUnicodeToolShim.props b/WindowsUnicodeToolShim.props new file mode 100644 index 0000000..1c52718 --- /dev/null +++ b/WindowsUnicodeToolShim.props @@ -0,0 +1,10 @@ + + + + + + $(SolutionDir)WindowsUnicodeToolShim;$(IncludePath) + + + + \ No newline at end of file diff --git a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.cpp b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.cpp new file mode 100644 index 0000000..ceed94a --- /dev/null +++ b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.cpp @@ -0,0 +1,154 @@ +#include + +#include "UTF8.h" +#include "UTF16.h" + +#include +#include + +// This library provides front-ends and shims to make tools a bit more portable by handling all path strings as UTF-8, +// and providing a "main" entry point that is also UTF-8. + +static std::string ConvertWStringToUTF8(const wchar_t *str) +{ + size_t strLength = wcslen(str); + + std::string result; + + for (size_t i = 0; i < strLength; ) + { + size_t charsDigested = 0; + uint32_t codePoint = 0; + uint8_t asUTF8[4]; + if (!PortabilityLayer::UTF16Processor::DecodeCodePoint(reinterpret_cast(str) + i, strLength - i, charsDigested, codePoint)) + return ""; + + i += charsDigested; + + size_t bytesEmitted = 0; + PortabilityLayer::UTF8Processor::EncodeCodePoint(asUTF8, bytesEmitted, codePoint); + + result.append(reinterpret_cast(asUTF8), bytesEmitted); + } + + return result; +} + +static std::wstring ConvertUTF8ToWString(const char *str) +{ + size_t strLength = strlen(str); + + std::wstring result; + + for (size_t i = 0; i < strLength; ) + { + size_t charsDigested = 0; + uint32_t codePoint = 0; + uint16_t asUTF16[4]; + if (!PortabilityLayer::UTF8Processor::DecodeCodePoint(reinterpret_cast(str) + i, strLength - i, charsDigested, codePoint)) + return L""; + + i += charsDigested; + + size_t codePointsEmitted = 0; + PortabilityLayer::UTF16Processor::EncodeCodePoint(asUTF16, codePointsEmitted, codePoint); + + result.append(reinterpret_cast(asUTF16), codePointsEmitted); + } + + return result; +} + +FILE *fopen_utf8(const char *path, const char *options) +{ + std::wstring pathUTF16 = ConvertUTF8ToWString(path); + std::wstring optionsUTF16 = ConvertUTF8ToWString(options); + + return _wfopen(pathUTF16.c_str(), optionsUTF16.c_str()); +} + +int fputs_utf8(const char *str, FILE *f) +{ + return fputs(str, f); +} + + +int mkdir_utf8(const char *path) +{ + std::wstring pathUTF16 = ConvertUTF8ToWString(path); + return _wmkdir(pathUTF16.c_str()); +} + +void TerminateDirectoryPath(std::string &path) +{ + const size_t length = path.length(); + + if (length == 0) + path.append("\\"); + else + { + const char lastChar = path[path.length() - 1]; + if (lastChar != '\\' && lastChar != '/') + path.append("\\"); + } +} + +void ScanDirectoryForExtension(std::vector &outPaths, const char *path, const char *ending, bool recursive) +{ + std::wstring dirFilter = std::wstring(L"\\\\?\\") + ConvertUTF8ToWString(path) + L"\\*"; + size_t endingLen = strlen(ending); + + WIN32_FIND_DATAW findDataW; + HANDLE h = FindFirstFileW(dirFilter.c_str(), &findDataW); + + if (h == INVALID_HANDLE_VALUE) + return; + + while (true) + { + std::string utf8Name = ConvertWStringToUTF8(findDataW.cFileName); + if (utf8Name != "." && utf8Name != "..") + { + if (recursive && findDataW.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ScanDirectoryForExtension(outPaths, (std::string(path) + "\\" + utf8Name).c_str(), ending, true); + else if (utf8Name.length() >= endingLen && utf8Name.substr(utf8Name.length() - endingLen) == ending) + outPaths.push_back(std::string(path) + "\\" + utf8Name); + } + + if (!FindNextFileW(h, &findDataW)) + break; + } + + FindClose(h); +} + + +int toolMain(int argc, const char **argv); + +int main(int argc, const char **argv) +{ + SetConsoleOutputCP(CP_UTF8); + + LPWSTR *szArglist; + int nArgs; + + szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); + + std::vector utf8ArgStrings; + std::vector utf8Args; + + utf8ArgStrings.resize(nArgs); + utf8Args.resize(nArgs); + + for (int i = 0; i < nArgs; i++) + { + utf8ArgStrings[i] = ConvertWStringToUTF8(szArglist[i]); + utf8Args[i] = utf8ArgStrings[i].c_str(); + } + + const char **args = nullptr; + if (nArgs) + args = &utf8Args[0]; + + return toolMain(nArgs, args); +} diff --git a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h new file mode 100644 index 0000000..c4f3469 --- /dev/null +++ b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h @@ -0,0 +1,9 @@ +#include +#include +#include + +FILE *fopen_utf8(const char *path, const char *options); +int fputs_utf8(const char *str, FILE *f); +int mkdir_utf8(const char *path); +void TerminateDirectoryPath(std::string &path); +void ScanDirectoryForExtension(std::vector& outPaths, const char *path, const char *ending, bool recursive); diff --git a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj new file mode 100644 index 0000000..c791a9f --- /dev/null +++ b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {15009625-1120-405E-8BBA-69A16CD6713D} + WindowsUnicodeToolShim + 10.0.17763.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + \ No newline at end of file diff --git a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj.filters b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj.filters new file mode 100644 index 0000000..5eb0fb4 --- /dev/null +++ b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/gpr2gpa/gpr2gpa.cpp b/gpr2gpa/gpr2gpa.cpp index 7f525d9..053df37 100644 --- a/gpr2gpa/gpr2gpa.cpp +++ b/gpr2gpa/gpr2gpa.cpp @@ -20,6 +20,8 @@ #include "rapidjson/rapidjson.h" #include "rapidjson/document.h" +#include "WindowsUnicodeToolShim.h" + #include #include #include @@ -205,8 +207,8 @@ void ConvertToMSDOSTimestamp(const PortabilityLayer::CombinedTimestamp &ts, uint void ExportZipFile(const char *path, const std::vector &entries, const PortabilityLayer::CombinedTimestamp &ts) { - FILE *outF = nullptr; - if (fopen_s(&outF, path, "wb")) + FILE *outF = fopen_utf8(path, "wb"); + if (!outF) { fprintf(stderr, "Error opening output path"); return; @@ -322,8 +324,14 @@ public: Rect ConstrainRegion(const Rect &rect) const override; void Start(PortabilityLayer::QDPictBlitSourceType sourceType, const PortabilityLayer::QDPictEmitScanlineParameters ¶ms) override; void BlitScanlineAndAdvance(const void *) override; + bool EmitQTContent(PortabilityLayer::IOStream *stream, uint32_t dataSize, bool isCompressed) override; bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) override; + void ReportError(int errorCode, int subCode) override + { + fprintf(stderr, "PICT import failed, error code %i, subcode %i\n", errorCode, subCode); + } + bool Export(std::vector &outData) const; private: @@ -462,6 +470,17 @@ void BMPDumperContext::BlitScanlineAndAdvance(const void *scanlineData) } } +bool BMPDumperContext::EmitQTContent(PortabilityLayer::IOStream *stream, uint32_t dataSize, bool isCompressed) +{ + // Only one known house ("Magic" seems to use uncompressed, which is partly documented here: + // https://github.com/gco/xee/blob/master/XeePhotoshopPICTLoader.m + + // Known compressed cases and codecs: + // "Egypt" res 10011: JPEG + // "The Meadows" res 3000: Apple Video (a.k.a. Apple RPZA) + return false; +} + bool BMPDumperContext::AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) { m_tempBuffers.resize(buffer1Size + buffer2Size); @@ -1078,7 +1097,7 @@ bool ApplyPatch(const std::vector &patchFileContents, std::vector(itemValue.GetString())); @@ -1125,47 +1144,19 @@ bool ApplyPatch(const std::vector &patchFileContents, std::vector [patch.json]"); - return -1; - } - std::string base = argv[1]; - - FILE *inF = nullptr; - if (fopen_s(&inF, argv[1], "rb")) + FILE *inF = fopen_utf8(resPath, "rb"); + if (!inF) { fprintf(stderr, "Error opening input file"); return -1; } - FILE *timestampF = nullptr; - if (fopen_s(×tampF, argv[2], "rb")) - { - fprintf(stderr, "Error opening metadata file"); - return -1; - } - - PortabilityLayer::CombinedTimestamp ts; - if (fread(&ts, 1, sizeof(ts), timestampF) != sizeof(ts)) - { - fprintf(stderr, "Error reading timestamp"); - return -1; - } - bool havePatchFile = false; std::vector patchFileContents; - FILE *patchF = nullptr; - if (argc == 5 && fopen_s(&patchF, argv[4], "rb")) - { - fprintf(stderr, "Error reading patch file"); - return -1; - } - if (patchF) { havePatchFile = true; @@ -1226,6 +1217,8 @@ int main(int argc, const char **argv) if (ImportPICT(entry.m_contents, resData, resSize)) contents.push_back(entry); + else + fprintf(stderr, "Failed to import PICT res %i\n", static_cast(res.m_resID)); } else if (typeList.m_resType == sndTypeID) { @@ -1285,9 +1278,123 @@ int main(int argc, const char **argv) std::sort(contents.begin(), contents.end(), EntryAlphaSortPredicate); - ExportZipFile(argv[3], contents, ts); + ExportZipFile(outPath, contents, ts); resFile->Destroy(); return 0; } + +int ConvertDirectory(const std::string &basePath, const PortabilityLayer::CombinedTimestamp &ts) +{ + std::vector paths; + ScanDirectoryForExtension(paths, basePath.c_str(), ".gpr", true); + + for (std::vector::const_iterator it = paths.begin(), itEnd = paths.end(); it != itEnd; ++it) + { + const std::string &resPath = *it; + std::string housePathBase = resPath.substr(0, resPath.length() - 4); + + std::string metaPath = housePathBase + ".gpf"; + + FILE *metaF = fopen_utf8(metaPath.c_str(), "rb"); + if (!metaF) + { + fprintf(stderr, "Failed to open metadata file "); + fputs_utf8(metaPath.c_str(), stderr); + fprintf(stderr, "\n"); + } + + PortabilityLayer::MacFilePropertiesSerialized mfps; + if (fread(mfps.m_data, 1, PortabilityLayer::MacFilePropertiesSerialized::kSize, metaF) != PortabilityLayer::MacFilePropertiesSerialized::kSize) + { + fclose(metaF); + fprintf(stderr, "Failed to load metadata file "); + fputs_utf8(metaPath.c_str(), stderr); + fprintf(stderr, "\n"); + return -1; + } + fclose(metaF); + + PortabilityLayer::MacFileProperties mfp; + mfps.Deserialize(mfp); + + if (mfp.m_fileType[0] == 'g' && mfp.m_fileType[1] == 'l' && mfp.m_fileType[2] == 'i' && mfp.m_fileType[3] == 'H') + { + std::string houseArchivePath = (housePathBase + ".gpa"); + fprintf(stdout, "Importing "); + fputs_utf8(houseArchivePath.c_str(), stdout); + fprintf(stdout, "\n"); + + int returnCode = ConvertSingleFile(resPath.c_str(), ts, nullptr, houseArchivePath.c_str()); + if (returnCode) + { + fprintf(stderr, "An error occurred while converting\n"); + fputs_utf8(resPath.c_str(), stderr); + fprintf(stderr, "\n"); + return returnCode; + } + } + } + + return 0; +} + +int PrintUsage() +{ + fprintf(stderr, "Usage: gpr2gpa [patch.json]"); + fprintf(stderr, " gpr2gpa \* "); + fprintf(stderr, " gpr2gpa /* "); + fprintf(stderr, " gpr2gpa * "); + return -1; +} + +int toolMain(int argc, const char **argv) +{ + if (argc < 3) + return PrintUsage(); + + FILE *timestampF = fopen_utf8(argv[2], "rb"); + if (!timestampF) + { + fprintf(stderr, "Error opening timestamp file"); + return -1; + } + + PortabilityLayer::CombinedTimestamp ts; + if (fread(&ts, 1, sizeof(ts), timestampF) != sizeof(ts)) + { + fprintf(stderr, "Error reading timestamp"); + return -1; + } + + fclose(timestampF); + + std::string base = argv[1]; + + if (base == "*") + return ConvertDirectory(".", ts); + + if (base.length() >= 2) + { + std::string baseEnding = base.substr(base.length() - 2, 2); + if (baseEnding == "\\*" || baseEnding == "/*") + return ConvertDirectory(base.substr(0, base.length() - 2), ts); + } + + if (argc != 4 && argc != 5) + return PrintUsage(); + + FILE *patchF = nullptr; + if (argc == 5) + { + patchF = fopen_utf8(argv[4], "rb"); + if (!patchF) + { + fprintf(stderr, "Error reading patch file"); + return -1; + } + } + + return ConvertSingleFile(argv[1], ts, patchF, argv[3]); +} diff --git a/gpr2gpa/gpr2gpa.vcxproj b/gpr2gpa/gpr2gpa.vcxproj index 51452ef..675efc5 100644 --- a/gpr2gpa/gpr2gpa.vcxproj +++ b/gpr2gpa/gpr2gpa.vcxproj @@ -64,6 +64,7 @@ + @@ -73,6 +74,7 @@ + @@ -82,6 +84,7 @@ + @@ -91,6 +94,7 @@ + @@ -142,6 +146,9 @@ {6ec62b0f-9353-40a4-a510-3788f1368b33} + + {15009625-1120-405e-8bba-69a16cd6713d} + {6ae5c85e-6631-4a12-97a0-a05f812fe9ca} diff --git a/unpacktool/unpacktool.cpp b/unpacktool/unpacktool.cpp index 434fd08..b81244a 100644 --- a/unpacktool/unpacktool.cpp +++ b/unpacktool/unpacktool.cpp @@ -22,6 +22,7 @@ #include "CompactProLZHRLEDecompressor.h" #include "CSInputBuffer.h" +#include "WindowsUnicodeToolShim.h" #include @@ -92,84 +93,6 @@ StuffItParser g_stuffItParser; StuffIt5Parser g_stuffIt5Parser; CompactProParser g_compactProParser; -std::string ConvertWStringToUTF8(const wchar_t *str) -{ - size_t strLength = wcslen(str); - - std::string result; - - for (size_t i = 0; i < strLength; ) - { - size_t charsDigested = 0; - uint32_t codePoint = 0; - uint8_t asUTF8[4]; - if (!PortabilityLayer::UTF16Processor::DecodeCodePoint(reinterpret_cast(str) + i, strLength - i, charsDigested, codePoint)) - return ""; - - i += charsDigested; - - size_t bytesEmitted = 0; - PortabilityLayer::UTF8Processor::EncodeCodePoint(asUTF8, bytesEmitted, codePoint); - - result.append(reinterpret_cast(asUTF8), bytesEmitted); - } - - return result; -} - -std::wstring ConvertUTF8ToWString(const char *str) -{ - size_t strLength = strlen(str); - - std::wstring result; - - for (size_t i = 0; i < strLength; ) - { - size_t charsDigested = 0; - uint32_t codePoint = 0; - uint16_t asUTF16[4]; - if (!PortabilityLayer::UTF8Processor::DecodeCodePoint(reinterpret_cast(str) + i, strLength - i, charsDigested, codePoint)) - return L""; - - i += charsDigested; - - size_t codePointsEmitted = 0; - PortabilityLayer::UTF16Processor::EncodeCodePoint(asUTF16, codePointsEmitted, codePoint); - - result.append(reinterpret_cast(asUTF16), codePointsEmitted); - } - - return result; -} - -FILE *fopen_utf8(const char *path, const char *options) -{ - std::wstring pathUTF16 = ConvertUTF8ToWString(path); - std::wstring optionsUTF16 = ConvertUTF8ToWString(options); - - return _wfopen(pathUTF16.c_str(), optionsUTF16.c_str()); -} - -int mkdir_utf8(const char *path) -{ - std::wstring pathUTF16 = ConvertUTF8ToWString(path); - return _wmkdir(pathUTF16.c_str()); -} - -void TerminateDirectoryPath(std::string &path) -{ - const size_t length = path.length(); - - if (length == 0) - path.append("\\"); - else - { - const char lastChar = path[path.length() - 1]; - if (lastChar != '\\' && lastChar != '/') - path.append("\\"); - } -} - std::string LegalizeWindowsFileName(const std::string &path) { const size_t length = path.length(); @@ -441,7 +364,7 @@ int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, for (int i = 0; i < depth; i++) printf(" "); - fputws(ConvertUTF8ToWString(path.data()).c_str(), stdout); + fputs_utf8(path.c_str(), stdout); printf("\n"); path = LegalizeWindowsFileName(path); @@ -479,7 +402,7 @@ int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::strin return 0; } -int unpackMain(int argc, const char **argv) +int toolMain(int argc, const char **argv) { if (argc != 3) { @@ -537,30 +460,3 @@ int unpackMain(int argc, const char **argv) return returnCode; } -int main(int argc, const char **argv) -{ - LPWSTR *szArglist; - int nArgs; - int i; - - szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); - - std::vector utf8ArgStrings; - std::vector utf8Args; - - utf8ArgStrings.resize(nArgs); - utf8Args.resize(nArgs); - - for (int i = 0; i < nArgs; i++) - { - utf8ArgStrings[i] = ConvertWStringToUTF8(szArglist[i]); - utf8Args[i] = utf8ArgStrings[i].c_str(); - } - - const char **args = nullptr; - if (nArgs) - args = &utf8Args[0]; - - return unpackMain(nArgs, args); -} - diff --git a/unpacktool/unpacktool.vcxproj b/unpacktool/unpacktool.vcxproj index a132425..57843a1 100644 --- a/unpacktool/unpacktool.vcxproj +++ b/unpacktool/unpacktool.vcxproj @@ -61,24 +61,28 @@ + + + + @@ -186,6 +190,9 @@ {6ec62b0f-9353-40a4-a510-3788f1368b33} + + {15009625-1120-405e-8bba-69a16cd6713d} +