diff --git a/bsnes/Makefile b/bsnes/Makefile index 06a58342..a820d46f 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -1,6 +1,6 @@ include nall/Makefile snes := snes -profile := compatibility +profile := accuracy ui := ui-phoenix # compiler diff --git a/bsnes/data/cheats.xml b/bsnes/data/cheats.xml index 4cd1423e..3cd2ef6a 100755 --- a/bsnes/data/cheats.xml +++ b/bsnes/data/cheats.xml @@ -3360,16 +3360,6 @@ Arcana (USA) - - Start with 60,000 gold pieces - 1D69-AFD0 - 3C69-AF00 - - - Start with 250 gold pieces - EC69-AFD0 - DD69-AF00 - Infinite money for weapons C225-0F02 @@ -3387,13 +3377,34 @@ C22F-A7DE - Level and statistics for all characters in group are increased after each battle you win + Level and statistics for all characters in group are increased after each battle B387-DFF2 Magic points don't decrease 828B-AF2E + + Start with 60,000 gold pieces + 1D69-AFD0 + 3C69-AF00 + + + Start with 250 gold pieces + EC69-AFD0 + DD69-AF00 + + + + Archer Maclean's Super Dropzone (Europe) + + Infinite lives + 7E002504 + + + Infinite bombs + 7E002604 + Ardy Lightfoot (USA) @@ -4290,10 +4301,6 @@ Battle Blaze (USA) - - Infinite continues - C285-D4AB - Infinite health (disable at end of round) - P1 7E06DCC0 @@ -4306,6 +4313,14 @@ No health - P1 7E06DC00 + + No health - P2 + 7E070400 + + + Infinite continues + C285-D4AB + Battle Clash (USA) @@ -4595,6 +4610,83 @@ 8982-CFD1 + + Battletoads-Double Dragon (USA) + + Invincibility (blinking) - P1 + 7E11127F + + + Infinite health - P1 + 7E003A2F + + + Infinite lives - P1 + 7E002605 + + + Infinite lives + 40B8-04AF + + + Enemies have less health + DE6E-1466 + + + Abobo has less health + 4EB7-1DD6 + + + Big Blag has less health + 4EB3-C4DB + + + Roper has less health + D7BA-3FA8 + + + Robo-Manus has less health + D7C5-3F66 + + + Start with 2 lives + DF60-D76D + + + Start with 10 lives + DB60-D76D + + + Start on level 2 with 11 lives + DD65-DD0D + CB66-D46D + DF66-D4AD + + + Start on level 3 with 11 lives + DD65-DD0D + CB66-D46D + D466-D4AD + + + Start on level 4 with 11 lives + DD65-DD0D + CB66-D46D + D766-D4AD + + + Start on level 5 with 11 lives + DD65-DD0D + CB66-D46D + D066-D4AD + + + Start on level 6 with 11 lives + DD65-DD0D + CB66-D46D + D966-D4AD + + Bazooka Blitzkrieg (USA) @@ -4996,6 +5088,21 @@ C262-0D62 + + BioMetal (USA) + + Infinite lives + C26E-6D02 + + + Infinite lives (alt) + 7E029D0A + + + Infinite charge + C262-0D62 + + Bishin Densetsu Zoku (Japan) @@ -7756,17 +7863,21 @@ Castlevania - Dracula X (USA) - Invincibility (turn on after title screen, during game play) - 10D2-FE86 + Invincibility + 2D69-CD9D Invincibility after one hit C969-CD2D - Infinite energy + Infinite health C96D-17FF + + Infinite lives + C9AF-47A7 + All hearts worth 99 2D25-1FD3 @@ -7840,10 +7951,6 @@ Start with 99 lives BB85-1D6F - - Infinite lives - C9AF-47A7 - Start with 25 hearts after you die 49A4-44D7 @@ -8990,26 +9097,6 @@ Infinite bombs (top-view levels) 22B8-0766 - - Start with 5 bombs on each life (side-view levels) - D9BB-AFA1 - D9CE-6D0D - - - Start with 9 bombs on each life (side-view levels) - DBBB-AFA1 - DBCE-6D0D - - - Start with 5 bombs on each life (top-view levels) - D963-6708 - D9CE-6D0F - - - Start with 9 bombs on each life (top-view levels) - DB63-6708 - DBCE-6D0F - Enable 30 and 99 lives in option menu (99 actually gives 35,081 lives) D987-AFA4 @@ -9062,6 +9149,26 @@ Always have Laser for gun 2 (disable during bonus stages) 7E1F8605 + + Start with 5 bombs on each life (side-view levels) + D9BB-AFA1 + D9CE-6D0D + + + Start with 9 bombs on each life (side-view levels) + DBBB-AFA1 + DBCE-6D0D + + + Start with 5 bombs on each life (top-view levels) + D963-6708 + D9CE-6D0F + + + Start with 9 bombs on each life (top-view levels) + DB63-6708 + DBCE-6D0F + Start on stage 2 7E008602 @@ -9128,6 +9235,10 @@ Cool World (USA) + + Invincibility (blinking) + 7E02BFFF + Infinite lives C2B6-D4DC @@ -9136,6 +9247,10 @@ Infinite continues C2B2-DF67 + + Infinite Nickels + 7E00ED63 + Start with 1 life DFBE-DD07 @@ -11065,10 +11180,6 @@ Infinite lives C2C1-4A2C - - Start with 255 lives - EE68-C33D - Easy level exit (press start and select) DDB0-34A4 @@ -11095,56 +11206,67 @@ 166A-3ECD - High-jump Donkey Kong + Multi-jump + 6D89-3A8A + 4080-13E3 + 6D89-1A73 + 2D89-13E3 + + + Multi-jump - Expresso (the Ostrich) + EE81-CEE3 + + + High-jump - Donkey Kong A086-13E3 - Super-jump Donkey Kong + Super-jump - Donkey Kong 2D86-13E3 - Mega-jump Donkey Kong + Mega-jump - Donkey Kong 3D86-13E3 - Moon-jump Donkey Kong + Moon-jump - Donkey Kong EE86-13E3 - High-jump Diddy Kong + High-jump - Diddy Kong 808B-1AE3 - Super-jump Diddy Kong + Super-jump - Diddy Kong AD8B-1AE3 - Mega-jump Diddy Kong + Mega-jump - Diddy Kong 2D8B-1AE3 - Moon-jump Diddy Kong + Moon-jump - Diddy Kong EE8B-1AE3 - High-jump Animals + High-jump - Animals A087-C3E3 - Super-jump Animals + Super-jump - Animals 2D87-C3E3 - Mega-jump Animals + Mega-jump - Animals 3087-C3E3 - Moon-jump Animals + Moon-jump - Animals EE87-C3E3 - Expresso Multi-jump (jump in mid-air) - EE81-CEE3 + Start with 255 lives + EE68-C33D @@ -11163,10 +11285,6 @@ Infinite lives C2C5-4A2C - - 255 lives - EE63-333D - Easy level exit (press start and select) DDB9-3764 @@ -11188,56 +11306,67 @@ 7E057901 - High-jump for Donkey Kong + Multi-jump + 6D85-327A + 4081-1E53 + 6D85-1A83 + 2D85-1E53 + + + Multi-jump - Expresso (the Ostrich) + EE8B-CA53 + + + High-jump - Donkey Kong A08C-1E53 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D8C-1E53 - Mega-jump for Donkey Kong + Mega-jump - Donkey Kong 308C-1E53 - Moon-jump for Donkey Kong + Moon-jump - Donkey Kong EE8C-1E53 - High-jump for Diddy Kong + High-jump - Diddy Kong 8088-1253 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD88-1253 - Mega-jump for Diddy Kong + Mega-jump - Diddy Kong 2D88-1253 - Moon-jump for Diddy Kong + Moon-jump - Diddy Kong EE88-1253 - High-jump for Animals + High-jump - Animals A089-CE53 - Super-jump for Animals + Super-jump - Animals 2D89-CE53 - Mega-jump for Animals + Mega-jump - Animals 3089-CE53 - Moon-jump for Animals + Moon-jump - Animals EE89-CE53 - Multi-jump in mid-air - EE8B-CA53 + Start with 255 lives + EE63-333D @@ -11266,45 +11395,6 @@ C2C9-4E2C C2C1-4A9C - - Start With 8 lives - D568-C34D - D568-C33D - - - Start With 11 lives - DC68-C34D - DC68-C33D - - - Start With 16 lives - DE68-C34D - DE68-C33D - - - Start With 26 lives - FB68-C34D - FB68-C33D - - - Start With 51 lives - 7468-C34D - 7468-C33D - - - Start With 76 lives - 0868-C34D - 0868-C33D - - - Start With 100 lives - 1768-C34D - 1768-C33D - - - Start with 255 lives - EE68-C34D - 10 Bananas needed for extra life DBC1-3D6D @@ -11381,101 +11471,152 @@ 166A-3ECD - High-jump for Donkey Kong + Multi-jump + 6D84-33EA + 4084-1273 + 6D84-1353 + 2D87-1273 + + + Multi-jump - Expresso (the Ostrich) + EE80-C373 + + + High-jump - Donkey Kong A081-1273 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D81-1273 - Mega-jump for Donkey Kong + Mega-jump - Donkey Kong 3D81-1273 - Moon-jump for Donkey Kong + Moon-jump - Donkey Kong EE81-1273 - High-jump for Diddy Kong + High-jump - Diddy Kong 8081-1E73 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD81-1E73 - Mega-jump for Diddy Kong + Mega-jump - Diddy Kong 2D81-1E73 - Moon-jump for Diddy Kong + Moon-jump - Diddy Kong EE81-1E73 - High-jump for Animals + High-jump - Animals A08F-C273 - Super-jump for Animals + Super-jump - Animals 2D8F-C273 - Mega-jump for Animals + Mega-jump - Animals 3D8F-C273 - Moon-jump for Animals + Moon-jump - Animals EE8F-C273 - High-jump for Donkey Kong + High-jump - Donkey Kong A081-1273 A086-13E3 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D81-1273 2D86-13E3 - Mega-Jump for Donkey Kong + Mega-Jump - Donkey Kong 3D81-1273 3D86-13E3 - High-jump for Diddy Kong + High-jump - Diddy Kong 8081-1E73 808B-1AE3 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD81-1E73 AD8B-1AE3 - Mega-Jump for Diddy Kong + Mega-Jump - Diddy Kong 2D81-1E73 2D8B-1AE3 - High-jump for all Animals + High-jump - all Animals A08F-C273 A087-C3E3 - Super-jump for all Animals + Super-jump - all Animals 2D8F-C273 2D87-C3E3 - Mega-jump for all Animals + Mega-jump - all Animals 3D8F-C273 3087-C3E3 - Expresso Multi-jump (jump in mid-air) - EE80-C373 + Start With 8 lives + D568-C34D + D568-C33D + + + Start With 11 lives + DC68-C34D + DC68-C33D + + + Start With 16 lives + DE68-C34D + DE68-C33D + + + Start With 26 lives + FB68-C34D + FB68-C33D + + + Start With 51 lives + 7468-C34D + 7468-C33D + + + Start With 76 lives + 0868-C34D + 0868-C33D + + + Start With 100 lives + 1768-C34D + 1768-C33D + + + Start with 255 lives + EE68-C34D + + + Infinite time in bonus stages + 7E137F99 + 7E13F399 @@ -11491,6 +11632,104 @@ C2A1-CE5B C2A5-C37B + + Get 2 lives per 100 bananas collected + D4A1-437C + D4A5-4AEC + + + Get 5 lives per 100 bananas collected + D9A1-437C + D9A5-4AEC + + + Get 255 lives per 100 bananas collected + EEA1-437C + + + When your last Kong is hit the other returns + EEC2-1A1D + EECC-CA4D + + + Easy level exit (press start and select) + DD6C-C7D4 + DD62-C4A4 + + + Kong family coins don't get used up + C2B9-13B7 + C2B1-13F7 + + + Kremcoins don't get used up + C2B9-1297 + C2B1-1A27 + + + Mega-jump - Diddy + EDD0-735A + + + Super-jump - Diddy + E7D0-735A + + + Jump higher - Diddy + E1D0-735A + + + Jump lower - Diddy + EBD0-735A + + + Jump much lower - Diddy + ECD0-735A + + + Mega-jump - Dixie + EDD7-5AEA + + + Super-jump - Dixie + E7D7-5AEA + + + Jump higher - Dixie + E5D7-5AEA + + + Jump lower - Dixie + EBD7-5AEA + + + Jump much lower - Dixie + ECD7-5AEA + + + Mega-jump - Rambi with Diddy riding + EFDD-535A + + + Super-jump - Rambi with Diddy riding + E7DD-535A + + + Rambi jumps higher with Diddy riding + E5DD-535A + + + Rambi doesn't jump as high with Diddy riding + ECDD-535A + + + Start with more kong family coins + 626D-4EBD + + + Start with more Kremcoins + 626D-432D + Start with 3 lives D465-3D67 @@ -11520,104 +11759,6 @@ Start with 255 lives EE65-3D67 - - Get 2 lives per 100 bananas collected - D4A1-437C - D4A5-4AEC - - - Get 5 lives per 100 bananas collected - D9A1-437C - D9A5-4AEC - - - Get 255 lives per 100 bananas collected - EEA1-437C - - - When your last Kong is hit the other returns - EEC2-1A1D - EECC-CA4D - - - Easy level exit (press start and select) - DD6C-C7D4 - DD62-C4A4 - - - Start with more kong family coins - 626D-4EBD - - - Kong family coins don't get used up - C2B9-13B7 - C2B1-13F7 - - - Start a new game with more Kremcoins - 626D-432D - - - Kremcoins don't get used up - C2B9-1297 - C2B1-1A27 - - - Mega-jump for Diddy - EDD0-735A - - - Super-jump for Diddy - E7D0-735A - - - Diddy jumps higher - E1D0-735A - - - Diddy doesn't jump as high - EBD0-735A - - - Diddy jumps much lower - ECD0-735A - - - Mega-jump for Dixie - EDD7-5AEA - - - Super-jump for Dixie - E7D7-5AEA - - - Dixie jumps higher - E5D7-5AEA - - - Dixie doesn't jump as high - EBD7-5AEA - - - Dixie jumps much lower - ECD7-5AEA - - - Mega-jump for Rambi with Diddy riding - EFDD-535A - - - Super-jump for Rambi with Diddy riding - E7DD-535A - - - Rambi jumps higher with Diddy riding - E5DD-535A - - - Rambi doesn't jump as high with Diddy riding - ECDD-535A - Donkey Kong Country 2 - Diddy's Kong Quest (USA) (En,Fr) @@ -11627,11 +11768,120 @@ 6DAF-12EB 6D8D-C33E + + Invincibility + 4028-434D + Infinite lives C2A1-CE5B C2A5-C37B + + Get 2 lives per 100 bananas collected + D4A1-437C + D4A5-4AEC + + + Get 5 lives per 100 bananas collected + D9A1-437C + D9A5-4AEC + + + Get 255 lives per 100 bananas collected + EEA1-437C + + + When your last Kong is hit the other returns + EEC2-1A1D + EECC-CA4D + + + Easy level exit (press start and select) + DD6C-C7D4 + DD62-C4A4 + + + Kong family coins don't get used up + C2B9-13B7 + C2B1-13F7 + + + Kremcoins don't get used up + C2B9-1297 + C2B1-1A27 + + + Multi-jump + 4DAF-121F + 54AF-12CF + A0AF-123F + 1DAF-134F + + + Mega-jump - Diddy + EDD0-735A + + + Super-jump - Diddy + E7D0-735A + + + Jump higher - Diddy + E1D0-735A + + + Jump lower - Diddy + EBD0-735A + + + Jump much lower - Diddy + ECD0-735A + + + Mega-jump - Dixie + EDD7-5AEA + + + Super-jump - Dixie + E7D7-5AEA + + + Jump higher - Dixie + E5D7-5AEA + + + Jump lower - Dixie + EBD7-5AEA + + + Jump much lower - Dixie + ECD7-5AEA + + + Mega-jump - Rambi with Diddy riding + EFDD-535A + + + Super-jump - Rambi with Diddy riding + E7DD-535A + + + Rambi jumps higher with Diddy riding + E5DD-535A + + + Rambi doesn't jump as high with Diddy riding + ECDD-535A + + + Start with more kong family coins + 626D-4EBD + + + Start with more Kremcoins + 626D-432D + Start with 3 lives D465-3D67 @@ -11661,104 +11911,6 @@ Start with 255 lives EE65-3D67 - - Get 2 lives per 100 bananas collected - D4A1-437C - D4A5-4AEC - - - Get 5 lives per 100 bananas collected - D9A1-437C - D9A5-4AEC - - - Get 255 lives per 100 bananas collected - EEA1-437C - - - When your last Kong is hit the other returns - EEC2-1A1D - EECC-CA4D - - - Easy level exit (press start and select) - DD6C-C7D4 - DD62-C4A4 - - - Start with more kong family coins - 626D-4EBD - - - Kong family coins don't get used up - C2B9-13B7 - C2B1-13F7 - - - Start a new game with more Kremcoins - 626D-432D - - - Kremcoins don't get used up - C2B9-1297 - C2B1-1A27 - - - Mega-jump for Diddy - EDD0-735A - - - Super-jump for Diddy - E7D0-735A - - - Diddy jumps higher - E1D0-735A - - - Diddy doesn't jump as high - EBD0-735A - - - Diddy jumps much lower - ECD0-735A - - - Mega-jump for Dixie - EDD7-5AEA - - - Super-jump for Dixie - E7D7-5AEA - - - Dixie jumps higher - E5D7-5AEA - - - Dixie doesn't jump as high - EBD7-5AEA - - - Dixie jumps much lower - ECD7-5AEA - - - Mega-jump for Rambi with Diddy riding - EFDD-535A - - - Super-jump for Rambi with Diddy riding - E7DD-535A - - - Rambi jumps higher with Diddy riding - E5DD-535A - - - Rambi doesn't jump as high with Diddy riding - ECDD-535A - Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr) @@ -11766,6 +11918,12 @@ Invincibility B768-C34D + + Invincibility (alt) + CBA0-CECF + DBA0-CE3F + DDA9-CA4F + Infinite lives CB6E-73CD @@ -11779,6 +11937,13 @@ 6D6C-7EFF 6D62-722F + + Multi-jump + 4D2B-3E1F + 5F2B-3ECF + BD2B-3E3F + 1D2C-3A4F + Doom (USA) @@ -17945,6 +18110,28 @@ BA2B-3FC4 + + Gekitotsu Dangan Jidousha Kessen - Battle Mobile (Japan) + + Invincibility + B989-04A9 + 898B-AFD5 + 8988-AFA5 + + + Infinite health + 898B-AFD5 + 8988-AFA5 + + + Infinite lives + C282-AF65 + + + Infinite Shields + 8BBF-A7A5 + + George Foreman's KO Boxing (USA) (Rev 1) @@ -19519,45 +19706,63 @@ Indiana Jones' Greatest Adventures (USA) - Infinite energy - CB24-4D64 + Invincibility + 62A7-1764 + 2D80-CDDF - Start with very little energy - DFC1-3707 + Infinite health + 7E012006 - Start with about 1/2 energy - D7C1-3707 + Infinite lives + C2C2-37A4 - Start with more energy (ignore energy meter) - DEC1-3701 - - - Hearts don't restore energy - C2B6-440F - - - Infinite grenades + Infinite Bombs C2B6-370F - Start with 5 grenades + Infinite Grenades + C2B6-370F + + + Infinite continues + 3CAB-CE82 + F0BC-173B + + + Hearts don't restore health + C2B6-440F + + + Can't collect Grenades + C2B9-37DF + + + Start with very little health + DFC1-3707 + + + Start with about 1/2 health + D7C1-3707 + + + Start with more health (ignore health meter) + DEC1-3701 + + + Start with 5 Grenades D9C7-CF0F - Start with 9 grenades + Start with 9 Grenades DBC7-CF0F - Start with 15 grenades + Start with 15 Grenades DEC7-CF0F - - Can't collect any more grenades - C2B9-37DF - Start with 2 lives DFCC-1E83 @@ -19574,11 +19779,6 @@ Start with no continues D1C8-13E3 - - Infinite continues - 3CAB-CE82 - F0BC-173B - Inindo - Way of the Ninja (USA) @@ -24015,13 +24215,6 @@ Have bottle 4 with infinite Good Bees 7EF35F08 - - Walk through walls - 07CB9AEA - 07CB9BEA - 07C1FAEA - 07C1FBEA - Legend of the Mystical Ninja, The (USA) @@ -30111,22 +30304,58 @@ Infinite Beat Whistles (alt) 7E0BA384 + + Infinite weapon energy + FEC2-E405 + Infinite Bolts 7E0BA6E7 7E0BA703 - Infinite weapon energy - FEC2-E405 + Infinite Danger Wrap + 7E0B91FF + + + Infinite Feeze Cracker + 7E0B85FF + + + Infinite Junk Shield + 7E0B89FF + + + Infinite Noise Crush + 7E0B8FFF + + + Infinite Scorch Wheel + 7E0B8BFF + + + Infinite Slash Claw + 7E0B8DFF Infinite slide 7E0C61FF - Moon-jump - 7E0C1AF4 + Infinite Thunder Bolt + 7E0B87FF + + + Infinite Rush Coil + 7E0B9BFF + + + Infinite Rush Search + 7E0B97FF + + + Infinite Wild Coil + 7E0B93FF Have exit @@ -30145,44 +30374,18 @@ 7E0B95FF - Infinite Feeze Cracker - 7E0B85FF + Multi-jump + 09FB-54A0 + 6DFB-5700 + CBFC-5D60 + D1FC-5DA0 + DDFC-5FD0 + 69FC-5F00 + D4FC-5F60 - Infinite Junk Shield - 7E0B89FF - - - Infinite Scorch Wheel - 7E0B8BFF - - - Infinite Slash Claw - 7E0B8DFF - - - Infinite Thunder Bolt - 7E0B87FF - - - Infinite Noise Crush - 7E0B8FFF - - - Infinite Danger Wrap - 7E0B91FF - - - Infinite Wild Coil - 7E0B93FF - - - Infinite Rush Coil - 7E0B9BFF - - - Infinite Rush Search - 7E0B97FF + Moon-jump + 7E0C1AF4 @@ -30204,6 +30407,10 @@ Infinite lives 7E1F8009 + + Infinite weapons once obtained + DDB3-4FA9 + One hit kills (most enemies) 6DB5-CD97 @@ -30237,8 +30444,10 @@ 7E1F86FF - Infinite weapon usage once obtained - DDB3-4FA9 + Multi-jump + 4065-17A9 + EA66-1409 + B966-1469 Bogus jump (may go back to normal jumps) @@ -30248,6 +30457,14 @@ Super-jump (may go back to normal jumps) D58A-1FBC + + Start with less health + D6BE-47AF + + + Start with more health + 4DBE-47AF + Start with 10 lives DBBE-446F @@ -30264,14 +30481,6 @@ Start with 1 life DDBE-446F - - Start with less health - D6BE-47AF - - - Start with more health - 4DBE-47AF - Mega Man X (USA) @@ -30292,28 +30501,12 @@ 7E1F8009 - Start with 10 lives - DBBE-446F + Start with all weapons and all enemies defeated (except Sigma) + 23BD-3F07 - Start with 7 lives - D1BE-446F - - - Start with 5 lives - D0BE-446F - - - Start with 1 life - DDBE-446F - - - Start with less health - D6BE-47AF - - - Start with more health - 4DBE-47AF + Infinite weapons once obtained + C9B3-4769 One hit kills (most enemies) @@ -30340,12 +30533,10 @@ 7E1F86FF - Start with all weapons and all enemies defeated (except Sigma) - 23BD-3F07 - - - Infinite weapons once you have them - C9B3-4769 + Multi-jump + 4065-1F09 + E465-17A9 + B966-1DD9 Bogus jump (may go back to normal jumps) @@ -30363,6 +30554,30 @@ Weapon charges to 1st power level faster DDB1-4F61 + + Start with less health + D6BE-47AF + + + Start with more health + 4DBE-47AF + + + Start with 10 lives + DBBE-446F + + + Start with 7 lives + D1BE-446F + + + Start with 5 lives + D0BE-446F + + + Start with 1 life + DDBE-446F + Mega Man X2 (USA) @@ -30390,10 +30605,6 @@ Infinite lives 7E1FB309 - - One hit kills - 6D2D-AF14 - Have Shouryuken (F, D, DF + Fire) (must have max health and health containers) 7E1FB180 @@ -30462,17 +30673,23 @@ Have sub-tank 4 full 7E1FB9FF + + One hit kills + 6D2D-AF14 + + + Multi-jump + 40BE-DD44 + 26BE-D4C4 + 8BBE-D434 + Mega Man X3 (USA) - Invincibility (enable) + Invincibility 7E0A0808 - - Invincibility (disable) - 7E0A0800 - Infinite health C2AD-6FF7 @@ -30525,6 +30742,12 @@ Skip bosses at start of game and proceed to stage select B933-DF6C + + Multi-jump + 4064-042F + D367-0F9F + C567-0FBF + Super-jump D586-6F26 @@ -31822,6 +32045,23 @@ 0823-3944 + + Mortal Kombat - Shinken Kourin Densetsu (Japan) + + Infinite health + CF31-4F64 + 6231-44D4 + EB31-4404 + D031-4464 + + + One hit kill - P1 + DD3C-44D4 + 623C-4464 + E83C-44A4 + D03C-47D4 + + Mortal Kombat 3 (USA) @@ -32018,10 +32258,28 @@ Invincibility - P1 C2B1-14F7 + + Invincibility - P1 (alt) + 849C6880 + 849C6909 + 849C7B00 + 85AA5260 + 85AB1C60 + 85AC2A60 + Invincibility - P2/CPU C2B5-14F7 + + Invincibility - P2/CPU (alt) + 849C6880 + 849C6906 + 849C7600 + 85AA5260 + 85AB1C60 + 85AC2A60 + Infinite health CB89-4FAA @@ -33896,10 +34154,22 @@ (NG) Invincibility 6DB5-3797 + + (NG) Invincibility after first hit + 7E009301 + (NG) Infinite health C9C3-3F2D + + (NG) Infinite Ninja Power + 7E006463 + + + (NG) Infinite lives + C9CF-CFBF + (NG) Infinite time DD81-1F97 @@ -33933,17 +34203,30 @@ D6C5-4DF4 - (NG) Infinite lives - C9CF-CFBF + (NG) Auto kill enemies that are close by + 849F9280 + 849F9F80 (NGII) Invincibility 1DB4-CD4D + + (NGII) Invincibility (alt) + 7E006825 + (NGII) Infinite health C9BB-441F + + (NGII) Infinite Ninja Power + 7E00AE63 + + + (NGII) Infinite lives + C961-3D37 + (NGII) Infinite time C96F-1FC7 @@ -33957,21 +34240,21 @@ D6B3-144F - (NGII) Start every life with two shadow ninjas + (NGII) Start every life with two Shadow Ninjas DEB9-14C4 - (NGII) Throwing stars doesn't use energy (ignore counter) + (NGII) Always have Fireball + 7E007D02 + + + (NGII) Throwing stars don't use Ninja Power (ignore counter) DFC4-1F47 (NGII) Start with 1 life DDB3-C4CF - - (NGII) Infinite lives - C961-3D37 - (NGIII) Invincibility DDAB-3FE7 @@ -33980,16 +34263,28 @@ (NGIII) Infinite health C92A-CFED + + (NGIII) Infinite lives + CB69-CD57 + + + (NGIII) Infinite lives (alt) + 7E00C403 + (NGIII) Infinite time C927-17EF - (NGIII) Almost infinite ninja power + (NGIII) Infinite Ninja Power + 7E00CD63 + + + (NGIII) Almost infinite Ninja Power C9A7-C757 - (NGIII) Start with 99 weapon points (ignore counter) + (NGIII) Start with 99 Ninja Power (ignore counter) 176C-CDE7 @@ -34004,10 +34299,6 @@ (NGIII) Start with 9 lives D66B-CF87 - - (NGIII) Infinite lives - CB69-CD57 - (NGIII) Start with very little health DF68-CD77 @@ -34907,7 +35198,7 @@ 7E050603 - Have All 3 Cartridges + Have all 3 Cartridges 7E050707 @@ -35270,7 +35561,7 @@ 7E174F25 - Chezni starts with 255 max. HP + Chezni starts with 255 max HP EEEB-6D1F @@ -35422,7 +35713,7 @@ FBEA-6F4D - Chezni starts with Rft shield + Chezni starts with Rft shield 59EA-6F4D @@ -36338,6 +36629,11 @@ Power Piggs of the Dark Age (USA) + + Invincibility + 1DB7-C40F + 6DB1-CF0D + Partial invincibility 7E06A43F @@ -36354,6 +36650,11 @@ Infinite Doughnuts 7E150509 + + Multi-jump + 4066-CFA7 + D5EA-44DC + Start on level Beautiful Downtown Pigg 7E187400 @@ -45179,15 +45480,24 @@ Super Castlevania IV (USA) Invincibility + 2D2B-6764 + 2DCD-D764 + + + Invincibility (blinking) 608E-D7FB C92C-6DD4 - Invincibility (blinking) + Invincibility (blinking) (alt) 7E00BC0A Infinite health + C22F-07D7 + + + Infinite health (alt) 7E13F410 @@ -45206,6 +45516,10 @@ Infinite time (alt) 7E13F099 + + Infinite lives + C96C-DFD6 + Infinite shots for most weapons A689-0FD7 @@ -45346,6 +45660,13 @@ 7E127B89 + + Super Donkey Kong 2 - Dixie & Diddy (Japan) + + Invincibility + 4022-431D + + Super Double Dragon (USA) @@ -46023,7 +46344,7 @@ (SMB) Multi-jump - 23C2BB02 + 2D8E-D7D2 (SMB) Run without holding the dash button @@ -46167,22 +46488,12 @@ (SMW) Always have Yoshi 7E0DC101 - - (SMW) Yoshi is invincible - 89E4-AFD9 - 89C6-D4DB - (SMW) Infinite flying time for Yoshi C2EC-0700 - (SMW) Collecting a Yoshi Coin gives you Yoshi's Wings - B9E5-A4AD - F8E5-A7DD - - - (SMW) Jump in mid air and float down (disable in water and to get on Yoshi) + (SMW) Multi-jump and float down (disable in water and to get on Yoshi) 7E147101 @@ -46284,6 +46595,24 @@ 7EFA0D63 7EFA0C63 + + Infinite Coins + 7FF8AFE7 + 7FF8B003 + + + Infinite Frog Coins + 7FF8B3E7 + 7FF8B403 + + + 255 EXP per battle (disable before going to the world map or using the menu button) + 7EFA02FF + + + Always Mario's turn + 7E070200 + Infinite tries in action sequences A98E-7707 @@ -46654,7 +46983,7 @@ D2E5-A7AD - Jump in mid air and float down (disable in water and to get on Yoshi) + Multi-jump and float down (disable in water and to get on Yoshi) 7E147101 @@ -47198,6 +47527,11 @@ Have all Boots and regular Bombs 7E09A5FF + + Automatically collect secret and/or special items in current room (Missiles, Energy Tanks, ect) + 402B-3DB7 + 402E-3FF7 + Maximum 999 Missiles 7E09C8E7 @@ -47725,13 +48059,52 @@ Super R-Type (USA) - Invincibility + Start with Invincibility (blinking) + 82C2-DF61 + + + Start with Invincibility (blinking) (alt) 7E15BD00 Infinite lives C2C7-6D0F + + Spiral motion gun takes less time to power up + 6D80-6DD1 + DD80-6D01 + + + Spiral motion gun takes much less time to power up + DD80-6DA1 + + + Spiral motion gun can't get over-charged + 6D84-6F01 + + + All FORCE satellites have 1 unit of power (but can't exceed 1 unit) + DD68-6DDB + CB6C-67AB + 7D68-6D6B + + + All FORCE satellites have 2 units of power (but can't exceed 2 units) + DF68-6DDB + CB6C-67AB + 7D68-6D6B + + + All FORCE satellites have 3 units of power + D468-6DDB + CB6C-67AB + 7D68-6D6B + + + Once FORCE has been obtained, keep it forever + C267-A4D9 + Start with 1 life instead of 3 DF66-0F00 @@ -47804,41 +48177,6 @@ CB6C-A7D9 DD6C-A769 - - Spiral motion gun takes less time to power up - 6D80-6DD1 - DD80-6D01 - - - Spiral motion gun takes much less time to power up - DD80-6DA1 - - - Spiral motion gun can't get over-charged - 6D84-6F01 - - - All FORCE satellites have 1 unit of power (but can't exceed 1 unit) - DD68-6DDB - CB6C-67AB - 7D68-6D6B - - - All FORCE satellites have 2 units of power (but can't exceed 2 units) - DF68-6DDB - CB6C-67AB - 7D68-6D6B - - - All FORCE satellites have 3 units of power - D468-6DDB - CB6C-67AB - 7D68-6D6B - - - Once FORCE has been obtained, keep it forever - C267-A4D9 - Super Scope 6 (USA) @@ -50166,6 +50504,18 @@ TKO Super Championship Boxing (USA) + + Infinite punch meters - both players + 4088-AF00 + + + Infinite punch meter - P1 + 40BF-04DD + + + Infinite punch meter - P2 + 40B0-076D + 9 minutes per round DB60-A7D4 @@ -50188,18 +50538,6 @@ D7B9-04DD D765-A764 - - Infinite punch meters - both players - 4088-AF00 - - - Infinite punch meter - P1 - 40BF-04DD - - - Infinite punch meter - P2 - 40B0-076D - Taz-Mania (USA) (Rev 1) @@ -51600,6 +51938,11 @@ Time Trax (USA) + + Invincibility + 84E948FF + 8187FFFF + Infinite health C2E8-1F60 @@ -51741,6 +52084,22 @@ Tiny Toon Adventures - Buster Busts Loose! (USA) + + Infinite health (when hit, a fake empty heart appears) + 3CE9-448A + + + Infinite dash meter + C927-47AD + + + Infinite lives (except football level) + DD34-37D7 + + + Infinite lives (football level) + DDC0-3F07 + Start with 1 life DF6F-14DF @@ -51789,14 +52148,6 @@ Continue with 99 lives BBC0-CFAF - - Infinite lives - except football level - DD34-37D7 - - - Infinite lives - football level - DDC0-3F07 - Start with no continues on normal level - handicap DD6D-47D0 @@ -51837,18 +52188,10 @@ Start with 5 hearts on Children or Normal difficulty levels, 3 on Challenge D9CD-4FAF - - Infinite health (when hit, a fake empty heart appears) - 3CE9-448A - Five heart maximum on challenge level D184-C4A1 - - Dash meter doesn't go down - C927-47AD - 1-up gives 2-up (not on mystery weight challenge level, or by collecting stars) D46B-CFAF @@ -54692,10 +55035,6 @@ WeaponLord (USA) - - Play as Zarak in story mode - D13C-561B - Infinite health - P1 5C1A-7B3B @@ -54704,6 +55043,14 @@ Infinite health - both players C21A-7BCB + + Infinite time + C204-56C5 + + + Play as Zarak in story mode + D13C-561B + Wheel of Fortune (USA) @@ -54881,6 +55228,18 @@ Wizard of Oz, The (USA) + + Invincibility + 2D8D-07A5 + + + Infinite health + 823C-AD66 + + + Infinite lives + 223E-DDAF + Infinite health - Dorthy 7E01E500 diff --git a/bsnes/libco/libco.c b/bsnes/libco/libco.c index dd020fe3..55676263 100755 --- a/bsnes/libco/libco.c +++ b/bsnes/libco/libco.c @@ -8,8 +8,8 @@ #include "x86.c" #elif defined(__GNUC__) && defined(__amd64__) #include "amd64.c" -#elif defined(__GNUC__) && defined(__powerpc__) && defined(__ELF__) - #include "ppc-elf.c" +#elif defined(__GNUC__) && defined(_ARCH_PPC) + #include "ppc.c" #elif defined(__GNUC__) #include "sjlj.c" #elif defined(_MSC_VER) && defined(_M_IX86) diff --git a/bsnes/libco/libco.h b/bsnes/libco/libco.h index b1b49a29..deb954fb 100755 --- a/bsnes/libco/libco.h +++ b/bsnes/libco/libco.h @@ -1,6 +1,6 @@ /* libco - version: 0.15 (2009-10-12) + version: 0.16 (2010-12-24) license: public domain */ diff --git a/bsnes/libco/ppc-elf.c b/bsnes/libco/ppc-elf.c deleted file mode 100755 index 5740f77f..00000000 --- a/bsnes/libco/ppc-elf.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * libco.ppc-elf - * author: Kernigh - * license: public domain - * - * PowerPC 32-bit ELF implementation of libco (for compile with GCC), - * ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb. - * This ELF version works for OpenBSD, and might also work for FreeBSD, - * NetBSD and Linux. - * - * Note 1: This implementation does not handle the AltiVec/VMX - * registers, because the ELF ABI does not mention them, - * and my OpenBSD system is not using them. - * - * Note 2: If you want position-independent code, then you must - * define __PIC__. gcc -fpic or -fPIC defines __PIC__, but - * gcc -fpie or -fPIE might not. If you want to use -fpie - * or -fPIE, then you might need a manual definition: - * gcc -fpie -D__PIC__=1 - * gcc -fPIE -D__PIC__=2 - * - * The ELF ABI is "System V Application Binary Interface, PowerPC - * Processor Supplement", which you can get from - * - * (PDF file, hosted by Linux Foundation). - * - * ELF and Mac OS X use similar conventions to allocate the registers, - * and to pass arguments and return values through registers. The main - * differences are that ELF has a slightly different stack format, that - * symbols are different (and without an extra underscore at the start), - * and that the assembly syntax is different. - * - * A function may destroy the values of volatile registers, but must - * preserve the values of nonvolatile registers. So the co_switch() - * function only saves the nonvolatile registers. - * - * [nonvolatile registers in ELF] - * %r1, %r14..%r31 - * %f14..%f31 - * %cr2..%cr4 in cr - * - * [volatile registers in ELF] - * %r0, %r3..%r10 - * %f0..%f13 - * %cr0, %cr1, %cr5..%cr7 in cr - * ctr, lr, xer - * - * lr (link register) is volatile, but it contains the return address, - * so co_switch must save lr. - * - * %r13 is the small data pointer. This is constant across threads, so - * co_switch() does not touch %r13. - * - * %r2 is a reserved register, so co_switch() does not touch %r2. Some - * systems might borrow an idea from the PowerPC Embedded ABI, and might - * use %r2 as a small read-only data pointer, which is constant across - * threads. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void * cothread_t; - -/* - * co_active_context is either in a global offset table (if we are - * compiling -fPIC or -fPIE) or has an absolute position. - */ -static void *co_main_stack_pointer; -static cothread_t co_active_context = &co_main_stack_pointer; - -extern cothread_t co_active() { - return co_active_context; -} - -/* - * Embedded assembly. - * - * We are not using the percent-sign substitution feature, - * so we must write "%r1", not "%%r1". - * - * We always write 'bl malloc@plt', not 'bl malloc'. The '@plt' - * is necessary in position-indepent code and seems to have no - * significant effect in fixed-position code. - * - * We never use the 'lmw' or 'stmw' instructions. The ELF ABI - * mentions that these instructions "are usually slower than - * a sequence of other instructions that have the same effect." - * We instead use sequences of 'lwz' or 'stz' instructions. - */ -__asm__("\n" -"### embedded assembly \n" -".section \".text\" \n" -" .balign 4 \n" -" \n" -/* - * void co_switch(co_thread to %r3) - * - * Allocate our stack frame of 240 bytes: - * Old New Value - * 4(%r1) 244(%r1) return address, used by us - * 0(%r1) 240(%r1) frame pointer - * 232(%r1) %f31 - * 224(%r1) %f30 - * ... - * 96(%r1) %f14 - * 92(%r1) %r31 - * 88(%r1) %r30 - * ... - * 24(%r1) %r14 - * 20(%r1) condition register - * 8(%r1) padding of 12 bytes - * 4(%r1) return address, never used - * 0(%r1) frame pointer - * - * Save our registers in our stack frame. - * Save our stack pointer in 0(%r4). - * Switch to the stack of the other thread. - * Restore registers and return. - */ -" .globl co_switch \n" -" .type co_switch, @function \n" -"co_switch: \n" -" mflr %r0 # %r0 = return address \n" -" mfcr %r9 # %r9 = condition register \n" -" stwu %r1, -240(%r1) # allocate stack frame \n" -" \n" -" stw %r0, 244(%r1) # save return address \n" -" stfd %f31, 232(%r1) # save floating-point regs \n" -" stfd %f30, 224(%r1) \n" -" stfd %f29, 216(%r1) \n" -" stfd %f28, 208(%r1) \n" -" stfd %f27, 200(%r1) \n" -" stfd %f26, 192(%r1) \n" -" stfd %f25, 184(%r1) \n" -" stfd %f24, 176(%r1) \n" -" stfd %f23, 168(%r1) \n" -" stfd %f22, 160(%r1) \n" -" stfd %f21, 152(%r1) \n" -" stfd %f20, 144(%r1) \n" -" stfd %f19, 136(%r1) \n" -" stfd %f18, 128(%r1) \n" -" stfd %f17, 120(%r1) \n" -" stfd %f16, 112(%r1) \n" -" stfd %f16, 104(%r1) \n" -" stfd %f14, 96(%r1) \n" -" stw %r31, 92(%r1) # save general-purpose regs \n" -" stw %r30, 88(%r1) \n" -" stw %r29, 84(%r1) \n" -" stw %r28, 80(%r1) \n" -" stw %r27, 76(%r1) \n" -" stw %r26, 72(%r1) \n" -" stw %r25, 68(%r1) \n" -" stw %r24, 64(%r1) \n" -" stw %r23, 60(%r1) \n" -" stw %r22, 56(%r1) \n" -" stw %r21, 52(%r1) \n" -" stw %r20, 48(%r1) \n" -" stw %r19, 44(%r1) \n" -" stw %r18, 40(%r1) \n" -" stw %r17, 36(%r1) \n" -" stw %r16, 32(%r1) \n" -" stw %r15, 28(%r1) \n" -" stw %r14, 24(%r1) \n" -" stw %r9, 20(%r1) # save condition reg \n" -" \n" -" # save current context, set new context \n" -" # %r4 = co_active_context \n" -" # co_active_context = %r3 \n" -#if __PIC__ == 2 -" # position-independent code, large model (-fPIC) \n" -" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" -" mflr %r8 # %r8 = address of got \n" -" addis %r7, %r8, co_active_context@got@ha \n" -" lwz %r6, co_active_context@got@l(%r7) \n" -" lwz %r4, 0(%r6) \n" -" stw %r3, 0(%r6) \n" -#elif __PIC__ == 1 -" # position-independent code, small model (-fpic) \n" -" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" -" mflr %r8 # %r8 = address of got \n" -" lwz %r7, co_active_context@got(%r8) \n" -" lwz %r4, 0(%r7) \n" -" stw %r3, 0(%r7) \n" -#else -" # fixed-position code \n" -" lis %r8, co_active_context@ha \n" -" lwz %r4, co_active_context@l(%r8) \n" -" stw %r3, co_active_context@l(%r8) \n" -#endif -" \n" -" # save current stack pointer \n" -" stw %r1, 0(%r4) \n" -" # get new stack pointer \n" -" lwz %r1, 0(%r3) \n" -" \n" -" lwz %r0, 244(%r1) # get return address \n" -" lfd %f31, 232(%r1) # restore floating-point regs \n" -" lfd %f30, 224(%r1) \n" -" lfd %f29, 216(%r1) \n" -" lfd %f28, 208(%r1) \n" -" lfd %f27, 200(%r1) \n" -" lfd %f26, 192(%r1) \n" -" lfd %f25, 184(%r1) \n" -" lfd %f24, 176(%r1) \n" -" lfd %f23, 168(%r1) \n" -" lfd %f22, 160(%r1) \n" -" lfd %f21, 152(%r1) \n" -" lfd %f20, 144(%r1) \n" -" lfd %f19, 136(%r1) \n" -" lfd %f18, 128(%r1) \n" -" lfd %f17, 120(%r1) \n" -" lfd %f16, 112(%r1) \n" -" lfd %f16, 104(%r1) \n" -" lfd %f14, 96(%r1) \n" -" lwz %r31, 92(%r1) # restore general-purpose regs \n" -" lwz %r30, 88(%r1) \n" -" lwz %r29, 84(%r1) \n" -" lwz %r28, 80(%r1) \n" -" lwz %r27, 76(%r1) \n" -" lwz %r26, 72(%r1) \n" -" lwz %r25, 68(%r1) \n" -" lwz %r24, 64(%r1) \n" -" lwz %r23, 60(%r1) \n" -" lwz %r22, 56(%r1) \n" -" lwz %r21, 52(%r1) \n" -" lwz %r20, 48(%r1) \n" -" lwz %r19, 44(%r1) \n" -" lwz %r18, 40(%r1) \n" -" lwz %r17, 36(%r1) \n" -" lwz %r16, 32(%r1) \n" -" lwz %r15, 28(%r1) \n" -" lwz %r14, 24(%r1) \n" -" lwz %r9, 20(%r1) # get condition reg \n" -" \n" -" addi %r1, %r1, 240 # free stack frame \n" -" mtlr %r0 # restore return address \n" -" mtcr %r9 # restore condition register \n" -" blr # return \n" -" .size co_switch, . - co_switch \n" -" \n" -/* - * cothread_t %r3 co_create(unsigned int stack_size %r3, - * void (*coentry %r4)()) - * - * Allocate a new stack, such that when you co_switch to that - * stack, then co_switch returns to coentry. - */ -" .globl co_create \n" -" .type co_create, @function \n" -"co_create: \n" -" mflr %r0 # %r0 = return address \n" -" stwu %r1, -16(%r1) # allocate my stack frame \n" -" stw %r0, 20(%r1) # save return address \n" -" stw %r31, 12(%r1) # save %r31 \n" -" stw %r30, 8(%r1) # save %r30 \n" -" \n" -" mr %r30, %r3 # %r30 = stack_size \n" -" mr %r31, %r4 # %r31 = coentry \n" -" \n" -" # Call malloc(stack_size %r3) to allocate stack; \n" -" # malloc() probably uses good alignment. \n" -" # \n" -" bl malloc@plt # returns %r3 = low end \n" -" cmpwi %r3, 0 # if returned NULL, \n" -" beq- 1f # then abort \n" -" \n" -" # we return %r3 = low end of stack \n" -" add %r4, %r3, %r30 # %r4 = high end of stack \n" -" \n" -" # uncomment if malloc() uses wrong alignment \n" -" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n" -" \n" - /* - * Allocate two stack frames: - * 16 bytes for stack frame with return address - * 240 bytes for co_switch stack frame - * - * Old New Value - * -8(%r4) 248(%r5) padding of 8 bytes - * -12(%r4) 244(%r5) return address = coentry - * -16(%r4) 240(%r5) frame pointer = NULL - * 232(%r5) %f31 = 0 - * ... - * 20(%r5) condition register = 0 - * 0(%r5) frame pointer - */ -" li %r9, (240-20)/4+1 \n" -" addi %r5, %r4, -16 # allocate first stack frame \n" -" li %r0, 0 \n" -" stwu %r5, -240(%r5) # allocate second stack frame \n" -" li %r8, 20 \n" -" mtctr %r9 # loop %r9 times \n" -"2: # loop to store zero to 20(%r5) through 240(%r5) \n" -" stwx %r0, %r5, %r8 \n" -" addi %r8, %r8, 4 # index += 4 \n" -" bdnz+ 2b # ctr -= 1, branch if nonzero \n" -" \n" -" stw %r31, 244(%r5) # return address = coentry \n" -" stw %r5, 0(%r3) # save stack pointer \n" -" \n" -" lwz %r0, 20(%r1) # get return address \n" -" lwz %r31, 12(%r1) # restore %r31 \n" -" lwz %r30, 8(%r1) # restore %r30 \n" -" mtlr %r0 # restore return address \n" -" addi %r1, %r1, 16 # free stack frame \n" -" blr # return \n" -" \n" -"1: b abort@plt # branch 1f to abort \n" -" .size co_create, . - co_create \n" -" \n" -/* - * void co_delete(cothread_t) => void free(void *) - */ -" .globl co_delete \n" -" .type co_delete, @function \n" -"co_delete: \n" -" b free@plt \n" -" \n" -); - -#ifdef __cplusplus -} -#endif diff --git a/bsnes/libco/ppc.c b/bsnes/libco/ppc.c new file mode 100755 index 00000000..a6028fdb --- /dev/null +++ b/bsnes/libco/ppc.c @@ -0,0 +1,407 @@ +/* + libco.ppc (2010-10-17) + author: blargg + license: public domain +*/ + +/* PowerPC 32/64 using embedded or external asm, with optional +floating-point and AltiVec save/restore */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) + +#if LIBCO_MPROTECT + #include + #include +#endif + +/* State format (offsets in 32-bit words) + ++0 Pointer to swap code + Rest of function descriptor for entry function ++8 PC ++10 SP + Special regs + GPRs + FPRs + VRs + stack +*/ + +enum { state_size = 1024 }; +enum { above_stack = 2048 }; +enum { stack_align = 256 }; + +static thread_local cothread_t co_active_handle = 0; + +/**** Determine environment ****/ + +#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) + +/* Whether function calls are indirect through a descriptor, +or are directly to function */ +#ifndef LIBCO_PPCDESC + #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) + #define LIBCO_PPCDESC 1 + #endif +#endif + +#ifdef LIBCO_PPC_ASM + + #ifdef __cplusplus + extern "C" + #endif + + /* Swap code is in ppc.S */ + void co_swap_asm( cothread_t, cothread_t ); + #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) + +#else + +/* Swap code is here in array. Please leave dieassembly comments, +as they make it easy to see what it does, and reorder instructions +if one wants to see whether that improves performance. */ +static const uint32_t libco_ppc_code [] = { +#if LIBCO_PPC64 + 0x7d000026, /* mfcr r8 */ + 0xf8240028, /* std r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0xf9c40048, /* std r14,72(r4) */ + 0xf9e40050, /* std r15,80(r4) */ + 0xfa040058, /* std r16,88(r4) */ + 0xfa240060, /* std r17,96(r4) */ + 0xfa440068, /* std r18,104(r4) */ + 0xfa640070, /* std r19,112(r4) */ + 0xfa840078, /* std r20,120(r4) */ + 0xfaa40080, /* std r21,128(r4) */ + 0xfac40088, /* std r22,136(r4) */ + 0xfae40090, /* std r23,144(r4) */ + 0xfb040098, /* std r24,152(r4) */ + 0xfb2400a0, /* std r25,160(r4) */ + 0xfb4400a8, /* std r26,168(r4) */ + 0xfb6400b0, /* std r27,176(r4) */ + 0xfb8400b8, /* std r28,184(r4) */ + 0xfba400c0, /* std r29,192(r4) */ + 0xfbc400c8, /* std r30,200(r4) */ + 0xfbe400d0, /* std r31,208(r4) */ + 0xf9240020, /* std r9,32(r4) */ + 0xe8e30020, /* ld r7,32(r3) */ + 0xe8230028, /* ld r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0xe9c30048, /* ld r14,72(r3) */ + 0xe9e30050, /* ld r15,80(r3) */ + 0xea030058, /* ld r16,88(r3) */ + 0xea230060, /* ld r17,96(r3) */ + 0xea430068, /* ld r18,104(r3) */ + 0xea630070, /* ld r19,112(r3) */ + 0xea830078, /* ld r20,120(r3) */ + 0xeaa30080, /* ld r21,128(r3) */ + 0xeac30088, /* ld r22,136(r3) */ + 0xeae30090, /* ld r23,144(r3) */ + 0xeb030098, /* ld r24,152(r3) */ + 0xeb2300a0, /* ld r25,160(r3) */ + 0xeb4300a8, /* ld r26,168(r3) */ + 0xeb6300b0, /* ld r27,176(r3) */ + 0xeb8300b8, /* ld r28,184(r3) */ + 0xeba300c0, /* ld r29,192(r3) */ + 0xebc300c8, /* ld r30,200(r3) */ + 0xebe300d0, /* ld r31,208(r3) */ + 0x7ccff120, /* mtcr r6 */ +#else + 0x7d000026, /* mfcr r8 */ + 0x90240028, /* stw r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0x91a4003c, /* stw r13,60(r4) */ + 0x91c40040, /* stw r14,64(r4) */ + 0x91e40044, /* stw r15,68(r4) */ + 0x92040048, /* stw r16,72(r4) */ + 0x9224004c, /* stw r17,76(r4) */ + 0x92440050, /* stw r18,80(r4) */ + 0x92640054, /* stw r19,84(r4) */ + 0x92840058, /* stw r20,88(r4) */ + 0x92a4005c, /* stw r21,92(r4) */ + 0x92c40060, /* stw r22,96(r4) */ + 0x92e40064, /* stw r23,100(r4) */ + 0x93040068, /* stw r24,104(r4) */ + 0x9324006c, /* stw r25,108(r4) */ + 0x93440070, /* stw r26,112(r4) */ + 0x93640074, /* stw r27,116(r4) */ + 0x93840078, /* stw r28,120(r4) */ + 0x93a4007c, /* stw r29,124(r4) */ + 0x93c40080, /* stw r30,128(r4) */ + 0x93e40084, /* stw r31,132(r4) */ + 0x91240020, /* stw r9,32(r4) */ + 0x80e30020, /* lwz r7,32(r3) */ + 0x80230028, /* lwz r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0x81a3003c, /* lwz r13,60(r3) */ + 0x81c30040, /* lwz r14,64(r3) */ + 0x81e30044, /* lwz r15,68(r3) */ + 0x82030048, /* lwz r16,72(r3) */ + 0x8223004c, /* lwz r17,76(r3) */ + 0x82430050, /* lwz r18,80(r3) */ + 0x82630054, /* lwz r19,84(r3) */ + 0x82830058, /* lwz r20,88(r3) */ + 0x82a3005c, /* lwz r21,92(r3) */ + 0x82c30060, /* lwz r22,96(r3) */ + 0x82e30064, /* lwz r23,100(r3) */ + 0x83030068, /* lwz r24,104(r3) */ + 0x8323006c, /* lwz r25,108(r3) */ + 0x83430070, /* lwz r26,112(r3) */ + 0x83630074, /* lwz r27,116(r3) */ + 0x83830078, /* lwz r28,120(r3) */ + 0x83a3007c, /* lwz r29,124(r3) */ + 0x83c30080, /* lwz r30,128(r3) */ + 0x83e30084, /* lwz r31,132(r3) */ + 0x7ccff120, /* mtcr r6 */ +#endif + +#ifndef LIBCO_PPC_NOFP + 0xd9c400e0, /* stfd f14,224(r4) */ + 0xd9e400e8, /* stfd f15,232(r4) */ + 0xda0400f0, /* stfd f16,240(r4) */ + 0xda2400f8, /* stfd f17,248(r4) */ + 0xda440100, /* stfd f18,256(r4) */ + 0xda640108, /* stfd f19,264(r4) */ + 0xda840110, /* stfd f20,272(r4) */ + 0xdaa40118, /* stfd f21,280(r4) */ + 0xdac40120, /* stfd f22,288(r4) */ + 0xdae40128, /* stfd f23,296(r4) */ + 0xdb040130, /* stfd f24,304(r4) */ + 0xdb240138, /* stfd f25,312(r4) */ + 0xdb440140, /* stfd f26,320(r4) */ + 0xdb640148, /* stfd f27,328(r4) */ + 0xdb840150, /* stfd f28,336(r4) */ + 0xdba40158, /* stfd f29,344(r4) */ + 0xdbc40160, /* stfd f30,352(r4) */ + 0xdbe40168, /* stfd f31,360(r4) */ + 0xc9c300e0, /* lfd f14,224(r3) */ + 0xc9e300e8, /* lfd f15,232(r3) */ + 0xca0300f0, /* lfd f16,240(r3) */ + 0xca2300f8, /* lfd f17,248(r3) */ + 0xca430100, /* lfd f18,256(r3) */ + 0xca630108, /* lfd f19,264(r3) */ + 0xca830110, /* lfd f20,272(r3) */ + 0xcaa30118, /* lfd f21,280(r3) */ + 0xcac30120, /* lfd f22,288(r3) */ + 0xcae30128, /* lfd f23,296(r3) */ + 0xcb030130, /* lfd f24,304(r3) */ + 0xcb230138, /* lfd f25,312(r3) */ + 0xcb430140, /* lfd f26,320(r3) */ + 0xcb630148, /* lfd f27,328(r3) */ + 0xcb830150, /* lfd f28,336(r3) */ + 0xcba30158, /* lfd f29,344(r3) */ + 0xcbc30160, /* lfd f30,352(r3) */ + 0xcbe30168, /* lfd f31,360(r3) */ +#endif + +#ifdef __ALTIVEC__ + 0x7ca042a6, /* mfvrsave r5 */ + 0x39040180, /* addi r8,r4,384 */ + 0x39240190, /* addi r9,r4,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x90a40034, /* stw r5,52(r4) */ + 0x4182005c, /* beq- 2 */ + 0x7e8041ce, /* stvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea049ce, /* stvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec041ce, /* stvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee049ce, /* stvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0041ce, /* stvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2049ce, /* stvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4041ce, /* stvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6049ce, /* stvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8041ce, /* stvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa049ce, /* stvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc041ce, /* stvx v30,r0,r8 */ + 0x7fe049ce, /* stvx v31,r0,r9 */ + 0x80a30034,/*2:lwz r5,52(r3) */ + 0x39030180, /* addi r8,r3,384 */ + 0x39230190, /* addi r9,r3,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x7ca043a6, /* mtvrsave r5 */ + 0x4d820420, /* beqctr */ + 0x7e8040ce, /* lvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea048ce, /* lvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec040ce, /* lvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee048ce, /* lvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0040ce, /* lvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2048ce, /* lvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4040ce, /* lvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6048ce, /* lvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8040ce, /* lvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa048ce, /* lvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc040ce, /* lvx v30,r0,r8 */ + 0x7fe048ce, /* lvx v31,r0,r9 */ +#endif + + 0x4e800420, /* bctr */ +}; + + #if LIBCO_PPCDESC + /* Function call goes through indirect descriptor */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) + #else + /* Function call goes directly to code */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) + #endif + +#endif + +static uint32_t* co_create_( unsigned size, uintptr_t entry ) +{ + uint32_t* t = (uint32_t*) malloc( size ); + + (void) entry; + + #if LIBCO_PPCDESC + if ( t ) + { + /* Copy entry's descriptor */ + memcpy( t, (void*) entry, sizeof (void*) * 3 ); + + /* Set function pointer to swap routine */ + #ifdef LIBCO_PPC_ASM + *(const void**) t = *(void**) &co_swap_asm; + #else + *(const void**) t = libco_ppc_code; + #endif + } + #endif + + return t; +} + +cothread_t co_create( unsigned int size, void (*entry_)( void ) ) +{ + uintptr_t entry = (uintptr_t) entry_; + uint32_t* t = NULL; + + /* Be sure main thread was successfully allocated */ + if ( co_active() ) + { + size += state_size + above_stack + stack_align; + t = co_create_( size, entry ); + } + + if ( t ) + { + uintptr_t sp; + int shift; + + /* Save current registers into new thread, so that any special ones will + have proper values when thread is begun */ + CO_SWAP_ASM( t, t ); + + #if LIBCO_PPCDESC + /* Get real address */ + entry = (uintptr_t) *(void**) entry; + #endif + + /* Put stack near end of block, and align */ + sp = (uintptr_t) t + size - above_stack; + sp -= sp % stack_align; + + /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. So, we manually write pointers at the proper size. We always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts + and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* Set up so entry will be called on next swap */ + t [8] = (uint32_t) (entry >> shift >> shift); + t [9] = (uint32_t) entry; + + t [10] = (uint32_t) (sp >> shift >> shift); + t [11] = (uint32_t) sp; + } + + return t; +} + +void co_delete( cothread_t t ) +{ + free( t ); +} + +static void co_init_( void ) +{ + #if LIBCO_MPROTECT + /* TODO: pre- and post-pad PPC code so that this doesn't make other + data executable and writable */ + long page_size = sysconf( _SC_PAGESIZE ); + if ( page_size > 0 ) + { + uintptr_t align = page_size; + uintptr_t begin = (uintptr_t) libco_ppc_code; + uintptr_t end = begin + sizeof libco_ppc_code; + + /* Align beginning and end */ + end += align - 1; + end -= end % align; + begin -= begin % align; + + mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); + } + #endif + + co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); +} + +cothread_t co_active() +{ + if ( !co_active_handle ) + co_init_(); + + return co_active_handle; +} + +void co_switch( cothread_t t ) +{ + cothread_t old = co_active_handle; + co_active_handle = t; + + CO_SWAP_ASM( t, old ); +} diff --git a/bsnes/libco/ppc.s b/bsnes/libco/ppc.s deleted file mode 100755 index d7f6b758..00000000 --- a/bsnes/libco/ppc.s +++ /dev/null @@ -1,478 +0,0 @@ -;***** -;libco.ppc (2007-11-29) -;author: Vas Crabb -;license: public domain -; -;cross-platform PowerPC implementation of libco -;special thanks to byuu for writing the original version -; -;[ABI compatibility] -;- gcc; mac os x; ppc -; -;[nonvolatile registers] -;- GPR1, GPR13 - GPR31 -;- FPR14 - FPR31 -;- V20 - V31 -;- VRSAVE, CR2 - CR4 -; -;[volatile registers] -;- GPR0, GPR2 - GPR12 -;- FPR0 - FPR13 -;- V0 - V19 -;- LR, CTR, XER, CR0, CR1, CR5 - CR7 -;***** - - -;Declare some target-specific stuff - - .section __TEXT,__text,regular,pure_instructions - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .machine ppc - - -;Constants - - .cstring - .align 2 - -_sysctl_altivec: - .ascii "hw.optional.altivec\0" - - -;Declare space for variables - -.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX -.lcomm _co_primary_buffer,1024,2 ;buffer (will be zeroed by loader) - - .data - .align 2 - -_co_active_context: - .long _co_primary_buffer - - - .text - .align 2 - - -;Declare exported names - -.globl _co_active -.globl _co_create -.globl _co_delete -.globl _co_switch - - -;***** -;extern "C" cothread_t co_active(); -;return = GPR3 -;***** - -_co_active: - mflr r0 ;GPR0 = return address - bcl 20,31,L_co_active$spb -L_co_active$spb: - mflr r2 ;GPR2 set for position-independance - addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 - lwz r3,lo16(_co_active_context-L_co_active$spb)(r3) - mtlr r0 ;LR = return address - blr ;return - - -;***** -;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); -;GPR3 = heapsize -;GPR4 = coentry -;return = GPR3 -;***** - -_co_create: - mflr r0 ;GPR0 = return address - stmw r30,-8(r1) ;save GPR30 and GPR31 - stw r0,8(r1) ;save return address - stwu r1,-(2*4+16+24)(r1) ;allocate 16 bytes for locals/parameters - -;create heap space (stack + register storage) - addi r31,r3,1024-24 ;subtract space for linkage - mr r30,r4 ;GPR30 = coentry - addi r3,r3,1024 ;allocate extra memory for contextual info - bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) - add r4,r3,r31 ;GPR4 points to top-of-stack - rlwinm r5,r4,0,0,27 ;force 16-byte alignment - -;store thread entry point + registers, so that first call to co_switch will execute coentry - stw r30,8(r5) ;store entry point - addi r6,0,2+19+18*2+12*4+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE - addi r0,0,0 - addi r7,0,4 ;start at 4(GPR5) - mtctr r6 -L_co_create$clear_loop: - stwx r0,r5,r7 ;clear a word - addi r7,r7,-4 ;increment pointer - bdnz L_co_create$clear_loop ;loop - stwu r5,-448(r5) ;store top of stack - -;initialize context memory heap and return - stw r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) - lwz r1,0(r1) ;deallocate stack frame - lwz r8,8(r1) ;fetch return address - lmw r30,-8(r1) ;restore GPR30 and GPR31 - mtlr r8 ;return address in LR - blr ;return - - -;***** -;extern "C" void co_delete(cothread_t cothread); -;GPR3 = cothread -;***** - -_co_delete: - b L_free$stub ;free(GPR3) - - -;***** -;extern "C" void co_switch(cothread_t cothread); -;GPR3 = cothread -;***** -; -;Frame looks like: -; -;Old New Value -; 8(r1) 456(r1) Saved LR -; 4(r1) 452(r1) Saved CR -; 0(r1) 448(r1) Old GPR1 -; -4(r1) 444(r1) Saved GPR31 -; -8(r1) 440(r1) Saved GPR30 -;... ... ... -; -72(r1) 376(r1) Saved GPR14 -; -76(r1) 372(r1) Saved GPR13 -; -80(r1) 368(r1) Saved VRSAVE -; -84(r1) 364(r1) +++ -; -88(r1) 360(r1) Saved FPR31 -; -92(r1) 356(r1) +++ -; -96(r1) 352(r1) Saved FPR30 -;... ... ... -;-212(r1) 236(r1) +++ -;-216(r1) 232(r1) Saved FPR15 -;-220(r1) 228(r1) +++ -;-224(r1) 224(r1) Saved FPR14 -;-228(r1) 220(r1) +++ value -;-232(r1) 216(r1) +++ len -;-236(r1) 212(r1) +++ -;-240(r1) 208(r1) Saved VR31 -;-244(r1) 204(r1) +++ -;-248(r1) 200(r1) +++ -;-252(r1) 196(r1) +++ -;-256(r1) 192(r1) Saved VR30 -;... ... ... -;-388(r1) 60(r1) +++ -;-392(r1) 56(r1) +++ -;-396(r1) 52(r1) +++ -;-400(r1) 48(r1) Saved VR21 -;-404(r1) 44(r1) +++ -;-408(r1) 40(r1) +++ Param 5 (GPR7) -;-412(r1) 36(r1) +++ Param 4 (GPR6) -;-416(r1) 32(r1) Saved VR20 Param 3 (GPR5) -;-420(r1) 28(r1) - Param 2 (GPR4) -;-424(r1) 24(r1) - Param 1 (GPR3) -;-428(r1) 20(r1) - Reserved -;-432(r1) 16(r1) - Reserved -;-436(r1) 12(r1) - Reserved -;-440(r1) 8(r1) - New LR -;-444(r1) 4(r1) - New CR -;-448(r1) 0(r1) Saved GPR1 - - -_co_switch: - stmw r13,-76(r1) ;save preserved GPRs - stfd f14,-224(r1) ;save preserved FPRs - stfd f15,-216(r1) - stfd f16,-208(r1) - stfd f17,-200(r1) - stfd f18,-192(r1) - stfd f19,-184(r1) - stfd f20,-176(r1) - stfd f21,-168(r1) - stfd f22,-160(r1) - stfd f23,-152(r1) - stfd f24,-144(r1) - stfd f25,-136(r1) - stfd f26,-128(r1) - stfd f27,-120(r1) - stfd f28,-112(r1) - stfd f29,-104(r1) - stfd f30,-96(r1) - stfd f31,-88(r1) - mflr r0 ;save return address - stw r0,8(r1) - mfcr r2 ;save condition codes - stw r2,4(r1) - stwu r1,-448(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) - - mr r30,r3 ;save new context pointer - bcl 20,31,L_co_switch$spb ;get address of co_active_context -L_co_switch$spb: - mflr r31 - - addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags - lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) - andis. r9,r8,0x8000 ;is it initialised? - bne+ L_co_switch$initialised - - addi r0,0,4 ;len = sizeof(int) - stw r0,216(r1) - addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" - addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) - addi r4,r1,220 ;GPR4 = &value - addi r5,r1,216 ;GPR5 = &len - addi r6,0,0 ;newp = 0 - addi r7,0,0 ;newlen = 0 - bl L_sysctlbyname$stub ;call sysctlbyname - lwz r2,220(r1) ;fetch result - addis r8,0,0x8000 ;set initialised bit - cmpwi cr5,r3,0 ;assume error means not present - cmpwi cr6,r2,0 ;test result - blt- cr5,L_co_switch$store_environ - beq cr6,L_co_switch$store_environ - oris r8,r8,0x4000 ;set the flag to say we have it! -L_co_switch$store_environ: - stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags -L_co_switch$initialised: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$save_no_vmx - mfspr r11,256 ;save VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - stw r11,368(r1) - beq L_co_switch$save_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,32 ;starting index - beq L_co_switch$save_skip_vr20 - stvx v20,r1,r2 ;save VR20 -L_co_switch$save_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$save_skip_vr21 - stvx v21,r1,r2 ;save VR21 -L_co_switch$save_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$save_skip_vr22 - stvx v22,r1,r2 ;save VR22 -L_co_switch$save_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$save_skip_vr23 - stvx v23,r1,r2 ;save VR23 -L_co_switch$save_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$save_skip_vr24 - stvx v24,r1,r2 ;save VR24 -L_co_switch$save_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$save_skip_vr25 - stvx v25,r1,r2 ;save VR25 -L_co_switch$save_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$save_skip_vr26 - stvx v26,r1,r2 ;save VR26 -L_co_switch$save_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$save_skip_vr27 - stvx v27,r1,r2 ;save VR27 -L_co_switch$save_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$save_skip_vr28 - stvx v28,r1,r2 ;save VR28 -L_co_switch$save_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$save_skip_vr29 - stvx v29,r1,r2 ;save VR29 -L_co_switch$save_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$save_skip_vr30 - stvx v30,r1,r2 ;save VR30 -L_co_switch$save_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$save_skip_vr31 - stvx v31,r1,r2 ;save VR31 -L_co_switch$save_skip_vr31: -L_co_switch$save_no_vmx: - - addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context - lwz r5,lo16(_co_active_context-L_co_switch$spb)(r4) - stw r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context - stw r1,0(r5) ;save current stack pointer - lwz r1,0(r30) ;get new stack pointer - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$restore_no_vmx - lwz r11,368(r1) ;restore VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - mtspr 256,r11 - beq L_co_switch$restore_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,32 ;starting index - beq L_co_switch$restore_skip_vr20 - lvx v20,r1,r2 ;restore VR20 -L_co_switch$restore_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$restore_skip_vr21 - lvx v21,r1,r2 ;restore VR21 -L_co_switch$restore_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$restore_skip_vr22 - lvx v22,r1,r2 ;restore VR22 -L_co_switch$restore_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$restore_skip_vr23 - lvx v23,r1,r2 ;restore VR23 -L_co_switch$restore_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$restore_skip_vr24 - lvx v24,r1,r2 ;restore VR24 -L_co_switch$restore_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$restore_skip_vr25 - lvx v25,r1,r2 ;restore VR25 -L_co_switch$restore_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$restore_skip_vr26 - lvx v26,r1,r2 ;restore VR26 -L_co_switch$restore_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$restore_skip_vr27 - lvx v27,r1,r2 ;restore VR27 -L_co_switch$restore_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$restore_skip_vr28 - lvx v28,r1,r2 ;restore VR28 -L_co_switch$restore_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$restore_skip_vr29 - lvx v29,r1,r2 ;restore VR29 -L_co_switch$restore_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$restore_skip_vr30 - lvx v30,r1,r2 ;restore VR30 -L_co_switch$restore_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$restore_skip_vr31 - lvx v31,r1,r2 ;restore VR31 -L_co_switch$restore_skip_vr31: -L_co_switch$restore_no_vmx: - - lwz r1,0(r1) ;deallocate stack frame - lwz r6,8(r1) ;return address in GPR6 - lwz r7,4(r1) ;condition codes in GPR7 - addi r0,0,0 ;make thread main crash if it returns - lmw r13,-76(r1) ;restore preserved GPRs - lfd f14,-224(r1) ;restore preserved FPRs - lfd f15,-216(r1) - lfd f16,-208(r1) - lfd f17,-200(r1) - lfd f18,-192(r1) - lfd f19,-184(r1) - lfd f20,-176(r1) - lfd f21,-168(r1) - lfd f22,-160(r1) - lfd f23,-152(r1) - lfd f24,-144(r1) - lfd f25,-136(r1) - lfd f26,-128(r1) - lfd f27,-120(r1) - lfd f28,-112(r1) - lfd f29,-104(r1) - lfd f30,-96(r1) - lfd f31,-88(r1) - mtlr r0 - mtctr r6 ;restore return address - mtcrf 32,r7 ;restore preserved condition codes - mtcrf 16,r7 - mtcrf 8,r7 - bctr ;return - - - -;Import external functions - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_malloc$stub: - .indirect_symbol _malloc - mflr r0 - bcl 20,31,L_malloc$spb -L_malloc$spb: - mflr r11 - addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) - mtlr r0 - lwzu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_malloc$lazy_ptr: - .indirect_symbol _malloc - .long dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_free$stub: - .indirect_symbol _free - mflr r0 - bcl 20,31,L_free$spb -L_free$spb: - mflr r11 - addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) - mtlr r0 - lwzu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_free$lazy_ptr: - .indirect_symbol _free - .long dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_sysctlbyname$stub: - .indirect_symbol _sysctlbyname - mflr r0 - bcl 20,31,L_sysctlbyname$spb -L_sysctlbyname$spb: - mflr r11 - addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) - mtlr r0 - lwzu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_sysctlbyname$lazy_ptr: - .indirect_symbol _sysctlbyname - .long dyld_stub_binding_helper - - -;This needs to be here! - - .subsections_via_symbols - diff --git a/bsnes/libco/ppc64.s b/bsnes/libco/ppc64.s deleted file mode 100755 index 2fb048d7..00000000 --- a/bsnes/libco/ppc64.s +++ /dev/null @@ -1,513 +0,0 @@ -;***** -;libco.ppc64 (2007-12-05) -;author: Vas Crabb -;license: public domain -; -;cross-platform 64-bit PowerPC implementation of libco -;special thanks to byuu for writing the original version -; -;[ABI compatibility] -;- gcc; mac os x; ppc64 -; -;[nonvolatile registers] -;- GPR1, GPR13 - GPR31 -;- FPR14 - FPR31 -;- V20 - V31 -;- VRSAVE, CR2 - CR4 -; -;[volatile registers] -;- GPR0, GPR2 - GPR12 -;- FPR0 - FPR13 -;- V0 - V19 -;- LR, CTR, XER, CR0, CR1, CR5 - CR7 -;***** - - -;Declare some target-specific stuff - - .section __TEXT,__text,regular,pure_instructions - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .machine ppc64 - - -;Constants - - .cstring - .align 3 - -_sysctl_altivec: - .ascii "hw.optional.altivec\0" - - -;Declare space for variables - -.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX -.lcomm _co_primary_buffer,1024,3 ;buffer (will be zeroed by loader) - - .data - .align 3 - -_co_active_context: - .quad _co_primary_buffer - - - .text - .align 2 - - -;Declare exported names - -.globl _co_active -.globl _co_create -.globl _co_delete -.globl _co_switch - - -;***** -;extern "C" cothread_t co_active(); -;return = GPR3 -;***** - -_co_active: - mflr r0 ;GPR0 = return address - bcl 20,31,L_co_active$spb -L_co_active$spb: - mflr r2 ;GPR2 set for position-independance - addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 - ld r3,lo16(_co_active_context-L_co_active$spb)(r3) - mtlr r0 ;LR = return address - blr ;return - - -;***** -;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); -;GPR3 = heapsize -;GPR4 = coentry -;return = GPR3 -;***** - -_co_create: - mflr r0 ;GPR0 = return address - std r30,-16(r1) ;save GPR30 and GPR31 - std r31,-8(r1) - std r0,16(r1) ;save return address - stdu r1,-(2*8+16+48)(r1) ;allocate 16 bytes for locals/parameters - -;create heap space (stack + register storage) - addi r31,r3,1024-48 ;subtract space for linkage - mr r30,r4 ;GPR30 = coentry - addi r3,r3,1024 ;allocate extra memory for contextual info - bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) - add r4,r3,r31 ;GPR4 points to top-of-stack - rldicr r5,r4,0,59 ;force 16-byte alignment - -;store thread entry point + registers, so that first call to co_switch will execute coentry - std r30,16(r5) ;store entry point - addi r6,0,2+19+18+12*2+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE - addi r0,0,0 - addi r7,0,8 ;start at 8(GPR5) - mtctr r6 -L_co_create$clear_loop: - stdx r0,r5,r7 ;clear a double - addi r7,r7,-8 ;increment pointer - bdnz L_co_create$clear_loop ;loop - stdu r5,-544(r5) ;store top of stack - -;initialize context memory heap and return - addis r9,0,0x8000 ;GPR13 not set (system TLS) - std r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) - stw r9,8(r3) ;this is a flag word - ld r1,0(r1) ;deallocate stack frame - ld r8,16(r1) ;fetch return address - ld r30,-16(r1) ;restore GPR30 and GPR31 - ld r31,-8(r1) - mtlr r8 ;return address in LR - blr ;return - - -;***** -;extern "C" void co_delete(cothread_t cothread); -;GPR3 = cothread -;***** - -_co_delete: - b L_free$stub ;free(GPR3) - - -;***** -;extern "C" void co_switch(cothread_t cothread); -;GPR3 = cothread -;***** -; -;Frame looks like: -; -;Old New Value -; 16(r1) 560(r1) Saved LR -; 8(r1) 552(r1) Saved CR -; 0(r1) 544(r1) Old GPR1 -; -8(r1) 536(r1) Saved GPR31 -; -16(r1) 528(r1) Saved GPR30 -;... ... ... -;-144(r1) 400(r1) Saved GPR14 -;-152(r1) 392(r1) Saved GPR13 -;-160(r1) 384(r1) Saved FPR31 -;-168(r1) 376(r1) Saved FPR30 -;... ... ... -;-288(r1) 256(r1) Saved FPR15 -;-296(r1) 248(r1) Saved FPR14 -;-304(r1) 240(r1) Saved VRSAVE -;-312(r1) 232(r1) +++ value -;-320(r1) 224(r1) Saved VR31 len -;-328(r1) 216(r1) +++ -;-336(r1) 208(r1) Saved VR30 -;... ... ... -;-456(r1) 88(r1) +++ -;-464(r1) 80(r1) Saved VR22 Param 5 (GPR7) -;-472(r1) 72(r1) +++ Param 4 (GPR6) -;-480(r1) 64(r1) Saved VR21 Param 3 (GPR5) -;-488(r1) 56(r1) +++ Param 2 (GPR4) -;-496(r1) 48(r1) Saved VR20 Param 1 (GPR3) -;-504(r1) 40(r1) - Reserved -;-512(r1) 32(r1) - Reserved -;-520(r1) 24(r1) - Reserved -;-528(r1) 16(r1) - New LR -;-536(r1) 8(r1) - New CR -;-544(r1) 0(r1) Saved GPR1 - - -_co_switch: - std r13,-152(r1) ;save preserved GPRs - std r14,-144(r1) - std r15,-136(r1) - std r16,-128(r1) - std r17,-120(r1) - std r18,-112(r1) - std r19,-104(r1) - std r20,-96(r1) - std r21,-88(r1) - std r22,-80(r1) - std r23,-72(r1) - std r24,-64(r1) - std r25,-56(r1) - std r26,-48(r1) - std r27,-40(r1) - std r28,-32(r1) - std r29,-24(r1) - std r30,-16(r1) - std r31,-8(r1) - mflr r0 ;save return address - std r0,16(r1) - mfcr r2 ;save condition codes - stw r2,8(r1) - stdu r1,-544(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) - stfd f14,248(r1) ;save preserved FPRs - stfd f15,256(r1) - stfd f16,264(r1) - stfd f17,272(r1) - stfd f18,280(r1) - stfd f19,288(r1) - stfd f20,296(r1) - stfd f21,304(r1) - stfd f22,312(r1) - stfd f23,320(r1) - stfd f24,328(r1) - stfd f25,336(r1) - stfd f26,344(r1) - stfd f27,352(r1) - stfd f28,360(r1) - stfd f29,368(r1) - stfd f30,376(r1) - stfd f31,384(r1) - - mr r30,r3 ;save new context pointer - bcl 20,31,L_co_switch$spb ;get address of co_active_context -L_co_switch$spb: - mflr r31 - - addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags - lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) - andis. r9,r8,0x8000 ;is it initialised? - bne+ L_co_switch$initialised - - addi r0,0,4 ;len = sizeof(int) - std r0,224(r1) - addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" - addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) - addi r4,r1,232 ;GPR4 = &value - addi r5,r1,224 ;GPR5 = &len - addi r6,0,0 ;newp = 0 - addi r7,0,0 ;newlen = 0 - bl L_sysctlbyname$stub ;call sysctlbyname - lwz r2,232(r1) ;fetch result - addis r8,0,0x8000 ;set initialised bit - cmpdi cr5,r3,0 ;assume error means not present - cmpwi cr6,r2,0 ;test result - blt- cr5,L_co_switch$store_environ - beq cr6,L_co_switch$store_environ - oris r8,r8,0x4000 ;set the flag to say we have it! -L_co_switch$store_environ: - stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags -L_co_switch$initialised: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$save_no_vmx - mfspr r11,256 ;save VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - stw r11,240(r1) - beq L_co_switch$save_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,48 ;starting index - beq L_co_switch$save_skip_vr20 - stvx v20,r1,r2 ;save VR20 -L_co_switch$save_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$save_skip_vr21 - stvx v21,r1,r2 ;save VR21 -L_co_switch$save_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$save_skip_vr22 - stvx v22,r1,r2 ;save VR22 -L_co_switch$save_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$save_skip_vr23 - stvx v23,r1,r2 ;save VR23 -L_co_switch$save_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$save_skip_vr24 - stvx v24,r1,r2 ;save VR24 -L_co_switch$save_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$save_skip_vr25 - stvx v25,r1,r2 ;save VR25 -L_co_switch$save_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$save_skip_vr26 - stvx v26,r1,r2 ;save VR26 -L_co_switch$save_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$save_skip_vr27 - stvx v27,r1,r2 ;save VR27 -L_co_switch$save_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$save_skip_vr28 - stvx v28,r1,r2 ;save VR28 -L_co_switch$save_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$save_skip_vr29 - stvx v29,r1,r2 ;save VR29 -L_co_switch$save_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$save_skip_vr30 - stvx v30,r1,r2 ;save VR30 -L_co_switch$save_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$save_skip_vr31 - stvx v31,r1,r2 ;save VR31 -L_co_switch$save_skip_vr31: -L_co_switch$save_no_vmx: - - addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context - ld r5,lo16(_co_active_context-L_co_switch$spb)(r4) - std r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context - std r1,0(r5) ;save current stack pointer - ld r1,0(r30) ;get new stack pointer - lwz r12,8(r30) ;have we already set GPR13 (system TLS)? - andis. r0,r12,0x8000 - beq+ L_co_switch$gpr13_set - std r13,392(r1) - xoris r12,r12,0x8000 - stw r12,8(r30) -L_co_switch$gpr13_set: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$restore_no_vmx - lwz r11,240(r1) ;restore VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - mtspr 256,r11 - beq L_co_switch$restore_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,48 ;starting index - beq L_co_switch$restore_skip_vr20 - lvx v20,r1,r2 ;restore VR20 -L_co_switch$restore_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$restore_skip_vr21 - lvx v21,r1,r2 ;restore VR21 -L_co_switch$restore_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$restore_skip_vr22 - lvx v22,r1,r2 ;restore VR22 -L_co_switch$restore_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$restore_skip_vr23 - lvx v23,r1,r2 ;restore VR23 -L_co_switch$restore_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$restore_skip_vr24 - lvx v24,r1,r2 ;restore VR24 -L_co_switch$restore_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$restore_skip_vr25 - lvx v25,r1,r2 ;restore VR25 -L_co_switch$restore_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$restore_skip_vr26 - lvx v26,r1,r2 ;restore VR26 -L_co_switch$restore_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$restore_skip_vr27 - lvx v27,r1,r2 ;restore VR27 -L_co_switch$restore_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$restore_skip_vr28 - lvx v28,r1,r2 ;restore VR28 -L_co_switch$restore_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$restore_skip_vr29 - lvx v29,r1,r2 ;restore VR29 -L_co_switch$restore_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$restore_skip_vr30 - lvx v30,r1,r2 ;restore VR30 -L_co_switch$restore_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$restore_skip_vr31 - lvx v31,r1,r2 ;restore VR31 -L_co_switch$restore_skip_vr31: -L_co_switch$restore_no_vmx: - - lfd f14,248(r1) ;restore preserved FPRs - lfd f15,256(r1) - lfd f16,264(r1) - lfd f17,272(r1) - lfd f18,280(r1) - lfd f19,288(r1) - lfd f20,296(r1) - lfd f21,304(r1) - lfd f22,312(r1) - lfd f23,320(r1) - lfd f24,328(r1) - lfd f25,336(r1) - lfd f26,344(r1) - lfd f27,352(r1) - lfd f28,360(r1) - lfd f29,368(r1) - lfd f30,376(r1) - lfd f31,384(r1) - addi r0,0,0 ;make thread main crash if it returns - ld r1,0(r1) ;deallocate stack frame - ld r6,16(r1) ;return address in GPR6 - lwz r7,8(r1) ;condition codes in GPR7 - ld r13,-152(r1) ;restore preserved GPRs - ld r14,-144(r1) - ld r15,-136(r1) - ld r16,-128(r1) - ld r17,-120(r1) - ld r18,-112(r1) - ld r19,-104(r1) - ld r20,-96(r1) - ld r21,-88(r1) - ld r22,-80(r1) - ld r23,-72(r1) - ld r24,-64(r1) - ld r25,-56(r1) - ld r26,-48(r1) - ld r27,-40(r1) - ld r28,-32(r1) - ld r29,-24(r1) - ld r30,-16(r1) - ld r31,-8(r1) - mtlr r0 - mtctr r6 ;restore return address - mtcrf 32,r7 ;restore preserved condition codes - mtcrf 16,r7 - mtcrf 8,r7 - bctr ;return - - - -;Import external functions - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_malloc$stub: - .indirect_symbol _malloc - mflr r0 - bcl 20,31,L_malloc$spb -L_malloc$spb: - mflr r11 - addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) - mtlr r0 - ldu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_malloc$lazy_ptr: - .indirect_symbol _malloc - .quad dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_free$stub: - .indirect_symbol _free - mflr r0 - bcl 20,31,L_free$spb -L_free$spb: - mflr r11 - addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) - mtlr r0 - ldu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_free$lazy_ptr: - .indirect_symbol _free - .quad dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_sysctlbyname$stub: - .indirect_symbol _sysctlbyname - mflr r0 - bcl 20,31,L_sysctlbyname$spb -L_sysctlbyname$spb: - mflr r11 - addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) - mtlr r0 - ldu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_sysctlbyname$lazy_ptr: - .indirect_symbol _sysctlbyname - .quad dyld_stub_binding_helper - - -;This needs to be here! - - .subsections_via_symbols - diff --git a/bsnes/snes/alt/cpu/debugger/debugger.cpp b/bsnes/snes/alt/cpu/debugger/debugger.cpp index f1054ed7..aa93b717 100755 --- a/bsnes/snes/alt/cpu/debugger/debugger.cpp +++ b/bsnes/snes/alt/cpu/debugger/debugger.cpp @@ -56,11 +56,11 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { } //internal - item("S-CPU MDR", string("0x", strhex<2>(regs.mdr))); + item("S-CPU MDR", string("0x", hex<2>(regs.mdr))); //$2181-2183 item("$2181-$2183", ""); - item("WRAM Address", string("0x", strhex<6>(status.wram_addr))); + item("WRAM Address", string("0x", hex<6>(status.wram_addr))); //$4016 item("$4016", ""); @@ -75,45 +75,45 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { //$4201 item("$4201", ""); - item("PIO", string("0x", strhex<2>(status.pio))); + item("PIO", string("0x", hex<2>(status.pio))); //$4202 item("$4202", ""); - item("Multiplicand", string("0x", strhex<2>(status.wrmpya))); + item("Multiplicand", string("0x", hex<2>(status.wrmpya))); //$4203 item("$4203", ""); - item("Multiplier", string("0x", strhex<2>(status.wrmpyb))); + item("Multiplier", string("0x", hex<2>(status.wrmpyb))); //$4204-$4205 item("$4204-$4205", ""); - item("Dividend", string("0x", strhex<4>(status.wrdiva))); + item("Dividend", string("0x", hex<4>(status.wrdiva))); //$4206 item("$4206", ""); - item("Divisor", string("0x", strhex<2>(status.wrdivb))); + item("Divisor", string("0x", hex<2>(status.wrdivb))); //$4207-$4208 item("$4207-$4208", ""); - item("H-Time", string("0x", strhex<4>(status.htime))); + item("H-Time", string("0x", hex<4>(status.htime))); //$4209-$420a item("$4209-$420a", ""); - item("V-Time", string("0x", strhex<4>(status.vtime))); + item("V-Time", string("0x", hex<4>(status.vtime))); //$420b unsigned dma_enable = 0; for(unsigned n = 0; n < 8; n++) dma_enable |= channel[n].dma_enabled << n; item("$420b", ""); - item("DMA Enable", string("0x", strhex<2>(dma_enable))); + item("DMA Enable", string("0x", hex<2>(dma_enable))); //$420c unsigned hdma_enable = 0; for(unsigned n = 0; n < 8; n++) hdma_enable |= channel[n].hdma_enabled << n; item("$420c", ""); - item("HDMA Enable", string("0x", strhex<2>(hdma_enable))); + item("HDMA Enable", string("0x", hex<2>(hdma_enable))); //$420d item("$420d", ""); @@ -130,25 +130,25 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { item("Transfer Mode", (unsigned)channel[i].transfer_mode); //$43x1 - item("B-Bus Address", string("0x", strhex<4>(channel[i].dest_addr))); + item("B-Bus Address", string("0x", hex<4>(channel[i].dest_addr))); //$43x2-$43x3 - item("A-Bus Address", string("0x", strhex<4>(channel[i].source_addr))); + item("A-Bus Address", string("0x", hex<4>(channel[i].source_addr))); //$43x4 - item("A-Bus Bank", string("0x", strhex<2>(channel[i].source_bank))); + item("A-Bus Bank", string("0x", hex<2>(channel[i].source_bank))); //$43x5-$43x6 - item("Transfer Size / Indirect Address", string("0x", strhex<4>(channel[i].transfer_size))); + item("Transfer Size / Indirect Address", string("0x", hex<4>(channel[i].transfer_size))); //$43x7 - item("Indirect Bank", string("0x", strhex<2>(channel[i].indirect_bank))); + item("Indirect Bank", string("0x", hex<2>(channel[i].indirect_bank))); //$43x8-$43x9 - item("Table Address", string("0x", strhex<4>(channel[i].hdma_addr))); + item("Table Address", string("0x", hex<4>(channel[i].hdma_addr))); //$43xa - item("Line Counter", string("0x", strhex<2>(channel[i].line_counter))); + item("Line Counter", string("0x", hex<2>(channel[i].line_counter))); } #undef item diff --git a/bsnes/snes/dsp/debugger/debugger.cpp b/bsnes/snes/dsp/debugger/debugger.cpp index 72d9d3e2..b7a5e3b5 100755 --- a/bsnes/snes/dsp/debugger/debugger.cpp +++ b/bsnes/snes/dsp/debugger/debugger.cpp @@ -14,30 +14,30 @@ bool DSPDebugger::property(unsigned id, string &name, string &value) { item("Main Volume - Right", (unsigned)state.regs[0x1c]); item("Echo Volume - Left", (unsigned)state.regs[0x2c]); item("Echo Volume - Right", (unsigned)state.regs[0x3c]); - item("Key On", string("0x", strhex<2>(state.regs[0x4c]))); - item("Key Off", string("0x", strhex<2>(state.regs[0x5c]))); + item("Key On", string("0x", hex<2>(state.regs[0x4c]))); + item("Key Off", string("0x", hex<2>(state.regs[0x5c]))); item("Flag - Reset", (bool)(state.regs[0x6c] & 0x80)); item("Flag - Mute", (bool)(state.regs[0x6c] & 0x40)); item("Flag - Echo Disable", (bool)(state.regs[0x6c] & 0x20)); item("Flag - Noise Clock", (unsigned)state.regs[0x6c] & 0x1f); item("Source End Block", (unsigned)state.regs[0x7c]); item("Echo Feedback", (unsigned)state.regs[0x0d]); - item("Pitch Modulation Enable", string("0x", strhex<2>(state.regs[0x2d]))); - item("Noise Enable", string("0x", strhex<2>(state.regs[0x3d]))); - item("Echo Enable", string("0x", strhex<2>(state.regs[0x4d]))); + item("Pitch Modulation Enable", string("0x", hex<2>(state.regs[0x2d]))); + item("Noise Enable", string("0x", hex<2>(state.regs[0x3d]))); + item("Echo Enable", string("0x", hex<2>(state.regs[0x4d]))); item("Source Directory", (unsigned)state.regs[0x5d]); item("Echo Start Address", (unsigned)state.regs[0x6d]); item("Echo Directory", (unsigned)state.regs[0x7d]); for(unsigned i = 0; i < 8; i++) { - item(string("Coefficient ", i), string("0x", strhex<2>(state.regs[(i << 4) + 0x0f]))); + item(string("Coefficient ", i), string("0x", hex<2>(state.regs[(i << 4) + 0x0f]))); } for(unsigned i = 0; i < 8; i++) { item(string("Voice ", i), ""); item("Volume - Left", (unsigned)state.regs[(i << 4) + 0x00]); item("Volume - Right", (unsigned)state.regs[(i << 4) + 0x01]); - item("Pitch Height", string("0x", strhex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8)))); + item("Pitch Height", string("0x", hex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8)))); item("Source Number", (unsigned)state.regs[(i << 4) + 0x04]); item("ADSR1", (unsigned)state.regs[(i << 4) + 0x05]); item("ADSR2", (unsigned)state.regs[(i << 4) + 0x06]); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 858d3756..70521ecd 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,12 +1,12 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "072.14"; - static const unsigned SerializerVersion = 14; + static const char Version[] = "073"; + static const unsigned SerializerVersion = 15; } } -#define DEBUGGER +//#define DEBUGGER #define CHEAT_SYSTEM #include diff --git a/pixelshaders/GLSL/Curvature.shader b/shaders/GLSL/Curvature.shader similarity index 61% rename from pixelshaders/GLSL/Curvature.shader rename to shaders/GLSL/Curvature.shader index 49abf527..fb6b92ed 100755 --- a/pixelshaders/GLSL/Curvature.shader +++ b/shaders/GLSL/Curvature.shader @@ -2,6 +2,8 @@ diff --git a/pixelshaders/GLSL/HDR-TV.shader b/shaders/GLSL/HDR-TV.shader similarity index 100% rename from pixelshaders/GLSL/HDR-TV.shader rename to shaders/GLSL/HDR-TV.shader diff --git a/pixelshaders/GLSL/HQ2x.shader b/shaders/GLSL/HQ2x.shader similarity index 100% rename from pixelshaders/GLSL/HQ2x.shader rename to shaders/GLSL/HQ2x.shader diff --git a/pixelshaders/GLSL/Pixellate.shader b/shaders/GLSL/Pixellate.shader similarity index 100% rename from pixelshaders/GLSL/Pixellate.shader rename to shaders/GLSL/Pixellate.shader diff --git a/pixelshaders/GLSL/Scale2x.shader b/shaders/GLSL/Scale2x.shader similarity index 100% rename from pixelshaders/GLSL/Scale2x.shader rename to shaders/GLSL/Scale2x.shader diff --git a/pixelshaders/HLSL/Sepia.shader b/shaders/HLSL/Sepia.shader similarity index 100% rename from pixelshaders/HLSL/Sepia.shader rename to shaders/HLSL/Sepia.shader diff --git a/snespurify/cc-gtk.sh b/snespurify/cc-gtk.sh new file mode 100755 index 00000000..3d1ce69f --- /dev/null +++ b/snespurify/cc-gtk.sh @@ -0,0 +1,4 @@ +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp `pkg-config --cflags gtk+-2.0` -DPHOENIX_GTK +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_GTK +g++-4.5 -s -o snespurify snespurify.o phoenix.o `pkg-config --libs gtk+-2.0` +rm *.o diff --git a/snespurify/cc-windows.bat b/snespurify/cc-windows.bat new file mode 100755 index 00000000..69c2194e --- /dev/null +++ b/snespurify/cc-windows.bat @@ -0,0 +1,7 @@ +windres phoenix/windows/phoenix.rc phoenix-resource.o +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp -DPHOENIX_WINDOWS +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_WINDOWS +g++ -mwindows -s -o snespurify snespurify.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 +upx --best snespurify.exe +@pause +@del *.o diff --git a/snespurify/nall/Makefile b/snespurify/nall/Makefile new file mode 100755 index 00000000..9a93bd23 --- /dev/null +++ b/snespurify/nall/Makefile @@ -0,0 +1,109 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),win) + compiler := gcc + else ifeq ($(platform),osx) + compiler := gcc-mp-4.5 + else + compiler := gcc-4.5 + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snespurify/nall/algorithm.hpp b/snespurify/nall/algorithm.hpp new file mode 100755 index 00000000..037f0bb7 --- /dev/null +++ b/snespurify/nall/algorithm.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } +} + +#endif diff --git a/snespurify/nall/any.hpp b/snespurify/nall/any.hpp new file mode 100755 index 00000000..b31cff3c --- /dev/null +++ b/snespurify/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snespurify/nall/array.hpp b/snespurify/nall/array.hpp new file mode 100755 index 00000000..9cfe7758 --- /dev/null +++ b/snespurify/nall/array.hpp @@ -0,0 +1,141 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void append(const T data) { + operator[](buffersize) = data; + } + + template void insert(unsigned index, const U list) { + unsigned listsize = container_size(list); + resize(buffersize + listsize); + memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T)); + foreach(item, list) pool[index++] = item; + } + + void insert(unsigned index, const T item) { + insert(index, array{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < buffersize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= buffersize) resize(index); //every element >= index was removed + else resize(buffersize - count); + } + + optional find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i }; + return { false, 0 }; + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/nall/base64.hpp b/snespurify/nall/base64.hpp new file mode 100755 index 00000000..e41c87b7 --- /dev/null +++ b/snespurify/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snespurify/nall/bit.hpp b/snespurify/nall/bit.hpp new file mode 100755 index 00000000..169fc144 --- /dev/null +++ b/snespurify/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snespurify/nall/concept.hpp b/snespurify/nall/concept.hpp new file mode 100755 index 00000000..47167e21 --- /dev/null +++ b/snespurify/nall/concept.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +#include +#include + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snespurify/nall/config.hpp b/snespurify/nall/config.hpp new file mode 100755 index 00000000..f555158e --- /dev/null +++ b/snespurify/nall/config.hpp @@ -0,0 +1,123 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: s.trim("\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0; + if(!qstrpos(line[i], " = ")) continue; + + lstring part; + part.qsplit(" = ", line[i]); + part[0].trim(); + part[1].trim(); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode::write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snespurify/nall/crc32.hpp b/snespurify/nall/crc32.hpp new file mode 100755 index 00000000..ad36fbf6 --- /dev/null +++ b/snespurify/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/snespurify/nall/detect.hpp b/snespurify/nall/detect.hpp new file mode 100755 index 00000000..b4991aaf --- /dev/null +++ b/snespurify/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snespurify/nall/dictionary.hpp b/snespurify/nall/dictionary.hpp new file mode 100755 index 00000000..dcb04151 --- /dev/null +++ b/snespurify/nall/dictionary.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + if(auto pos = strpos(input, "}}")) { + string temp = substr(input, pos() + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + part[0].trim(); + part[1].trim(); + + //remove quotes + part[0].trim<1>("\""); + part[1].trim<1>("\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snespurify/nall/directory.hpp b/snespurify/nall/directory.hpp new file mode 100755 index 00000000..df0bf086 --- /dev/null +++ b/snespurify/nall/directory.hpp @@ -0,0 +1,151 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static bool exists(const string &pathname); + static lstring folders(const string &pathname, const string &pattern = "*"); + static lstring files(const string &pathname, const string &pattern = "*"); + static lstring contents(const string &pathname, const string &pattern = "*"); +}; + +#if defined(_WIN32) + inline bool directory::exists(const string &pathname) { + DWORD result = GetFileAttributes(utf16_t(pathname)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#else + inline bool directory::exists(const string &pathname) { + DIR *dp = opendir(pathname); + if(!dp) return false; + closedir(dp); + return true; + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if(ep->d_type & DT_DIR) { + if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/")); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) { + if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#endif + +} + +#endif diff --git a/snespurify/nall/dl.hpp b/snespurify/nall/dl.hpp new file mode 100755 index 00000000..6fa7603f --- /dev/null +++ b/snespurify/nall/dl.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*, const char* = ""); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*, const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snespurify/nall/endian.hpp b/snespurify/nall/endian.hpp new file mode 100755 index 00000000..40d15633 --- /dev/null +++ b/snespurify/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snespurify/nall/file.hpp b/snespurify/nall/file.hpp new file mode 100755 index 00000000..103c7d4a --- /dev/null +++ b/snespurify/nall/file.hpp @@ -0,0 +1,261 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + enum class index : unsigned { absolute, relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode::write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + template void print(Args... args) { + string data(args...); + const char *p = data; + while(*p) write(*p++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, index index_ = index::absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(index_) { + case index::absolute: req_offset = offset; break; + case index::relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode::read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, mode mode_) { + if(fp) return false; + + switch(file_mode = mode_) { + #if !defined(_WIN32) + case mode::read: fp = fopen(fn, "rb"); break; + case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode::readwrite: fp = fopen(fn, "rb+"); break; + case mode::writeread: fp = fopen(fn, "wb+"); break; + #else + case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode::read; + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + mode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snespurify/nall/filemap.hpp b/snespurify/nall/filemap.hpp new file mode 100755 index 00000000..52acb2fa --- /dev/null +++ b/snespurify/nall/filemap.hpp @@ -0,0 +1,200 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + + bool opened() const { return p_opened(); } + bool open(const char *filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snespurify/nall/foreach.hpp b/snespurify/nall/foreach.hpp new file mode 100755 index 00000000..00a039f3 --- /dev/null +++ b/snespurify/nall/foreach.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#include +#include + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#endif diff --git a/snespurify/nall/function.hpp b/snespurify/nall/function.hpp new file mode 100755 index 00000000..645991fb --- /dev/null +++ b/snespurify/nall/function.hpp @@ -0,0 +1,60 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +namespace nall { + template class function; + + template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + virtual ~container() {} + } *callback; + + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function)(P...)) : function(function) {} + }; + + template struct member : container { + R (C::*function)(P...); + C *object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function)(P...), C *object) : function(function), object(object) {} + }; + + template struct lambda : container { + L object; + R operator()(P... p) const { return object(std::forward

(p)...); } + container* copy() const { return new lambda(object); } + lambda(const L& object) : object(object) {} + }; + + public: + operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } + void reset() { if(callback) { delete callback; callback = 0; } } + + function& operator=(const function &source) { + if(this != &source) { + if(callback) { delete callback; callback = 0; } + if(source.callback) callback = source.callback->copy(); + } + return *this; + } + + function(const function &source) { operator=(source); } + function() : callback(0) {} + function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C *object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C *object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } + }; +} + +#endif diff --git a/snespurify/nall/input.hpp b/snespurify/nall/input.hpp new file mode 100755 index 00000000..28b10453 --- /dev/null +++ b/snespurify/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + s.ltrim("KB"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + s.ltrim("MS"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + s.ltrim("JP"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snespurify/nall/lzss.hpp b/snespurify/nall/lzss.hpp new file mode 100755 index 00000000..202bc814 --- /dev/null +++ b/snespurify/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snespurify/nall/moduloarray.hpp b/snespurify/nall/moduloarray.hpp new file mode 100755 index 00000000..be549ae9 --- /dev/null +++ b/snespurify/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snespurify/nall/platform.hpp b/snespurify/nall/platform.hpp new file mode 100755 index 00000000..72eeec09 --- /dev/null +++ b/snespurify/nall/platform.hpp @@ -0,0 +1,122 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface + #define dllexport __declspec(dllexport) +#else + #include + #include + #include + #define dllexport +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +//========================= +//file system functionality +//========================= + +#if defined(_WIN32) + inline char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + inline char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + inline char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } +#else + //realpath() already exists + + inline char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + inline char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } +#endif + +#endif + diff --git a/snespurify/nall/priorityqueue.hpp b/snespurify/nall/priorityqueue.hpp new file mode 100755 index 00000000..7104e791 --- /dev/null +++ b/snespurify/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snespurify/nall/property.hpp b/snespurify/nall/property.hpp new file mode 100755 index 00000000..6fd33acd --- /dev/null +++ b/snespurify/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snespurify/nall/random.hpp b/snespurify/nall/random.hpp new file mode 100755 index 00000000..74ebc2d2 --- /dev/null +++ b/snespurify/nall/random.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +namespace nall { + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } + + struct random_cyclic { + unsigned seed; + inline unsigned operator()() { + return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320); + } + random_cyclic() : seed(0) {} + }; +} + +#endif diff --git a/snespurify/nall/serial.hpp b/snespurify/nall/serial.hpp new file mode 100755 index 00000000..9ac8451a --- /dev/null +++ b/snespurify/nall/serial.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snespurify/nall/serializer.hpp b/snespurify/nall/serializer.hpp new file mode 100755 index 00000000..ff2337ab --- /dev/null +++ b/snespurify/nall/serializer.hpp @@ -0,0 +1,146 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + icapacity = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snespurify/nall/sha256.hpp b/snespurify/nall/sha256.hpp new file mode 100755 index 00000000..7f41f04e --- /dev/null +++ b/snespurify/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/snespurify/nall/snes/info.hpp b/snespurify/nall/snes/info.hpp new file mode 100755 index 00000000..df8b5f59 --- /dev/null +++ b/snespurify/nall/snes/info.hpp @@ -0,0 +1,869 @@ +#ifndef NALL_SNES_INFO_HPP +#define NALL_SNES_INFO_HPP + +namespace nall { + +class snes_information { +public: + string xml_memory_map; + + inline snes_information(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +snes_information::snes_information(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xml_memory_map = xml; + return; + } + + xml << "\n"; + + if(type == TypeSuperGameBoy1Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(type == TypeSuperGameBoy2Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(has_spc7110) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + xml << " \n"; + } else { + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == ExLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_sdd1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_cx4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + //ST-0011 addresses not verified; chip is unsupported + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xml_memory_map = xml; +} + +void snes_information::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + if(size < 32768) { + type = TypeUnknown; + return; + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned snes_information::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned snes_information::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned snes_information::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool snes_information::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} + +} + +#endif diff --git a/snespurify/nall/sort.hpp b/snespurify/nall/sort.hpp new file mode 100755 index 00000000..23c317a5 --- /dev/null +++ b/snespurify/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snespurify/nall/static.hpp b/snespurify/nall/static.hpp new file mode 100755 index 00000000..4acb9fd0 --- /dev/null +++ b/snespurify/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snespurify/nall/stdint.hpp b/snespurify/nall/stdint.hpp new file mode 100755 index 00000000..d8b6c788 --- /dev/null +++ b/snespurify/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snespurify/nall/string.hpp b/snespurify/nall/string.hpp new file mode 100755 index 00000000..9acc2e9d --- /dev/null +++ b/snespurify/nall/string.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snespurify/nall/string/base.hpp b/snespurify/nall/string/base.hpp new file mode 100755 index 00000000..77f15e17 --- /dev/null +++ b/snespurify/nall/string/base.hpp @@ -0,0 +1,159 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + + inline string& assign(const char*); + inline string& append(const char*); + inline string& append(bool); + inline string& append(signed int value); + inline string& append(unsigned int value); + inline string& append(double value); + + inline bool readfile(const char*); + + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + inline unsigned length() const; + + inline bool equals(const char*) const; + inline bool iequals(const char*) const; + + inline bool wildcard(const char*) const; + inline bool iwildcard(const char*) const; + + inline bool beginswith(const char*) const; + inline bool ibeginswith(const char*) const; + inline bool endswith(const char*) const; + inline bool iendswith(const char*) const; + + inline string& lower(); + inline string& upper(); + inline string& transform(const char *before, const char *after); + + template inline string& ltrim(const char *key = " "); + template inline string& rtrim(const char *key = " "); + template inline string& trim (const char *key = " "); + + inline optional position(const char *key) const; + inline optional qposition(const char *key) const; + + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + inline string& operator=(string&&); + + template inline string(Args&&... args); + inline string(const string&); + inline string(string&&); + inline ~string(); + + protected: + char *data; + unsigned size; + + #if defined(QSTRING_H) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline optional find(const char*) const; + template inline void split (const char*, const char*); + template inline void qsplit(const char*, const char*); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *str1, const char *str2); + inline bool wildcard(const char *str, const char *pattern); + inline bool iwildcard(const char *str, const char *pattern); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //platform.hpp + inline string realpath(const char *name); + inline string userpath(); + inline string currentpath(); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //strpos.hpp + inline optional strpos(const char *str, const char *key); + inline optional qstrpos(const char *str, const char *key); + + //trim.hpp + template inline char* ltrim(char *str, const char *key = " "); + template inline char* rtrim(char *str, const char *key = " "); + template inline char* trim (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline void print(Args&&... args); +}; + +#endif diff --git a/snespurify/nall/string/bsv.hpp b/snespurify/nall/string/bsv.hpp new file mode 100755 index 00000000..d4b919e0 --- /dev/null +++ b/snespurify/nall/string/bsv.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_BSV_HPP +#define NALL_STRING_BSV_HPP + +//BSV parser +//version 0.01 + +namespace nall { + +inline string bsv_decode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '}' ) return ""; + if(*input == '\r') return ""; + if(*input == '\n') return ""; + + //normal characters + if(*input != '{') { output[offset++] = *input++; continue; } + + //entities + if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } + if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } + if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } + + //illegal entities + return ""; + } + output[offset] = 0; + return output; +} + +inline string bsv_encode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '\r') return ""; + + if(*input == '\n') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'f'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '{') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '}') { + output[offset++] = '{'; + output[offset++] = 'r'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + output[offset++] = *input++; + } + output[offset] = 0; + return output; +} + +} + +#endif diff --git a/snespurify/nall/string/cast.hpp b/snespurify/nall/string/cast.hpp new file mode 100755 index 00000000..5b17c408 --- /dev/null +++ b/snespurify/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QSTRING_H) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snespurify/nall/string/compare.hpp b/snespurify/nall/string/compare.hpp new file mode 100755 index 00000000..bce0895b --- /dev/null +++ b/snespurify/nall/string/compare.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *str1, const char *str2) { + while(*str1) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +bool wildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool iwildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/snespurify/nall/string/convert.hpp b/snespurify/nall/string/convert.hpp new file mode 100755 index 00000000..12a6c1ff --- /dev/null +++ b/snespurify/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.' || x == ',') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snespurify/nall/string/core.hpp b/snespurify/nall/string/core.hpp new file mode 100755 index 00000000..bad29030 --- /dev/null +++ b/snespurify/nall/string/core.hpp @@ -0,0 +1,139 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string& string::append(bool value) { append(value ? "true" : "false"); return *this; } +string& string::append(signed int value) { append(strsigned(value)); return *this; } +string& string::append(unsigned int value) { append(strunsigned(value)); return *this; } +string& string::append(double value) { append(strdouble(value)); return *this; } + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +static void istring(string &output) { +} + +template +static void istring(string &output, const T &value, Args&&... args) { + output.append(value); + istring(output, std::forward(args)...); +} + +template string::string(Args&&... args) { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; + istring(*this, std::forward(args)...); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string::~string() { + if(data) free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +optional lstring::find(const char *key) const { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return { true, i }; + } + return { false, 0 }; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snespurify/nall/string/filename.hpp b/snespurify/nall/string/filename.hpp new file mode 100755 index 00000000..93d605ae --- /dev/null +++ b/snespurify/nall/string/filename.hpp @@ -0,0 +1,63 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/" +// "foo/" -> "foo/" +// "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snespurify/nall/string/math.hpp b/snespurify/nall/string/math.hpp new file mode 100755 index 00000000..ea8b99c8 --- /dev/null +++ b/snespurify/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snespurify/nall/string/platform.hpp b/snespurify/nall/string/platform.hpp new file mode 100755 index 00000000..42c1a756 --- /dev/null +++ b/snespurify/nall/string/platform.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_PLATFORM_HPP +#define NALL_STRING_PLATFORM_HPP + +namespace nall { + +string realpath(const char *name) { + char path[PATH_MAX]; + if(::realpath(name, path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string userpath() { + char path[PATH_MAX]; + if(::userpath(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string currentpath() { + char path[PATH_MAX]; + if(::getcwd(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +} + +#endif diff --git a/snespurify/nall/string/replace.hpp b/snespurify/nall/string/replace.hpp new file mode 100755 index 00000000..db405a9b --- /dev/null +++ b/snespurify/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snespurify/nall/string/split.hpp b/snespurify/nall/string/split.hpp new file mode 100755 index 00000000..8d3ca877 --- /dev/null +++ b/snespurify/nall/string/split.hpp @@ -0,0 +1,58 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +template void lstring::split(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +template void lstring::qsplit(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snespurify/nall/string/strl.hpp b/snespurify/nall/string/strl.hpp new file mode 100755 index 00000000..84c841fa --- /dev/null +++ b/snespurify/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snespurify/nall/string/strpos.hpp b/snespurify/nall/string/strpos.hpp new file mode 100755 index 00000000..1907a2f3 --- /dev/null +++ b/snespurify/nall/string/strpos.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_STRPOS_HPP +#define NALL_STRING_STRPOS_HPP + +//usage example: +//if(auto pos = strpos(str, key)) print(pos(), "\n"); +//prints position of key within str, only if it is found + +namespace nall { + +optional strpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) return { true, i }; + } + + return { false, 0 }; +} + +optional qstrpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) return { true, i }; + i++; + } + + return { false, 0 }; +} + +} + +#endif diff --git a/snespurify/nall/string/trim.hpp b/snespurify/nall/string/trim.hpp new file mode 100755 index 00000000..f5355d7d --- /dev/null +++ b/snespurify/nall/string/trim.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +//limit defaults to zero, which will underflow on first compare; equivalent to no limit +template char* ltrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + if(--limit == 0) break; + } + return str; +} + +template char* rtrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strend(str, key)) { + str[strlen(str) - strlen(key)] = 0; + if(--limit == 0) break; + } + return str; +} + +template char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +} + +#endif diff --git a/snespurify/nall/string/utility.hpp b/snespurify/nall/string/utility.hpp new file mode 100755 index 00000000..d2bad881 --- /dev/null +++ b/snespurify/nall/string/utility.hpp @@ -0,0 +1,157 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snespurify/nall/string/variadic.hpp b/snespurify/nall/string/variadic.hpp new file mode 100755 index 00000000..6c027fc8 --- /dev/null +++ b/snespurify/nall/string/variadic.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +template inline void print(Args&&... args) { + printf("%s", (const char*)string(std::forward(args)...)); +} + +} + +#endif diff --git a/snespurify/nall/string/wrapper.hpp b/snespurify/nall/string/wrapper.hpp new file mode 100755 index 00000000..eadf0a10 --- /dev/null +++ b/snespurify/nall/string/wrapper.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_STRING_WRAPPER_HPP +#define NALL_STRING_WRAPPER_HPP + +namespace nall { + +unsigned string::length() const { return strlen(data); } + +bool string::equals(const char *str) const { return !strcmp(data, str); } +bool string::iequals(const char *str) const { return !stricmp(data, str); } + +bool string::wildcard(const char *str) const { return nall::wildcard(data, str); } +bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); } + +bool string::beginswith(const char *str) const { return strbegin(data, str); } +bool string::ibeginswith(const char *str) const { return stribegin(data, str); } + +bool string::endswith(const char *str) const { return strend(data, str); } +bool string::iendswith(const char *str) const { return striend(data, str); } + +string& string::lower() { nall::strlower(data); return *this; } +string& string::upper() { nall::strupper(data); return *this; } +string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; } + +template string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } +template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } +template string& string::trim (const char *key) { nall::trim (data, key); return *this; } + +optional string::position(const char *key) const { return strpos(data, key); } +optional string::qposition(const char *key) const { return qstrpos(data, key); } + +} + +#endif diff --git a/snespurify/nall/string/xml.hpp b/snespurify/nall/string/xml.hpp new file mode 100755 index 00000000..185a89f9 --- /dev/null +++ b/snespurify/nall/string/xml.hpp @@ -0,0 +1,266 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, "")) { + source += pos() + 3; + continue; + } else { + return ""; + } + } + + if(strbegin(source, "")) { + if(pos() - 9 > 0) { + string cdata = substr(source, 9, pos() - 9); + data << cdata; + offset += strlen(cdata); + } + source += 9 + offset + 3; + continue; + } else { + return ""; + } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ")) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + data.rtrim(); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'"); + else throw "..."; + attribute.append(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + if(auto offset = strpos(data, "-->")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + if(strbegin(data, "![CDATA[")) { + if(auto offset = strpos(data, "]]>")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + string tag = substr(data, 0, offset()); + data += offset() + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + tag.rtrim<1>("?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + tag.rtrim<1>("/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + tag = substr(data, 0, offset()); + data += offset() + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ")) tag.replace(" ", " "); + tag.rtrim(); + + if(name != tag) throw "..."; + return true; + } + } else { + element.append(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.append(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snespurify/nall/ups.hpp b/snespurify/nall/ups.hpp new file mode 100755 index 00000000..ffcdb2d7 --- /dev/null +++ b/snespurify/nall/ups.hpp @@ -0,0 +1,223 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include +#include +#include +#include + +namespace nall { + +struct ups { + enum class result : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; + + function progress; + + result create( + const uint8_t *sourcedata, unsigned sourcelength, + const uint8_t *targetdata, unsigned targetlength, + const char *patchfilename + ) { + source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata; + source_length = sourcelength, target_length = targetlength; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; + + if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable; + + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); + + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); + + if(x == y) { + offset++; + continue; + } + + encode(offset++ - relative); + patch_write(x ^ y); + + while(true) { + if(offset >= output_length) { + patch_write(0x00); + break; + } + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result::success; + } + + result apply( + const uint8_t *patchdata, unsigned patchlength, + const uint8_t *sourcedata, unsigned sourcelength, + uint8_t *targetdata, unsigned &targetlength + ) { + patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata; + patch_length = patchlength, source_length = sourcelength, target_length = targetlength; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result::patch_invalid; + if(patch_read() != 'U') return result::patch_invalid; + if(patch_read() != 'P') return result::patch_invalid; + if(patch_read() != 'S') return result::patch_invalid; + if(patch_read() != '1') return result::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid; + targetlength = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < targetlength) return result::target_too_small; + target_length = targetlength; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; + } + } + while(source_offset < source_length) target_write(source_read()); + while(target_offset < target_length) target_write(source_read()); + + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success; + return result::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success; + return result::target_invalid; + } else { + return result::source_invalid; + } + } + +private: + uint8_t *patch_data, *source_data, *target_data; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + +} + +#endif diff --git a/snespurify/nall/utf8.hpp b/snespurify/nall/utf8.hpp new file mode 100755 index 00000000..f5597b85 --- /dev/null +++ b/snespurify/nall/utf8.hpp @@ -0,0 +1,86 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t&) = delete; + utf8_t& operator=(const utf8_t&) = delete; + + private: + char *buffer; + }; + + inline void utf8_args(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snespurify/nall/utility.hpp b/snespurify/nall/utility.hpp new file mode 100755 index 00000000..60bda562 --- /dev/null +++ b/snespurify/nall/utility.hpp @@ -0,0 +1,39 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template class optional { + bool valid; + T value; + public: + inline operator bool() const { return valid; } + inline const T& operator()() const { if(!valid) throw; return value; } + inline optional(bool valid, const T &value) : valid(valid), value(value) {} + }; + + template inline T* allocate(unsigned size, const T &value) { + T *array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snespurify/nall/varint.hpp b/snespurify/nall/varint.hpp new file mode 100755 index 00000000..fe4732b1 --- /dev/null +++ b/snespurify/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snespurify/nall/vector.hpp b/snespurify/nall/vector.hpp new file mode 100755 index 00000000..543c7b69 --- /dev/null +++ b/snespurify/nall/vector.hpp @@ -0,0 +1,281 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + template void insert(unsigned index, const U list) { + linear_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, linear_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + template void insert(unsigned index, const U list) { + pointer_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(*pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, pointer_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + *pool[i] = *pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/cc-gtk.sh b/snespurify/phoenix/cc-gtk.sh new file mode 100755 index 00000000..779431e4 --- /dev/null +++ b/snespurify/phoenix/cc-gtk.sh @@ -0,0 +1,4 @@ +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp `pkg-config --cflags gtk+-2.0` -DPHOENIX_GTK +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_GTK +g++-4.5 -s -o test-gtk test.o phoenix.o `pkg-config --libs gtk+-2.0` +rm *.o diff --git a/snespurify/phoenix/cc-qt.sh b/snespurify/phoenix/cc-qt.sh new file mode 100755 index 00000000..45cc5237 --- /dev/null +++ b/snespurify/phoenix/cc-qt.sh @@ -0,0 +1,6 @@ +moc -i -o qt/qt.moc qt/qt.moc.hpp +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp `pkg-config --cflags QtCore QtGui` -DPHOENIX_QT +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_QT +g++-4.5 -s -o test-qt test.o phoenix.o `pkg-config --libs QtCore QtGui` +rm *.o +#rm qt/qt.moc diff --git a/snespurify/phoenix/cc-windows.bat b/snespurify/phoenix/cc-windows.bat new file mode 100755 index 00000000..da544e37 --- /dev/null +++ b/snespurify/phoenix/cc-windows.bat @@ -0,0 +1,6 @@ +windres windows/phoenix.rc phoenix-resource.o +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp -DPHOENIX_WINDOWS +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_WINDOWS +g++ -mconsole -s -o test-windows test.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 +@pause +@del *.o diff --git a/snespurify/phoenix/gtk/button.cpp b/snespurify/phoenix/gtk/button.cpp new file mode 100755 index 00000000..af2a8a61 --- /dev/null +++ b/snespurify/phoenix/gtk/button.cpp @@ -0,0 +1,13 @@ +static void Button_tick(Button *self) { + if(self->onTick) self->onTick(); +} + +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_button_new_with_label(text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} diff --git a/snespurify/phoenix/gtk/canvas.cpp b/snespurify/phoenix/gtk/canvas.cpp new file mode 100755 index 00000000..43913871 --- /dev/null +++ b/snespurify/phoenix/gtk/canvas.cpp @@ -0,0 +1,59 @@ +static void Canvas_expose(Canvas *self) { + uint32_t *rgb = self->canvas->bufferRGB; + uint32_t *bgr = self->canvas->bufferBGR; + for(unsigned y = self->object->widget->allocation.height; y; y--) { + for(unsigned x = self->object->widget->allocation.width; x; x--) { + uint32_t pixel = *rgb++; + *bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff); + } + } + + gdk_draw_rgb_32_image( + self->object->widget->window, + self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)], + 0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height, + GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch + ); +} + +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->bufferRGB = new uint32_t[width * height](); + canvas->bufferBGR = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + + object->widget = gtk_drawing_area_new(); + widget->parent = &parent; + GdkColor color; + color.pixel = color.red = color.green = color.blue = 0; + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); + gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uint32_t* Canvas::buffer() { + return canvas->bufferRGB; +} + +void Canvas::redraw() { + GdkRectangle rect; + rect.x = 0; + rect.y = 0; + rect.width = object->widget->allocation.width; + rect.height = object->widget->allocation.height; + gdk_window_invalidate_rect(object->widget->window, &rect, true); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->bufferRGB = 0; + canvas->bufferBGR = 0; +} + +Canvas::~Canvas() { + if(canvas->bufferRGB) delete[] canvas->bufferRGB; + if(canvas->bufferBGR) delete[] canvas->bufferBGR; +} diff --git a/snespurify/phoenix/gtk/checkbox.cpp b/snespurify/phoenix/gtk/checkbox.cpp new file mode 100755 index 00000000..f569775c --- /dev/null +++ b/snespurify/phoenix/gtk/checkbox.cpp @@ -0,0 +1,23 @@ +static void CheckBox_tick(CheckBox *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_check_button_new_with_label(text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool CheckBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void CheckBox::setChecked(bool checked) { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), checked); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/combobox.cpp b/snespurify/phoenix/gtk/combobox.cpp new file mode 100755 index 00000000..dbabf247 --- /dev/null +++ b/snespurify/phoenix/gtk/combobox.cpp @@ -0,0 +1,48 @@ +void ComboBox_change(ComboBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_combo_box_new_text(); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ComboBox::reset() { + object->locked = true; + for(signed i = counter - 1; i >= 0; i--) { + gtk_combo_box_remove_text(GTK_COMBO_BOX(object->widget), i); + } + object->locked = false; + counter = 0; +} + +void ComboBox::addItem(const string &text) { + gtk_combo_box_append_text(GTK_COMBO_BOX(object->widget), text); + if(counter++ == 0) setSelection(0); +} + +unsigned ComboBox::selection() { + return gtk_combo_box_get_active(GTK_COMBO_BOX(object->widget)); +} + +void ComboBox::setSelection(unsigned item) { + object->locked = true; + gtk_combo_box_set_active(GTK_COMBO_BOX(object->widget), item); + object->locked = false; +} + +ComboBox::ComboBox() { + counter = 0; +} diff --git a/snespurify/phoenix/gtk/editbox.cpp b/snespurify/phoenix/gtk/editbox.cpp new file mode 100755 index 00000000..a821177d --- /dev/null +++ b/snespurify/phoenix/gtk/editbox.cpp @@ -0,0 +1,49 @@ +static void EditBox_change(EditBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_scrolled_window_new(0, 0); + widget->parent = &parent; + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + object->subWidget = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), GTK_WRAP_WORD_CHAR); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(object->subWidget)); + gtk_text_buffer_set_text(object->textBuffer, text, -1); + g_signal_connect_swapped(G_OBJECT(object->textBuffer), "changed", G_CALLBACK(EditBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void EditBox::setFocused() { + gtk_widget_grab_focus(object->subWidget); +} + +void EditBox::setEditable(bool editable) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(object->subWidget), editable); +} + +void EditBox::setWordWrap(bool wordWrap) { + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE); +} + +string EditBox::text() { + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(object->textBuffer, &start); + gtk_text_buffer_get_end_iter(object->textBuffer, &end); + char *temp = gtk_text_buffer_get_text(object->textBuffer, &start, &end, true); + string text = temp; + g_free(temp); + return text; +} + +void EditBox::setText(const string &text) { + object->locked = true; + gtk_text_buffer_set_text(object->textBuffer, text, -1); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/font.cpp b/snespurify/phoenix/gtk/font.cpp new file mode 100755 index 00000000..459151e0 --- /dev/null +++ b/snespurify/phoenix/gtk/font.cpp @@ -0,0 +1,18 @@ +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->font = pango_font_description_new(); + pango_font_description_set_family(font->font, name); + pango_font_description_set_size(font->font, size * PANGO_SCALE); + pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL); + pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + return true; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) pango_font_description_free(font->font); + delete font; +} diff --git a/snespurify/phoenix/gtk/gtk.cpp b/snespurify/phoenix/gtk/gtk.cpp new file mode 100755 index 00000000..381e3250 --- /dev/null +++ b/snespurify/phoenix/gtk/gtk.cpp @@ -0,0 +1,189 @@ +#include +#include +#include + +#define None X11None +#define Window X11Window + +#include +#include +#include +#include + +#undef None +#undef Window + +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +Window Window::None; + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + int argc = 1; + char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + gtk_init(&argc, &argvp); + + gtk_rc_parse_string( + "style \"phoenix-gtk\"\n" + "{\n" + " GtkComboBox::appears-as-list = 1\n" + " GtkTreeView::vertical-separator = 0\n" + "}\n" + "class \"GtkComboBox\" style \"phoenix-gtk\"\n" + "class \"GtkTreeView\" style \"phoenix-gtk\"\n" + ); +} + +bool OS::pending() { + return gtk_events_pending(); +} + +void OS::run() { + while(pending()) gtk_main_iteration_do(false); +} + +void OS::main() { + gtk_main(); +} + +void OS::quit() { + gtk_main_quit(); +} + +unsigned OS::desktopWidth() { + return gdk_screen_get_width(gdk_screen_get_default()); +} + +unsigned OS::desktopHeight() { + return gdk_screen_get_height(gdk_screen_get_default()); +} + +string OS::folderSelect(Window &parent, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Select Folder", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Open File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Save File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +} diff --git a/snespurify/phoenix/gtk/gtk.hpp b/snespurify/phoenix/gtk/gtk.hpp new file mode 100755 index 00000000..3ba82fe2 --- /dev/null +++ b/snespurify/phoenix/gtk/gtk.hpp @@ -0,0 +1,265 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + Action(); +//private: + struct Data; + Data *action; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool checked(); + void setChecked(); +private: + MenuRadioItem *first; +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + virtual bool focused(); + virtual void setFocused(); + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool focused(); + void setFocused(); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setDefaultFont(Font &font); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; + static Window None; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +private: + unsigned counter; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setFocused(); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const nall::string &text); +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setFocused(); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void setFont(Font &font); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setPosition(unsigned position); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); +private: + RadioBox *first; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const nall::string &text); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + static void initialize(); +}; + +} diff --git a/snespurify/phoenix/gtk/horizontalslider.cpp b/snespurify/phoenix/gtk/horizontalslider.cpp new file mode 100755 index 00000000..9e6e76c7 --- /dev/null +++ b/snespurify/phoenix/gtk/horizontalslider.cpp @@ -0,0 +1,25 @@ +static void HorizontalSlider_change(HorizontalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_hscale_new_with_range(0, length - 1, 1); + widget->parent = &parent; + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned HorizontalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void HorizontalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/snespurify/phoenix/gtk/label.cpp b/snespurify/phoenix/gtk/label.cpp new file mode 100755 index 00000000..8321523f --- /dev/null +++ b/snespurify/phoenix/gtk/label.cpp @@ -0,0 +1,13 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_label_new(text); + widget->parent = &parent; + gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.5); + gtk_widget_set_size_request(object->widget, width, height); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void Label::setText(const string &text) { + gtk_label_set_text(GTK_LABEL(object->widget), text); +} diff --git a/snespurify/phoenix/gtk/listbox.cpp b/snespurify/phoenix/gtk/listbox.cpp new file mode 100755 index 00000000..e851e6ba --- /dev/null +++ b/snespurify/phoenix/gtk/listbox.cpp @@ -0,0 +1,195 @@ +static void ListBox_activate(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + self->listBox->selection = selection; + if(self->onActivate) self->onActivate(); +} + +static void ListBox_change(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + if(selection == self->listBox->selection) return; + self->listBox->selection = selection; + if(self->onChange) self->onChange(); +} + +static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) { + unsigned index = strunsigned(path_string); + self->setChecked(index, !self->checked(index)); + if(self->onTick) self->onTick(index); +} + +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + listBox->selection = -1; + object->widget = gtk_scrolled_window_new(0, 0); + widget->parent = &parent; + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + + lstring list; + list.split("\t", string("\t", text)); + + GType *v = (GType*)malloc(list.size() * sizeof(GType)); + for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); + listBox->store = gtk_list_store_newv(list.size(), v); + free(v); + + object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + g_object_unref(G_OBJECT(listBox->store)); + + for(unsigned i = 0; i < list.size(); i++) { + if(i == 0) { + listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "active", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, false); + gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); + g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); + } else { + listBox->column[i].renderer = gtk_cell_renderer_text_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "text", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, true); + } + listBox->column[i].label = gtk_label_new(list[i]); + gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); + gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); + gtk_widget_show(listBox->column[i].label); + } + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column + gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); + + g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void ListBox::setFocused() { + gtk_widget_grab_focus(object->subWidget); +} + +void ListBox::setHeaderVisible(bool visible) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible); +} + +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable); +} + +void ListBox::setFont(Font &font) { + Widget::setFont(font); + unsigned columns = 1; + while(true) { + if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break; + columns++; + } + for(unsigned i = 0; i < columns; i++) { + gtk_widget_modify_font(listBox->column[i].label, font.font->font); + } +} + +void ListBox::reset() { + listBox->selection = -1; + gtk_list_store_clear(GTK_LIST_STORE(listBox->store)); + gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store)); + //reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListBox is now empty + gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); + gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); +} + +void ListBox::resizeColumnsToContent() { + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget)); +} + +void ListBox::addItem(const string &text) { + lstring list; + list.split("\t", text); + GtkTreeIter iter; + gtk_list_store_append(listBox->store, &iter); + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); +} + +void ListBox::setItem(unsigned row, const string &text) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + for(unsigned i = 0; i <= row; i++) { + if(i == 0) gtk_tree_model_get_iter_first(model, &iter); + else gtk_tree_model_iter_next(model, &iter); + } + + lstring list; + list.split("\t", text); + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); +} + +bool ListBox::checked(unsigned row) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + bool state; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, 0, &state, -1); + gtk_tree_path_free(path); + return state; +} + +void ListBox::setChecked(unsigned row, bool checked) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + gtk_tree_model_get_iter(model, &iter, path); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1); + gtk_tree_path_free(path); +} + +optional ListBox::selection() { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 }; + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + signed current = -1; + if(auto position = selection()) current = position(); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + gtk_tree_selection_unselect_all(selection); + + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return; + if(row == 0) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return; + if(row == i) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + } +} + +ListBox::ListBox() { + listBox = new ListBox::Data; + listBox->checkable = false; +} diff --git a/snespurify/phoenix/gtk/menu.cpp b/snespurify/phoenix/gtk/menu.cpp new file mode 100755 index 00000000..fbfc67bc --- /dev/null +++ b/snespurify/phoenix/gtk/menu.cpp @@ -0,0 +1,129 @@ +static void Action_setFont(GtkWidget *widget, gpointer font) { + if(font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font); + } + } +} + +bool Action::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Action::setVisible(bool visible) { + gtk_widget_set_visible(object->widget, visible); +} + +bool Action::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Action::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +Action::Action() { + action = new Action::Data; + action->font = 0; +} + +void Menu::create(Window &parent, const string &text) { + action->font = parent.window->defaultFont; + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_bar_append(parent.object->menu, object->widget); + gtk_widget_show(object->widget); +} + +void Menu::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuSeparator::create(Menu &parent) { + action->font = parent.action->font; + object->widget = gtk_separator_menu_item_new(); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuItem_tick(MenuItem *self) { + if(self->onTick) self->onTick(); +} + +void MenuItem::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->widget = gtk_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuCheckItem_tick(MenuCheckItem *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->widget = gtk_check_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuCheckItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuCheckItem::setChecked(bool state) { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), state); + object->locked = false; +} + +static void MenuRadioItem_tick(MenuRadioItem *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + first = this; + action->font = parent.action->font; + object->parentMenu = &parent; + object->widget = gtk_radio_menu_item_new_with_label(0, text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + first = parent.first; + action->font = parent.action->font; + object->parentMenu = parent.object->parentMenu; + object->widget = gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(first->object->widget), text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(object->parentMenu->object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuRadioItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), true); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/messagewindow.cpp b/snespurify/phoenix/gtk/messagewindow.cpp new file mode 100755 index 00000000..6c41f022 --- /dev/null +++ b/snespurify/phoenix/gtk/messagewindow.cpp @@ -0,0 +1,65 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, gint response) { + if(response == GTK_RESPONSE_OK) return MessageWindow::Response::Ok; + if(response == GTK_RESPONSE_CANCEL) return MessageWindow::Response::Cancel; + if(response == GTK_RESPONSE_YES) return MessageWindow::Response::Yes; + if(response == GTK_RESPONSE_NO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} diff --git a/snespurify/phoenix/gtk/object.cpp b/snespurify/phoenix/gtk/object.cpp new file mode 100755 index 00000000..f09960ac --- /dev/null +++ b/snespurify/phoenix/gtk/object.cpp @@ -0,0 +1,57 @@ +struct Object::Data { + bool locked; + GtkWidget *widget; + GtkWidget *subWidget; + GtkWidget *menuContainer; + GtkWidget *formContainer; + GtkWidget *statusContainer; + GtkWidget *menu; + GtkWidget *status; + Menu *parentMenu; + Window *parentWindow; + GtkTextBuffer *textBuffer; + unsigned position; +}; + +struct Font::Data { + PangoFontDescription *font; +}; + +struct Action::Data { + Font *font; +}; + +struct Widget::Data { + Window *parent; +}; + +struct Window::Data { + Font *defaultFont; +}; + +struct Canvas::Data { + uint32_t *bufferRGB; + uint32_t *bufferBGR; + unsigned pitch; +}; + +struct ListBox::Data { + GtkListStore *store; + struct GtkColumn { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *label; + }; + linear_vector column; + bool checkable; + signed selection; +}; + +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + object = new Object::Data; + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/progressbar.cpp b/snespurify/phoenix/gtk/progressbar.cpp new file mode 100755 index 00000000..193e924d --- /dev/null +++ b/snespurify/phoenix/gtk/progressbar.cpp @@ -0,0 +1,12 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_progress_bar_new(); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ProgressBar::setPosition(unsigned position) { + position = position <= 100 ? position : 0; + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)position / 100.0); +} diff --git a/snespurify/phoenix/gtk/radiobox.cpp b/snespurify/phoenix/gtk/radiobox.cpp new file mode 100755 index 00000000..603e199f --- /dev/null +++ b/snespurify/phoenix/gtk/radiobox.cpp @@ -0,0 +1,36 @@ +static void RadioBox_tick(RadioBox *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + first = this; + object->parentWindow = &parent; + object->widget = gtk_radio_button_new_with_label(0, text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + first = parent.first; + object->parentWindow = parent.object->parentWindow; + object->widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(parent.object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(object->parentWindow->window->defaultFont) setFont(*object->parentWindow->window->defaultFont); + gtk_fixed_put(GTK_FIXED(object->parentWindow->object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool RadioBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void RadioBox::setChecked() { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), true); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/textbox.cpp b/snespurify/phoenix/gtk/textbox.cpp new file mode 100755 index 00000000..66455f03 --- /dev/null +++ b/snespurify/phoenix/gtk/textbox.cpp @@ -0,0 +1,33 @@ +static void TextBox_activate(TextBox *self) { + if(self->onActivate) self->onActivate(); +} + +static void TextBox_change(TextBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_entry_new(); + widget->parent = &parent; + gtk_entry_set_text(GTK_ENTRY(object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(TextBox_activate), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void TextBox::setEditable(bool editable) { + gtk_entry_set_editable(GTK_ENTRY(object->widget), editable); +} + +string TextBox::text() { + return gtk_entry_get_text(GTK_ENTRY(object->widget)); +} + +void TextBox::setText(const string &text) { + object->locked = true; + gtk_entry_set_text(GTK_ENTRY(object->widget), text); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/verticalslider.cpp b/snespurify/phoenix/gtk/verticalslider.cpp new file mode 100755 index 00000000..1cca9e4c --- /dev/null +++ b/snespurify/phoenix/gtk/verticalslider.cpp @@ -0,0 +1,25 @@ +static void VerticalSlider_change(VerticalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_vscale_new_with_range(0, length - 1, 1); + widget->parent = &parent; + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned VerticalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void VerticalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/snespurify/phoenix/gtk/viewport.cpp b/snespurify/phoenix/gtk/viewport.cpp new file mode 100755 index 00000000..3b407727 --- /dev/null +++ b/snespurify/phoenix/gtk/viewport.cpp @@ -0,0 +1,20 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_drawing_area_new(); + widget->parent = &parent; +//gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_set_size_request(object->widget, width, height); + + GdkColor color; + color.pixel = 0; + color.red = 0; + color.green = 0; + color.blue = 0; + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); + + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uintptr_t Viewport::handle() { + return GDK_WINDOW_XID(object->widget->window); +} diff --git a/snespurify/phoenix/gtk/widget.cpp b/snespurify/phoenix/gtk/widget.cpp new file mode 100755 index 00000000..4f0bf079 --- /dev/null +++ b/snespurify/phoenix/gtk/widget.cpp @@ -0,0 +1,47 @@ +static void Widget_setFont(GtkWidget *widget, gpointer font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font); + } +} + +void Widget::setFont(Font &font) { + Widget_setFont(object->widget, font.font->font); +} + +bool Widget::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Widget::setVisible(bool visible) { + if(visible) gtk_widget_show(object->widget); + else gtk_widget_hide(object->widget); +} + +bool Widget::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Widget::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +bool Widget::focused() { + return gtk_widget_is_focus(object->widget); +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + gtk_widget_grab_focus(object->widget); +} + +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + if(widget->parent == 0) return; + gtk_fixed_move(GTK_FIXED(widget->parent->object->formContainer), object->widget, x, y); + gtk_widget_set_size_request(object->widget, width, height); +} + +Widget::Widget() { + widget = new Widget::Data; + widget->parent = 0; +} diff --git a/snespurify/phoenix/gtk/window.cpp b/snespurify/phoenix/gtk/window.cpp new file mode 100755 index 00000000..5b289296 --- /dev/null +++ b/snespurify/phoenix/gtk/window.cpp @@ -0,0 +1,99 @@ +static gint Window_close(Window *window) { + if(window->onClose) { + if(window->onClose()) window->setVisible(false); + return true; + } + window->setVisible(false); + return true; +} + +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_move(GTK_WINDOW(object->widget), x, y); + + gtk_window_set_title(GTK_WINDOW(object->widget), text); + gtk_window_set_resizable(GTK_WINDOW(object->widget), false); + gtk_widget_set_app_paintable(object->widget, true); + + g_signal_connect_swapped(G_OBJECT(object->widget), "delete_event", G_CALLBACK(Window_close), (gpointer)this); + + object->menuContainer = gtk_vbox_new(false, 0); + gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer); + gtk_widget_show(object->menuContainer); + + object->menu = gtk_menu_bar_new(); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->menu, false, false, 0); + + object->formContainer = gtk_fixed_new(); + gtk_widget_set_size_request(object->formContainer, width, height); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->formContainer, true, true, 0); + gtk_widget_show(object->formContainer); + + object->statusContainer = gtk_event_box_new(); + object->status = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(object->status), false); + gtk_container_add(GTK_CONTAINER(object->statusContainer), object->status); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->statusContainer, false, false, 0); + gtk_widget_show(object->statusContainer); + + gtk_widget_realize(object->widget); +} + +bool Window::focused() { + return gtk_window_is_active(GTK_WINDOW(object->widget)); +} + +void Window::setFocused() { + gtk_window_present(GTK_WINDOW(object->widget)); +} + +Geometry Window::geometry() { + gint x, y, width, height; + gtk_window_get_position(GTK_WINDOW(object->widget), &x, &y); + gtk_widget_get_size_request(object->formContainer, &width, &height); + return Geometry(x, y, width, height); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + gtk_window_move(GTK_WINDOW(object->widget), x, y); + gtk_widget_set_size_request(object->formContainer, width, height); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = &font; +} + +void Window::setFont(Font &font) { + Widget_setFont(object->status, font.font->font); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + GdkColor color; + color.pixel = (red << 16) | (green << 8) | (blue << 0); + color.red = (red << 8) | (red << 0); + color.green = (green << 8) | (green << 0); + color.blue = (blue << 8) | (blue << 0); + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); +} + +void Window::setTitle(const string &text) { + gtk_window_set_title(GTK_WINDOW(object->widget), text); +} + +void Window::setStatusText(const string &text) { + gtk_statusbar_pop(GTK_STATUSBAR(object->status), 1); + gtk_statusbar_push(GTK_STATUSBAR(object->status), 1, text); +} + +void Window::setMenuVisible(bool visible) { + gtk_widget_set_visible(object->menu, visible); +} + +void Window::setStatusVisible(bool visible) { + gtk_widget_set_visible(object->status, visible); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; +} diff --git a/snespurify/phoenix/nall/Makefile b/snespurify/phoenix/nall/Makefile new file mode 100755 index 00000000..9a93bd23 --- /dev/null +++ b/snespurify/phoenix/nall/Makefile @@ -0,0 +1,109 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),win) + compiler := gcc + else ifeq ($(platform),osx) + compiler := gcc-mp-4.5 + else + compiler := gcc-4.5 + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snespurify/phoenix/nall/algorithm.hpp b/snespurify/phoenix/nall/algorithm.hpp new file mode 100755 index 00000000..037f0bb7 --- /dev/null +++ b/snespurify/phoenix/nall/algorithm.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } +} + +#endif diff --git a/snespurify/phoenix/nall/any.hpp b/snespurify/phoenix/nall/any.hpp new file mode 100755 index 00000000..b31cff3c --- /dev/null +++ b/snespurify/phoenix/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snespurify/phoenix/nall/array.hpp b/snespurify/phoenix/nall/array.hpp new file mode 100755 index 00000000..9cfe7758 --- /dev/null +++ b/snespurify/phoenix/nall/array.hpp @@ -0,0 +1,141 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void append(const T data) { + operator[](buffersize) = data; + } + + template void insert(unsigned index, const U list) { + unsigned listsize = container_size(list); + resize(buffersize + listsize); + memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T)); + foreach(item, list) pool[index++] = item; + } + + void insert(unsigned index, const T item) { + insert(index, array{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < buffersize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= buffersize) resize(index); //every element >= index was removed + else resize(buffersize - count); + } + + optional find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i }; + return { false, 0 }; + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/base64.hpp b/snespurify/phoenix/nall/base64.hpp new file mode 100755 index 00000000..e41c87b7 --- /dev/null +++ b/snespurify/phoenix/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/bit.hpp b/snespurify/phoenix/nall/bit.hpp new file mode 100755 index 00000000..169fc144 --- /dev/null +++ b/snespurify/phoenix/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snespurify/phoenix/nall/concept.hpp b/snespurify/phoenix/nall/concept.hpp new file mode 100755 index 00000000..47167e21 --- /dev/null +++ b/snespurify/phoenix/nall/concept.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +#include +#include + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snespurify/phoenix/nall/config.hpp b/snespurify/phoenix/nall/config.hpp new file mode 100755 index 00000000..f555158e --- /dev/null +++ b/snespurify/phoenix/nall/config.hpp @@ -0,0 +1,123 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: s.trim("\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0; + if(!qstrpos(line[i], " = ")) continue; + + lstring part; + part.qsplit(" = ", line[i]); + part[0].trim(); + part[1].trim(); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode::write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/crc32.hpp b/snespurify/phoenix/nall/crc32.hpp new file mode 100755 index 00000000..ad36fbf6 --- /dev/null +++ b/snespurify/phoenix/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/snespurify/phoenix/nall/detect.hpp b/snespurify/phoenix/nall/detect.hpp new file mode 100755 index 00000000..b4991aaf --- /dev/null +++ b/snespurify/phoenix/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snespurify/phoenix/nall/dictionary.hpp b/snespurify/phoenix/nall/dictionary.hpp new file mode 100755 index 00000000..dcb04151 --- /dev/null +++ b/snespurify/phoenix/nall/dictionary.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + if(auto pos = strpos(input, "}}")) { + string temp = substr(input, pos() + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + part[0].trim(); + part[1].trim(); + + //remove quotes + part[0].trim<1>("\""); + part[1].trim<1>("\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/directory.hpp b/snespurify/phoenix/nall/directory.hpp new file mode 100755 index 00000000..df0bf086 --- /dev/null +++ b/snespurify/phoenix/nall/directory.hpp @@ -0,0 +1,151 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static bool exists(const string &pathname); + static lstring folders(const string &pathname, const string &pattern = "*"); + static lstring files(const string &pathname, const string &pattern = "*"); + static lstring contents(const string &pathname, const string &pattern = "*"); +}; + +#if defined(_WIN32) + inline bool directory::exists(const string &pathname) { + DWORD result = GetFileAttributes(utf16_t(pathname)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#else + inline bool directory::exists(const string &pathname) { + DIR *dp = opendir(pathname); + if(!dp) return false; + closedir(dp); + return true; + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if(ep->d_type & DT_DIR) { + if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/")); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) { + if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#endif + +} + +#endif diff --git a/snespurify/phoenix/nall/dl.hpp b/snespurify/phoenix/nall/dl.hpp new file mode 100755 index 00000000..6fa7603f --- /dev/null +++ b/snespurify/phoenix/nall/dl.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*, const char* = ""); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*, const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snespurify/phoenix/nall/endian.hpp b/snespurify/phoenix/nall/endian.hpp new file mode 100755 index 00000000..40d15633 --- /dev/null +++ b/snespurify/phoenix/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snespurify/phoenix/nall/file.hpp b/snespurify/phoenix/nall/file.hpp new file mode 100755 index 00000000..103c7d4a --- /dev/null +++ b/snespurify/phoenix/nall/file.hpp @@ -0,0 +1,261 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + enum class index : unsigned { absolute, relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode::write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + template void print(Args... args) { + string data(args...); + const char *p = data; + while(*p) write(*p++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, index index_ = index::absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(index_) { + case index::absolute: req_offset = offset; break; + case index::relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode::read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, mode mode_) { + if(fp) return false; + + switch(file_mode = mode_) { + #if !defined(_WIN32) + case mode::read: fp = fopen(fn, "rb"); break; + case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode::readwrite: fp = fopen(fn, "rb+"); break; + case mode::writeread: fp = fopen(fn, "wb+"); break; + #else + case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode::read; + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + mode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/filemap.hpp b/snespurify/phoenix/nall/filemap.hpp new file mode 100755 index 00000000..52acb2fa --- /dev/null +++ b/snespurify/phoenix/nall/filemap.hpp @@ -0,0 +1,200 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + + bool opened() const { return p_opened(); } + bool open(const char *filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snespurify/phoenix/nall/foreach.hpp b/snespurify/phoenix/nall/foreach.hpp new file mode 100755 index 00000000..00a039f3 --- /dev/null +++ b/snespurify/phoenix/nall/foreach.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#include +#include + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#endif diff --git a/snespurify/phoenix/nall/function.hpp b/snespurify/phoenix/nall/function.hpp new file mode 100755 index 00000000..645991fb --- /dev/null +++ b/snespurify/phoenix/nall/function.hpp @@ -0,0 +1,60 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +namespace nall { + template class function; + + template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + virtual ~container() {} + } *callback; + + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function)(P...)) : function(function) {} + }; + + template struct member : container { + R (C::*function)(P...); + C *object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function)(P...), C *object) : function(function), object(object) {} + }; + + template struct lambda : container { + L object; + R operator()(P... p) const { return object(std::forward

(p)...); } + container* copy() const { return new lambda(object); } + lambda(const L& object) : object(object) {} + }; + + public: + operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } + void reset() { if(callback) { delete callback; callback = 0; } } + + function& operator=(const function &source) { + if(this != &source) { + if(callback) { delete callback; callback = 0; } + if(source.callback) callback = source.callback->copy(); + } + return *this; + } + + function(const function &source) { operator=(source); } + function() : callback(0) {} + function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C *object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C *object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/input.hpp b/snespurify/phoenix/nall/input.hpp new file mode 100755 index 00000000..28b10453 --- /dev/null +++ b/snespurify/phoenix/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + s.ltrim("KB"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + s.ltrim("MS"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + s.ltrim("JP"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snespurify/phoenix/nall/lzss.hpp b/snespurify/phoenix/nall/lzss.hpp new file mode 100755 index 00000000..202bc814 --- /dev/null +++ b/snespurify/phoenix/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/moduloarray.hpp b/snespurify/phoenix/nall/moduloarray.hpp new file mode 100755 index 00000000..be549ae9 --- /dev/null +++ b/snespurify/phoenix/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/platform.hpp b/snespurify/phoenix/nall/platform.hpp new file mode 100755 index 00000000..72eeec09 --- /dev/null +++ b/snespurify/phoenix/nall/platform.hpp @@ -0,0 +1,122 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface + #define dllexport __declspec(dllexport) +#else + #include + #include + #include + #define dllexport +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +//========================= +//file system functionality +//========================= + +#if defined(_WIN32) + inline char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + inline char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + inline char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } +#else + //realpath() already exists + + inline char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + inline char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } +#endif + +#endif + diff --git a/snespurify/phoenix/nall/priorityqueue.hpp b/snespurify/phoenix/nall/priorityqueue.hpp new file mode 100755 index 00000000..7104e791 --- /dev/null +++ b/snespurify/phoenix/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/property.hpp b/snespurify/phoenix/nall/property.hpp new file mode 100755 index 00000000..6fd33acd --- /dev/null +++ b/snespurify/phoenix/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/random.hpp b/snespurify/phoenix/nall/random.hpp new file mode 100755 index 00000000..74ebc2d2 --- /dev/null +++ b/snespurify/phoenix/nall/random.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +namespace nall { + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } + + struct random_cyclic { + unsigned seed; + inline unsigned operator()() { + return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320); + } + random_cyclic() : seed(0) {} + }; +} + +#endif diff --git a/snespurify/phoenix/nall/serial.hpp b/snespurify/phoenix/nall/serial.hpp new file mode 100755 index 00000000..9ac8451a --- /dev/null +++ b/snespurify/phoenix/nall/serial.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/serializer.hpp b/snespurify/phoenix/nall/serializer.hpp new file mode 100755 index 00000000..ff2337ab --- /dev/null +++ b/snespurify/phoenix/nall/serializer.hpp @@ -0,0 +1,146 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + icapacity = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snespurify/phoenix/nall/sha256.hpp b/snespurify/phoenix/nall/sha256.hpp new file mode 100755 index 00000000..7f41f04e --- /dev/null +++ b/snespurify/phoenix/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/snespurify/phoenix/nall/snes/info.hpp b/snespurify/phoenix/nall/snes/info.hpp new file mode 100755 index 00000000..67db4748 --- /dev/null +++ b/snespurify/phoenix/nall/snes/info.hpp @@ -0,0 +1,864 @@ +#ifndef NALL_SNES_INFO_HPP +#define NALL_SNES_INFO_HPP + +namespace nall { + +class snes_information { +public: + string xml_memory_map; + + inline snes_information(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +snes_information::snes_information(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xml_memory_map = xml; + return; + } + + xml << "\n"; + + if(type == TypeSuperGameBoy1Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(type == TypeSuperGameBoy2Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(has_spc7110) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + xml << " \n"; + } else { + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == ExLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_sdd1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_cx4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + //ST-0011 addresses not verified; chip is unsupported + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xml_memory_map = xml; +} + +void snes_information::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned snes_information::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned snes_information::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned snes_information::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool snes_information::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/sort.hpp b/snespurify/phoenix/nall/sort.hpp new file mode 100755 index 00000000..23c317a5 --- /dev/null +++ b/snespurify/phoenix/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snespurify/phoenix/nall/static.hpp b/snespurify/phoenix/nall/static.hpp new file mode 100755 index 00000000..4acb9fd0 --- /dev/null +++ b/snespurify/phoenix/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/stdint.hpp b/snespurify/phoenix/nall/stdint.hpp new file mode 100755 index 00000000..d8b6c788 --- /dev/null +++ b/snespurify/phoenix/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snespurify/phoenix/nall/string.hpp b/snespurify/phoenix/nall/string.hpp new file mode 100755 index 00000000..9acc2e9d --- /dev/null +++ b/snespurify/phoenix/nall/string.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/string/base.hpp b/snespurify/phoenix/nall/string/base.hpp new file mode 100755 index 00000000..77f15e17 --- /dev/null +++ b/snespurify/phoenix/nall/string/base.hpp @@ -0,0 +1,159 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + + inline string& assign(const char*); + inline string& append(const char*); + inline string& append(bool); + inline string& append(signed int value); + inline string& append(unsigned int value); + inline string& append(double value); + + inline bool readfile(const char*); + + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + inline unsigned length() const; + + inline bool equals(const char*) const; + inline bool iequals(const char*) const; + + inline bool wildcard(const char*) const; + inline bool iwildcard(const char*) const; + + inline bool beginswith(const char*) const; + inline bool ibeginswith(const char*) const; + inline bool endswith(const char*) const; + inline bool iendswith(const char*) const; + + inline string& lower(); + inline string& upper(); + inline string& transform(const char *before, const char *after); + + template inline string& ltrim(const char *key = " "); + template inline string& rtrim(const char *key = " "); + template inline string& trim (const char *key = " "); + + inline optional position(const char *key) const; + inline optional qposition(const char *key) const; + + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + inline string& operator=(string&&); + + template inline string(Args&&... args); + inline string(const string&); + inline string(string&&); + inline ~string(); + + protected: + char *data; + unsigned size; + + #if defined(QSTRING_H) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline optional find(const char*) const; + template inline void split (const char*, const char*); + template inline void qsplit(const char*, const char*); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *str1, const char *str2); + inline bool wildcard(const char *str, const char *pattern); + inline bool iwildcard(const char *str, const char *pattern); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //platform.hpp + inline string realpath(const char *name); + inline string userpath(); + inline string currentpath(); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //strpos.hpp + inline optional strpos(const char *str, const char *key); + inline optional qstrpos(const char *str, const char *key); + + //trim.hpp + template inline char* ltrim(char *str, const char *key = " "); + template inline char* rtrim(char *str, const char *key = " "); + template inline char* trim (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline void print(Args&&... args); +}; + +#endif diff --git a/snespurify/phoenix/nall/string/bsv.hpp b/snespurify/phoenix/nall/string/bsv.hpp new file mode 100755 index 00000000..d4b919e0 --- /dev/null +++ b/snespurify/phoenix/nall/string/bsv.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_BSV_HPP +#define NALL_STRING_BSV_HPP + +//BSV parser +//version 0.01 + +namespace nall { + +inline string bsv_decode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '}' ) return ""; + if(*input == '\r') return ""; + if(*input == '\n') return ""; + + //normal characters + if(*input != '{') { output[offset++] = *input++; continue; } + + //entities + if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } + if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } + if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } + + //illegal entities + return ""; + } + output[offset] = 0; + return output; +} + +inline string bsv_encode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '\r') return ""; + + if(*input == '\n') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'f'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '{') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '}') { + output[offset++] = '{'; + output[offset++] = 'r'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + output[offset++] = *input++; + } + output[offset] = 0; + return output; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/cast.hpp b/snespurify/phoenix/nall/string/cast.hpp new file mode 100755 index 00000000..5b17c408 --- /dev/null +++ b/snespurify/phoenix/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QSTRING_H) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snespurify/phoenix/nall/string/compare.hpp b/snespurify/phoenix/nall/string/compare.hpp new file mode 100755 index 00000000..bce0895b --- /dev/null +++ b/snespurify/phoenix/nall/string/compare.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *str1, const char *str2) { + while(*str1) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +bool wildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool iwildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/convert.hpp b/snespurify/phoenix/nall/string/convert.hpp new file mode 100755 index 00000000..12a6c1ff --- /dev/null +++ b/snespurify/phoenix/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.' || x == ',') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/core.hpp b/snespurify/phoenix/nall/string/core.hpp new file mode 100755 index 00000000..bad29030 --- /dev/null +++ b/snespurify/phoenix/nall/string/core.hpp @@ -0,0 +1,139 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string& string::append(bool value) { append(value ? "true" : "false"); return *this; } +string& string::append(signed int value) { append(strsigned(value)); return *this; } +string& string::append(unsigned int value) { append(strunsigned(value)); return *this; } +string& string::append(double value) { append(strdouble(value)); return *this; } + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +static void istring(string &output) { +} + +template +static void istring(string &output, const T &value, Args&&... args) { + output.append(value); + istring(output, std::forward(args)...); +} + +template string::string(Args&&... args) { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; + istring(*this, std::forward(args)...); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string::~string() { + if(data) free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +optional lstring::find(const char *key) const { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return { true, i }; + } + return { false, 0 }; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/filename.hpp b/snespurify/phoenix/nall/string/filename.hpp new file mode 100755 index 00000000..93d605ae --- /dev/null +++ b/snespurify/phoenix/nall/string/filename.hpp @@ -0,0 +1,63 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/" +// "foo/" -> "foo/" +// "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/math.hpp b/snespurify/phoenix/nall/string/math.hpp new file mode 100755 index 00000000..ea8b99c8 --- /dev/null +++ b/snespurify/phoenix/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/platform.hpp b/snespurify/phoenix/nall/string/platform.hpp new file mode 100755 index 00000000..42c1a756 --- /dev/null +++ b/snespurify/phoenix/nall/string/platform.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_PLATFORM_HPP +#define NALL_STRING_PLATFORM_HPP + +namespace nall { + +string realpath(const char *name) { + char path[PATH_MAX]; + if(::realpath(name, path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string userpath() { + char path[PATH_MAX]; + if(::userpath(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string currentpath() { + char path[PATH_MAX]; + if(::getcwd(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/replace.hpp b/snespurify/phoenix/nall/string/replace.hpp new file mode 100755 index 00000000..db405a9b --- /dev/null +++ b/snespurify/phoenix/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snespurify/phoenix/nall/string/split.hpp b/snespurify/phoenix/nall/string/split.hpp new file mode 100755 index 00000000..8d3ca877 --- /dev/null +++ b/snespurify/phoenix/nall/string/split.hpp @@ -0,0 +1,58 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +template void lstring::split(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +template void lstring::qsplit(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snespurify/phoenix/nall/string/strl.hpp b/snespurify/phoenix/nall/string/strl.hpp new file mode 100755 index 00000000..84c841fa --- /dev/null +++ b/snespurify/phoenix/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/strpos.hpp b/snespurify/phoenix/nall/string/strpos.hpp new file mode 100755 index 00000000..1907a2f3 --- /dev/null +++ b/snespurify/phoenix/nall/string/strpos.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_STRPOS_HPP +#define NALL_STRING_STRPOS_HPP + +//usage example: +//if(auto pos = strpos(str, key)) print(pos(), "\n"); +//prints position of key within str, only if it is found + +namespace nall { + +optional strpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) return { true, i }; + } + + return { false, 0 }; +} + +optional qstrpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) return { true, i }; + i++; + } + + return { false, 0 }; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/trim.hpp b/snespurify/phoenix/nall/string/trim.hpp new file mode 100755 index 00000000..f5355d7d --- /dev/null +++ b/snespurify/phoenix/nall/string/trim.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +//limit defaults to zero, which will underflow on first compare; equivalent to no limit +template char* ltrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + if(--limit == 0) break; + } + return str; +} + +template char* rtrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strend(str, key)) { + str[strlen(str) - strlen(key)] = 0; + if(--limit == 0) break; + } + return str; +} + +template char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/utility.hpp b/snespurify/phoenix/nall/string/utility.hpp new file mode 100755 index 00000000..d2bad881 --- /dev/null +++ b/snespurify/phoenix/nall/string/utility.hpp @@ -0,0 +1,157 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/variadic.hpp b/snespurify/phoenix/nall/string/variadic.hpp new file mode 100755 index 00000000..6c027fc8 --- /dev/null +++ b/snespurify/phoenix/nall/string/variadic.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +template inline void print(Args&&... args) { + printf("%s", (const char*)string(std::forward(args)...)); +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/wrapper.hpp b/snespurify/phoenix/nall/string/wrapper.hpp new file mode 100755 index 00000000..eadf0a10 --- /dev/null +++ b/snespurify/phoenix/nall/string/wrapper.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_STRING_WRAPPER_HPP +#define NALL_STRING_WRAPPER_HPP + +namespace nall { + +unsigned string::length() const { return strlen(data); } + +bool string::equals(const char *str) const { return !strcmp(data, str); } +bool string::iequals(const char *str) const { return !stricmp(data, str); } + +bool string::wildcard(const char *str) const { return nall::wildcard(data, str); } +bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); } + +bool string::beginswith(const char *str) const { return strbegin(data, str); } +bool string::ibeginswith(const char *str) const { return stribegin(data, str); } + +bool string::endswith(const char *str) const { return strend(data, str); } +bool string::iendswith(const char *str) const { return striend(data, str); } + +string& string::lower() { nall::strlower(data); return *this; } +string& string::upper() { nall::strupper(data); return *this; } +string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; } + +template string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } +template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } +template string& string::trim (const char *key) { nall::trim (data, key); return *this; } + +optional string::position(const char *key) const { return strpos(data, key); } +optional string::qposition(const char *key) const { return qstrpos(data, key); } + +} + +#endif diff --git a/snespurify/phoenix/nall/string/xml.hpp b/snespurify/phoenix/nall/string/xml.hpp new file mode 100755 index 00000000..185a89f9 --- /dev/null +++ b/snespurify/phoenix/nall/string/xml.hpp @@ -0,0 +1,266 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, "")) { + source += pos() + 3; + continue; + } else { + return ""; + } + } + + if(strbegin(source, "")) { + if(pos() - 9 > 0) { + string cdata = substr(source, 9, pos() - 9); + data << cdata; + offset += strlen(cdata); + } + source += 9 + offset + 3; + continue; + } else { + return ""; + } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ")) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + data.rtrim(); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'"); + else throw "..."; + attribute.append(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + if(auto offset = strpos(data, "-->")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + if(strbegin(data, "![CDATA[")) { + if(auto offset = strpos(data, "]]>")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + string tag = substr(data, 0, offset()); + data += offset() + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + tag.rtrim<1>("?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + tag.rtrim<1>("/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + tag = substr(data, 0, offset()); + data += offset() + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ")) tag.replace(" ", " "); + tag.rtrim(); + + if(name != tag) throw "..."; + return true; + } + } else { + element.append(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.append(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/ups.hpp b/snespurify/phoenix/nall/ups.hpp new file mode 100755 index 00000000..ffcdb2d7 --- /dev/null +++ b/snespurify/phoenix/nall/ups.hpp @@ -0,0 +1,223 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include +#include +#include +#include + +namespace nall { + +struct ups { + enum class result : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; + + function progress; + + result create( + const uint8_t *sourcedata, unsigned sourcelength, + const uint8_t *targetdata, unsigned targetlength, + const char *patchfilename + ) { + source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata; + source_length = sourcelength, target_length = targetlength; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; + + if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable; + + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); + + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); + + if(x == y) { + offset++; + continue; + } + + encode(offset++ - relative); + patch_write(x ^ y); + + while(true) { + if(offset >= output_length) { + patch_write(0x00); + break; + } + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result::success; + } + + result apply( + const uint8_t *patchdata, unsigned patchlength, + const uint8_t *sourcedata, unsigned sourcelength, + uint8_t *targetdata, unsigned &targetlength + ) { + patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata; + patch_length = patchlength, source_length = sourcelength, target_length = targetlength; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result::patch_invalid; + if(patch_read() != 'U') return result::patch_invalid; + if(patch_read() != 'P') return result::patch_invalid; + if(patch_read() != 'S') return result::patch_invalid; + if(patch_read() != '1') return result::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid; + targetlength = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < targetlength) return result::target_too_small; + target_length = targetlength; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; + } + } + while(source_offset < source_length) target_write(source_read()); + while(target_offset < target_length) target_write(source_read()); + + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success; + return result::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success; + return result::target_invalid; + } else { + return result::source_invalid; + } + } + +private: + uint8_t *patch_data, *source_data, *target_data; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + +} + +#endif diff --git a/snespurify/phoenix/nall/utf8.hpp b/snespurify/phoenix/nall/utf8.hpp new file mode 100755 index 00000000..f5597b85 --- /dev/null +++ b/snespurify/phoenix/nall/utf8.hpp @@ -0,0 +1,86 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t&) = delete; + utf8_t& operator=(const utf8_t&) = delete; + + private: + char *buffer; + }; + + inline void utf8_args(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snespurify/phoenix/nall/utility.hpp b/snespurify/phoenix/nall/utility.hpp new file mode 100755 index 00000000..60bda562 --- /dev/null +++ b/snespurify/phoenix/nall/utility.hpp @@ -0,0 +1,39 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template class optional { + bool valid; + T value; + public: + inline operator bool() const { return valid; } + inline const T& operator()() const { if(!valid) throw; return value; } + inline optional(bool valid, const T &value) : valid(valid), value(value) {} + }; + + template inline T* allocate(unsigned size, const T &value) { + T *array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snespurify/phoenix/nall/varint.hpp b/snespurify/phoenix/nall/varint.hpp new file mode 100755 index 00000000..fe4732b1 --- /dev/null +++ b/snespurify/phoenix/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snespurify/phoenix/nall/vector.hpp b/snespurify/phoenix/nall/vector.hpp new file mode 100755 index 00000000..543c7b69 --- /dev/null +++ b/snespurify/phoenix/nall/vector.hpp @@ -0,0 +1,281 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + template void insert(unsigned index, const U list) { + linear_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, linear_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + template void insert(unsigned index, const U list) { + pointer_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(*pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, pointer_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + *pool[i] = *pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/phoenix.cpp b/snespurify/phoenix/phoenix.cpp new file mode 100755 index 00000000..bf51451b --- /dev/null +++ b/snespurify/phoenix/phoenix.cpp @@ -0,0 +1,17 @@ +#if defined(PHOENIX_WINDOWS) + #define UNICODE + #define WINVER 0x0501 + #define _WIN32_WINNT 0x0501 + #define _WIN32_IE 0x0600 + #define NOMINMAX +#endif + +#include "phoenix.hpp" + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.cpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.cpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.cpp" +#endif diff --git a/snespurify/phoenix/phoenix.hpp b/snespurify/phoenix/phoenix.hpp new file mode 100755 index 00000000..99c82d15 --- /dev/null +++ b/snespurify/phoenix/phoenix.hpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.hpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.hpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.hpp" +#endif diff --git a/snespurify/phoenix/qt/button.cpp b/snespurify/phoenix/qt/button.cpp new file mode 100755 index 00000000..e11a14b5 --- /dev/null +++ b/snespurify/phoenix/qt/button.cpp @@ -0,0 +1,13 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + button->setParent(parent.window->container); + button->setGeometry(x, y, width, height); + button->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) button->setFont(*parent.window->defaultFont); + button->show(); + button->connect(button, SIGNAL(released()), SLOT(onTick())); +} + +Button::Button() { + button = new Button::Data(*this); + widget->widget = button; +} diff --git a/snespurify/phoenix/qt/canvas.cpp b/snespurify/phoenix/qt/canvas.cpp new file mode 100755 index 00000000..a0887604 --- /dev/null +++ b/snespurify/phoenix/qt/canvas.cpp @@ -0,0 +1,39 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setParent(parent.window->container); + canvas->setGeometry(x, y, width, height); + canvas->show(); +} + +void Canvas::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + delete canvas->image; + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setGeometry(x, y, width, height); + canvas->update(); +} + +uint32_t* Canvas::buffer() { + return (uint32_t*)canvas->image->bits(); +} + +void Canvas::redraw() { + canvas->update(); +} + +Canvas::Canvas() { + canvas = new Canvas::Data(*this); + canvas->image = 0; + widget->widget = canvas; +} + +Canvas::~Canvas() { + if(canvas->image) delete canvas->image; + delete canvas; +} + +void Canvas::Data::paintEvent(QPaintEvent *event) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} diff --git a/snespurify/phoenix/qt/checkbox.cpp b/snespurify/phoenix/qt/checkbox.cpp new file mode 100755 index 00000000..a0ab121b --- /dev/null +++ b/snespurify/phoenix/qt/checkbox.cpp @@ -0,0 +1,21 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + checkBox->setParent(parent.window->container); + checkBox->setGeometry(x, y, width, height); + checkBox->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) checkBox->setFont(*parent.window->defaultFont); + checkBox->show(); + checkBox->connect(checkBox, SIGNAL(stateChanged(int)), SLOT(onTick())); +} + +bool CheckBox::checked() { + return checkBox->isChecked(); +} + +void CheckBox::setChecked(bool checked) { + checkBox->setChecked(checked); +} + +CheckBox::CheckBox() { + checkBox = new CheckBox::Data(*this); + widget->widget = checkBox; +} diff --git a/snespurify/phoenix/qt/combobox.cpp b/snespurify/phoenix/qt/combobox.cpp new file mode 100755 index 00000000..41bd7d8f --- /dev/null +++ b/snespurify/phoenix/qt/combobox.cpp @@ -0,0 +1,38 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + comboBox->setParent(parent.window->container); + comboBox->setGeometry(x, y, width, height); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } + + comboBox->connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(onChange())); + if(parent.window->defaultFont) comboBox->setFont(*parent.window->defaultFont); + comboBox->show(); +} + +void ComboBox::reset() { + while(comboBox->count()) comboBox->removeItem(0); +} + +void ComboBox::addItem(const string &text) { + comboBox->addItem(QString::fromUtf8(text)); +} + +unsigned ComboBox::selection() { + signed index = comboBox->currentIndex(); + return (index >= 0 ? index : 0); +} + +void ComboBox::setSelection(unsigned row) { + object->locked = true; + comboBox->setCurrentIndex(row); + object->locked = false; +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data(*this); + widget->widget = comboBox; +} diff --git a/snespurify/phoenix/qt/editbox.cpp b/snespurify/phoenix/qt/editbox.cpp new file mode 100755 index 00000000..cb8bc61b --- /dev/null +++ b/snespurify/phoenix/qt/editbox.cpp @@ -0,0 +1,29 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + editBox->setParent(parent.window->container); + editBox->setGeometry(x, y, width, height); + editBox->setPlainText(QString::fromUtf8(text)); + if(parent.window->defaultFont) editBox->setFont(*parent.window->defaultFont); + editBox->show(); + editBox->connect(editBox, SIGNAL(textChanged()), SLOT(onChange())); +} + +void EditBox::setEditable(bool editable) { + editBox->setReadOnly(editable == false); +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->setWordWrapMode(wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap); +} + +string EditBox::text() { + return editBox->toPlainText().toUtf8().constData(); +} + +void EditBox::setText(const string &text) { + editBox->setPlainText(QString::fromUtf8(text)); +} + +EditBox::EditBox() { + editBox = new EditBox::Data(*this); + widget->widget = editBox; +} diff --git a/snespurify/phoenix/qt/font.cpp b/snespurify/phoenix/qt/font.cpp new file mode 100755 index 00000000..01d6bab6 --- /dev/null +++ b/snespurify/phoenix/qt/font.cpp @@ -0,0 +1,14 @@ +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->setFamily(QString::fromUtf8(name)); + font->setPointSize(size); + font->setBold((style & Style::Bold) == Style::Bold); + font->setItalic((style & Style::Italic) == Style::Italic); +} + +Font::Font() { + font = new Font::Data(*this); +} + +Font::~Font() { + delete font; +} diff --git a/snespurify/phoenix/qt/horizontalslider.cpp b/snespurify/phoenix/qt/horizontalslider.cpp new file mode 100755 index 00000000..48f9e18c --- /dev/null +++ b/snespurify/phoenix/qt/horizontalslider.cpp @@ -0,0 +1,22 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + horizontalSlider->setParent(parent.window->container); + horizontalSlider->setGeometry(x, y, width, height); + horizontalSlider->setRange(0, length - 1); + horizontalSlider->setPageStep(length >> 3); + horizontalSlider->connect(horizontalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + horizontalSlider->show(); +} + +unsigned HorizontalSlider::position() { + return horizontalSlider->value(); +} + +void HorizontalSlider::setPosition(unsigned position) { + horizontalSlider->setValue(position); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data(*this); + widget->widget = horizontalSlider; +} diff --git a/snespurify/phoenix/qt/label.cpp b/snespurify/phoenix/qt/label.cpp new file mode 100755 index 00000000..cee55452 --- /dev/null +++ b/snespurify/phoenix/qt/label.cpp @@ -0,0 +1,16 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + label->setParent(parent.window->container); + label->setGeometry(x, y, width, height); + label->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) label->setFont(*parent.window->defaultFont); + label->show(); +} + +void Label::setText(const string &text) { + label->setText(QString::fromUtf8(text)); +} + +Label::Label() { + label = new Label::Data(*this); + widget->widget = label; +} diff --git a/snespurify/phoenix/qt/listbox.cpp b/snespurify/phoenix/qt/listbox.cpp new file mode 100755 index 00000000..ac5e97ee --- /dev/null +++ b/snespurify/phoenix/qt/listbox.cpp @@ -0,0 +1,102 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + listBox->setParent(parent.window->container); + listBox->setGeometry(x, y, width, height); + listBox->setAllColumnsShowFocus(true); + listBox->setRootIsDecorated(false); + + lstring list; + list.split("\t", text); + QStringList labels; + foreach(item, list) labels << QString::fromUtf8(item); + listBox->setColumnCount(list.size()); + listBox->setHeaderLabels(labels); + for(unsigned i = 0; i < list.size(); i++) listBox->resizeColumnToContents(i); + + listBox->setHeaderHidden(true); + listBox->setAlternatingRowColors(list.size() >= 2); + listBox->connect(listBox, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate())); + listBox->connect(listBox, SIGNAL(itemSelectionChanged()), SLOT(onChange())); + listBox->connect(listBox, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onTick(QTreeWidgetItem*))); + if(parent.window->defaultFont) listBox->setFont(*parent.window->defaultFont); + listBox->show(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + listBox->setHeaderHidden(headerVisible == false); +} + +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(listBox->checkable) { + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) items[i]->setCheckState(0, Qt::Unchecked); + } +} + +void ListBox::reset() { + listBox->clear(); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columnCount(); i++) listBox->resizeColumnToContents(i); +} + +void ListBox::addItem(const string &text) { + object->locked = true; + auto items = listBox->findItems("", Qt::MatchContains); + QTreeWidgetItem *item = new QTreeWidgetItem(listBox); + if(listBox->checkable) item->setCheckState(0, Qt::Unchecked); + item->setData(0, Qt::UserRole, (unsigned)items.size()); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, QString::fromUtf8(list[i])); + object->locked = false; +} + +void ListBox::setItem(unsigned row, const string &text) { + object->locked = true; + QTreeWidgetItem *item = listBox->topLevelItem(row); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, QString::fromUtf8(list[i])); + object->locked = false; +} + +bool ListBox::checked(unsigned row) { + QTreeWidgetItem *item = listBox->topLevelItem(row); + return (item ? item->checkState(0) == Qt::Checked : false); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + QTreeWidgetItem *item = listBox->topLevelItem(row); + if(item) item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); + object->locked = false; +} + +optional ListBox::selection() { + QTreeWidgetItem *item = listBox->currentItem(); + if(item == 0) return { false, 0 }; + if(item->isSelected() == false) return { false, 0 }; + unsigned row = item->data(0, Qt::UserRole).toUInt(); + return { true, row }; +} + +void ListBox::setSelection(unsigned row) { + object->locked = true; + QTreeWidgetItem *item = listBox->currentItem(); + if(item) item->setSelected(false); + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) { + if(items[i]->data(0, Qt::UserRole).toUInt() == row) { + listBox->setCurrentItem(items[i]); + break; + } + } + object->locked = false; +} + +ListBox::ListBox() { + listBox = new ListBox::Data(*this); + widget->widget = listBox; +} diff --git a/snespurify/phoenix/qt/menu.cpp b/snespurify/phoenix/qt/menu.cpp new file mode 100755 index 00000000..8e6fa819 --- /dev/null +++ b/snespurify/phoenix/qt/menu.cpp @@ -0,0 +1,169 @@ +void Menu::create(Window &parent, const string &text) { + menu->parent = &parent; + if(menu->parent->window->defaultFont) menu->setFont(*menu->parent->window->defaultFont); + menu->setTitle(QString::fromUtf8(text)); + parent.window->menuBar->addMenu(menu); +} + +void Menu::create(Menu &parent, const string &text) { + menu->parent = parent.menu->parent; + if(menu->parent->window->defaultFont) menu->setFont(*menu->parent->window->defaultFont); + menu->setTitle(QString::fromUtf8(text)); + parent.menu->addMenu(menu); +} + +bool Menu::visible() { + return menu->isVisible(); +} + +void Menu::setVisible(bool visible) { + menu->setVisible(visible); +} + +bool Menu::enabled() { + return menu->isEnabled(); +} + +void Menu::setEnabled(bool enabled) { + menu->setEnabled(enabled); +} + +Menu::Menu() { + menu = new Menu::Data(*this); +} + +void MenuSeparator::create(Menu &parent) { + menuSeparator->action = parent.menu->addSeparator(); +} + +bool MenuSeparator::visible() { + return menuSeparator->action->isVisible(); +} + +void MenuSeparator::setVisible(bool visible) { + menuSeparator->action->setVisible(visible); +} + +bool MenuSeparator::enabled() { + return menuSeparator->action->isEnabled(); +} + +void MenuSeparator::setEnabled(bool enabled) { + menuSeparator->action->setEnabled(enabled); +} + +MenuSeparator::MenuSeparator() { + menuSeparator = new MenuSeparator::Data(*this); +} + +void MenuItem::create(Menu &parent, const string &text) { + menuItem->setText(QString::fromUtf8(text)); + menuItem->connect(menuItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuItem); +} + +bool MenuItem::visible() { + return menuItem->isVisible(); +} + +void MenuItem::setVisible(bool visible) { + menuItem->setVisible(visible); +} + +bool MenuItem::enabled() { + return menuItem->isEnabled(); +} + +void MenuItem::setEnabled(bool enabled) { + menuItem->setEnabled(enabled); +} + +MenuItem::MenuItem() { + menuItem = new MenuItem::Data(*this); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + menuCheckItem->setText(QString::fromUtf8(text)); + menuCheckItem->setCheckable(true); + menuCheckItem->connect(menuCheckItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuCheckItem); +} + +bool MenuCheckItem::visible() { + return menuCheckItem->isVisible(); +} + +void MenuCheckItem::setVisible(bool visible) { + menuCheckItem->setVisible(visible); +} + +bool MenuCheckItem::enabled() { + return menuCheckItem->isEnabled(); +} + +void MenuCheckItem::setEnabled(bool enabled) { + menuCheckItem->setEnabled(enabled); +} + +bool MenuCheckItem::checked() { + return menuCheckItem->isChecked(); +} + +void MenuCheckItem::setChecked(bool checked) { + menuCheckItem->setChecked(checked); +} + +MenuCheckItem::MenuCheckItem() { + menuCheckItem = new MenuCheckItem::Data(*this); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + menuRadioItem->parent = &parent; + menuRadioItem->actionGroup = new QActionGroup(0); + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(QString::fromUtf8(text)); + menuRadioItem->setCheckable(true); + menuRadioItem->setChecked(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + menuRadioItem->parent = parent.menuRadioItem->parent; + menuRadioItem->actionGroup = parent.menuRadioItem->actionGroup; + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(QString::fromUtf8(text)); + menuRadioItem->setCheckable(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +bool MenuRadioItem::visible() { + return menuRadioItem->isVisible(); +} + +void MenuRadioItem::setVisible(bool visible) { + menuRadioItem->setVisible(visible); +} + +bool MenuRadioItem::enabled() { + return menuRadioItem->isEnabled(); +} + +void MenuRadioItem::setEnabled(bool enabled) { + menuRadioItem->setEnabled(enabled); +} + +bool MenuRadioItem::checked() { + return menuRadioItem->isChecked(); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + menuRadioItem->setChecked(true); + object->locked = false; +} + +MenuRadioItem::MenuRadioItem() { + menuRadioItem = new MenuRadioItem::Data(*this); +} diff --git a/snespurify/phoenix/qt/messagewindow.cpp b/snespurify/phoenix/qt/messagewindow.cpp new file mode 100755 index 00000000..5d60ea01 --- /dev/null +++ b/snespurify/phoenix/qt/messagewindow.cpp @@ -0,0 +1,45 @@ +static QMessageBox::StandardButtons MessageWindow_buttons(MessageWindow::Buttons buttons) { + QMessageBox::StandardButtons standardButtons = QMessageBox::NoButton; + if(buttons == MessageWindow::Buttons::Ok) standardButtons = QMessageBox::Ok; + if(buttons == MessageWindow::Buttons::OkCancel) standardButtons = QMessageBox::Ok | QMessageBox::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) standardButtons = QMessageBox::Yes | QMessageBox::No; + return standardButtons; +} + +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, QMessageBox::StandardButton response) { + if(response == QMessageBox::Ok) return MessageWindow::Response::Ok; + if(response == QMessageBox::Cancel) return MessageWindow::Response::Cancel; + if(response == QMessageBox::Yes) return MessageWindow::Response::Yes; + if(response == QMessageBox::No) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::information(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::question(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::warning(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::critical(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} diff --git a/snespurify/phoenix/qt/object.cpp b/snespurify/phoenix/qt/object.cpp new file mode 100755 index 00000000..ae6cc6d9 --- /dev/null +++ b/snespurify/phoenix/qt/object.cpp @@ -0,0 +1,7 @@ +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + object = new Object::Data(*this); +} diff --git a/snespurify/phoenix/qt/progressbar.cpp b/snespurify/phoenix/qt/progressbar.cpp new file mode 100755 index 00000000..ab21f882 --- /dev/null +++ b/snespurify/phoenix/qt/progressbar.cpp @@ -0,0 +1,16 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + progressBar->setParent(parent.window->container); + progressBar->setGeometry(x, y, width, height); + progressBar->setRange(0, 100); + progressBar->setTextVisible(false); + progressBar->show(); +} + +void ProgressBar::setPosition(unsigned position) { + progressBar->setValue(position); +} + +ProgressBar::ProgressBar() { + progressBar = new ProgressBar::Data(*this); + widget->widget = progressBar; +} diff --git a/snespurify/phoenix/qt/qt.cpp b/snespurify/phoenix/qt/qt.cpp new file mode 100755 index 00000000..203b4b6f --- /dev/null +++ b/snespurify/phoenix/qt/qt.cpp @@ -0,0 +1,128 @@ +#include +#include +using namespace nall; + +namespace phoenix { + +#include "qt.moc.hpp" +#include "qt.moc" + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS::Data *OS::os = 0; +Window Window::None; + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + os = new OS::Data; + static int argc = 1; + static char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + os->application = new QApplication(argc, argvp); +} + +bool OS::pending() { + return QApplication::hasPendingEvents(); +} + +void OS::run() { + QApplication::processEvents(); +} + +void OS::main() { + QApplication::exec(); +} + +void OS::quit() { + QApplication::quit(); +} + +unsigned OS::desktopWidth() { + return QApplication::desktop()->screenGeometry().width(); +} + +unsigned OS::desktopHeight() { + return QApplication::desktop()->screenGeometry().height(); +} + +string OS::folderSelect(Window &parent, const string &path) { + QString directory = QFileDialog::getExistingDirectory( + &parent != &Window::None ? parent.window : 0, "Select Directory", + QString::fromUtf8(path), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks + ); + string name = directory.toUtf8().constData(); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getOpenFileName( + &parent != &Window::None ? parent.window : 0, "Open File", + QString::fromUtf8(path), QString::fromUtf8(filters) + ); + return filename.toUtf8().constData(); +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getSaveFileName( + &parent != &Window::None ? parent.window : 0, "Save File", + QString::fromUtf8(path), QString::fromUtf8(filters) + ); + return filename.toUtf8().constData(); +} + +} diff --git a/snespurify/phoenix/qt/qt.hpp b/snespurify/phoenix/qt/qt.hpp new file mode 100755 index 00000000..3740e4c6 --- /dev/null +++ b/snespurify/phoenix/qt/qt.hpp @@ -0,0 +1,337 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool visible() = 0; + virtual void setVisible(bool visible = true) = 0; + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + Menu(); +//private: + struct Data; + Data *menu; +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuSeparator(); +//private: + struct Data; + Data *menuSeparator; +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuItem(); +//private: + struct Data; + Data *menuItem; +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); + MenuCheckItem(); +//private: + struct Data; + Data *menuCheckItem; +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); + MenuRadioItem(); +//private: + struct Data; + Data *menuRadioItem; +}; + +struct Widget : Object { + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + virtual bool focused(); + virtual void setFocused(); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setDefaultFont(Font &font); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + bool focused(); + Window(); +//private: + struct Data; + Data *window; + static Window None; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + Button(); +//private: + struct Data; + Data *button; +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); + CheckBox(); +//private: + struct Data; + Data *checkBox; +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned row); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const nall::string &text); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); + Label(); +//private: + struct Data; + Data *label; +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setPosition(unsigned position); + ProgressBar(); +//private: + struct Data; + Data *progressBar; +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const nall::string &text); + TextBox(); +//private: + struct Data; + Data *textBox; +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); + Viewport(); +//private: + struct Data; + Data *viewport; +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + struct Data; + static Data *os; + static void initialize(); +}; + +} diff --git a/snespurify/phoenix/qt/qt.moc b/snespurify/phoenix/qt/qt.moc new file mode 100755 index 00000000..5bb99a3b --- /dev/null +++ b/snespurify/phoenix/qt/qt.moc @@ -0,0 +1,930 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'qt.moc.hpp' +** +** Created: Mon Oct 11 13:03:04 2010 +** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qt.moc.hpp' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 62 +#error "This file was generated using the moc from 4.6.2. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_MenuItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuItem__Data[] = { + "MenuItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuItem__Data, + qt_meta_data_MenuItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_MenuCheckItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 21, 20, 20, 20, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuCheckItem__Data[] = { + "MenuCheckItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuCheckItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuCheckItem__Data, + qt_meta_data_MenuCheckItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuCheckItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuCheckItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuCheckItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuCheckItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuCheckItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_MenuRadioItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 21, 20, 20, 20, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuRadioItem__Data[] = { + "MenuRadioItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuRadioItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuRadioItem__Data, + qt_meta_data_MenuRadioItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuRadioItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuRadioItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuRadioItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuRadioItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuRadioItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Window__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Window__Data[] = { + "Window::Data\0" +}; + +const QMetaObject Window::Data::staticMetaObject = { + { &QWidget::staticMetaObject, qt_meta_stringdata_Window__Data, + qt_meta_data_Window__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Window::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Window::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Window::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Window__Data)) + return static_cast(const_cast< Data*>(this)); + return QWidget::qt_metacast(_clname); +} + +int Window::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_Button__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 14, 13, 13, 13, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Button__Data[] = { + "Button::Data\0\0onTick()\0" +}; + +const QMetaObject Button::Data::staticMetaObject = { + { &QPushButton::staticMetaObject, qt_meta_stringdata_Button__Data, + qt_meta_data_Button__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Button::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Button::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Button::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Button__Data)) + return static_cast(const_cast< Data*>(this)); + return QPushButton::qt_metacast(_clname); +} + +int Button::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QPushButton::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Canvas__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Canvas__Data[] = { + "Canvas::Data\0" +}; + +const QMetaObject Canvas::Data::staticMetaObject = { + { &QWidget::staticMetaObject, qt_meta_stringdata_Canvas__Data, + qt_meta_data_Canvas__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Canvas::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Canvas::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Canvas::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Canvas__Data)) + return static_cast(const_cast< Data*>(this)); + return QWidget::qt_metacast(_clname); +} + +int Canvas::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_CheckBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_CheckBox__Data[] = { + "CheckBox::Data\0\0onTick()\0" +}; + +const QMetaObject CheckBox::Data::staticMetaObject = { + { &QCheckBox::staticMetaObject, qt_meta_stringdata_CheckBox__Data, + qt_meta_data_CheckBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &CheckBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *CheckBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *CheckBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_CheckBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QCheckBox::qt_metacast(_clname); +} + +int CheckBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QCheckBox::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_ComboBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_ComboBox__Data[] = { + "ComboBox::Data\0\0onChange()\0" +}; + +const QMetaObject ComboBox::Data::staticMetaObject = { + { &QComboBox::staticMetaObject, qt_meta_stringdata_ComboBox__Data, + qt_meta_data_ComboBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &ComboBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *ComboBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *ComboBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_ComboBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QComboBox::qt_metacast(_clname); +} + +int ComboBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QComboBox::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_EditBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_EditBox__Data[] = { + "EditBox::Data\0\0onChange()\0" +}; + +const QMetaObject EditBox::Data::staticMetaObject = { + { &QTextEdit::staticMetaObject, qt_meta_stringdata_EditBox__Data, + qt_meta_data_EditBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &EditBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *EditBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *EditBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_EditBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QTextEdit::qt_metacast(_clname); +} + +int EditBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QTextEdit::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_HorizontalSlider__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 24, 23, 23, 23, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_HorizontalSlider__Data[] = { + "HorizontalSlider::Data\0\0onChange()\0" +}; + +const QMetaObject HorizontalSlider::Data::staticMetaObject = { + { &QSlider::staticMetaObject, qt_meta_stringdata_HorizontalSlider__Data, + qt_meta_data_HorizontalSlider__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &HorizontalSlider::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *HorizontalSlider::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *HorizontalSlider::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_HorizontalSlider__Data)) + return static_cast(const_cast< Data*>(this)); + return QSlider::qt_metacast(_clname); +} + +int HorizontalSlider::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QSlider::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Label__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Label__Data[] = { + "Label::Data\0" +}; + +const QMetaObject Label::Data::staticMetaObject = { + { &QLabel::staticMetaObject, qt_meta_stringdata_Label__Data, + qt_meta_data_Label__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Label::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Label::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Label::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Label__Data)) + return static_cast(const_cast< Data*>(this)); + return QLabel::qt_metacast(_clname); +} + +int Label::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QLabel::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_ListBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 3, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x0a, + 28, 14, 14, 14, 0x0a, + 44, 39, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_ListBox__Data[] = { + "ListBox::Data\0\0onActivate()\0onChange()\0" + "item\0onTick(QTreeWidgetItem*)\0" +}; + +const QMetaObject ListBox::Data::staticMetaObject = { + { &QTreeWidget::staticMetaObject, qt_meta_stringdata_ListBox__Data, + qt_meta_data_ListBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &ListBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *ListBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *ListBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_ListBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QTreeWidget::qt_metacast(_clname); +} + +int ListBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QTreeWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onActivate(); break; + case 1: onChange(); break; + case 2: onTick((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break; + default: ; + } + _id -= 3; + } + return _id; +} +static const uint qt_meta_data_RadioBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_RadioBox__Data[] = { + "RadioBox::Data\0\0onTick()\0" +}; + +const QMetaObject RadioBox::Data::staticMetaObject = { + { &QRadioButton::staticMetaObject, qt_meta_stringdata_RadioBox__Data, + qt_meta_data_RadioBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &RadioBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *RadioBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *RadioBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_RadioBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QRadioButton::qt_metacast(_clname); +} + +int RadioBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QRadioButton::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_TextBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x0a, + 28, 14, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_TextBox__Data[] = { + "TextBox::Data\0\0onActivate()\0onChange()\0" +}; + +const QMetaObject TextBox::Data::staticMetaObject = { + { &QLineEdit::staticMetaObject, qt_meta_stringdata_TextBox__Data, + qt_meta_data_TextBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &TextBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *TextBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *TextBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_TextBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QLineEdit::qt_metacast(_clname); +} + +int TextBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QLineEdit::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onActivate(); break; + case 1: onChange(); break; + default: ; + } + _id -= 2; + } + return _id; +} +static const uint qt_meta_data_VerticalSlider__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 22, 21, 21, 21, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_VerticalSlider__Data[] = { + "VerticalSlider::Data\0\0onChange()\0" +}; + +const QMetaObject VerticalSlider::Data::staticMetaObject = { + { &QSlider::staticMetaObject, qt_meta_stringdata_VerticalSlider__Data, + qt_meta_data_VerticalSlider__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &VerticalSlider::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *VerticalSlider::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *VerticalSlider::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_VerticalSlider__Data)) + return static_cast(const_cast< Data*>(this)); + return QSlider::qt_metacast(_clname); +} + +int VerticalSlider::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QSlider::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_OS__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_OS__Data[] = { + "OS::Data\0" +}; + +const QMetaObject OS::Data::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_OS__Data, + qt_meta_data_OS__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &OS::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *OS::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *OS::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_OS__Data)) + return static_cast(const_cast< Data*>(this)); + return QObject::qt_metacast(_clname); +} + +int OS::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/snespurify/phoenix/qt/qt.moc.hpp b/snespurify/phoenix/qt/qt.moc.hpp new file mode 100755 index 00000000..8beaed5e --- /dev/null +++ b/snespurify/phoenix/qt/qt.moc.hpp @@ -0,0 +1,311 @@ +struct Object::Data { +public: + Object &self; + bool locked; + + Data(Object &self) : self(self) { + locked = false; + } +}; + +struct Font::Data : public QFont { +public: + Font &self; + + Data(Font &self) : self(self) { + } +}; + +struct Menu::Data : public QMenu { +public: + Menu &self; + Window *parent; + + Data(Menu &self) : self(self), parent(0) { + } +}; + +struct MenuSeparator::Data { +public: + MenuSeparator &self; + QAction *action; + + Data(MenuSeparator &self) : self(self) { + } +}; + +struct MenuItem::Data : public QAction { + Q_OBJECT + +public: + MenuItem &self; + + Data(MenuItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuCheckItem::Data : public QAction { + Q_OBJECT + +public: + MenuCheckItem &self; + + Data(MenuCheckItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuRadioItem::Data : public QAction { + Q_OBJECT + +public: + MenuRadioItem &self; + Menu *parent; + QActionGroup *actionGroup; + + Data(MenuRadioItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.object->locked == false && self.onTick && self.checked()) self.onTick(); + } +}; + +struct Widget::Data { +public: + Widget &self; + QWidget *widget; + + Data(Widget &self) : self(self) { + } +}; + +struct Window::Data : public QWidget { + Q_OBJECT + +public: + Window &self; + QFont *defaultFont; + QVBoxLayout *layout; + QMenuBar *menuBar; + QWidget *container; + QStatusBar *statusBar; + + void closeEvent(QCloseEvent *event) { + if(self.onClose) { + bool result = self.onClose(); + if(result == false) event->ignore(); + } + } + + Data(Window &self) : self(self) { + } +}; + +struct Button::Data : public QPushButton { + Q_OBJECT + +public: + Button &self; + + Data(Button &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct Canvas::Data : public QWidget { + Q_OBJECT + +public: + Canvas &self; + QImage *image; + void paintEvent(QPaintEvent*); + + Data(Canvas &self) : self(self) { + } +}; + +struct CheckBox::Data : public QCheckBox { + Q_OBJECT + +public: + CheckBox &self; + + Data(CheckBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct ComboBox::Data : public QComboBox { + Q_OBJECT + +public: + ComboBox &self; + + Data(ComboBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.object->locked == false && self.onChange) self.onChange(); + } +}; + +struct EditBox::Data : public QTextEdit { + Q_OBJECT + +public: + EditBox &self; + + Data(EditBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct HorizontalSlider::Data : public QSlider { + Q_OBJECT + +public: + HorizontalSlider &self; + + Data(HorizontalSlider &self) : self(self), QSlider(Qt::Horizontal) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Label::Data : public QLabel { + Q_OBJECT + +public: + Label &self; + + Data(Label &self) : self(self) { + } +}; + +struct ListBox::Data : public QTreeWidget { + Q_OBJECT + +public: + ListBox &self; + bool checkable; + + Data(ListBox &self) : self(self) { + checkable = false; + } + +public slots: + void onActivate() { + if(self.object->locked == false && self.onActivate) self.onActivate(); + } + + void onChange() { + if(self.object->locked == false && self.onChange) self.onChange(); + } + + void onTick(QTreeWidgetItem *item) { + if(self.object->locked == false && self.onTick) self.onTick(item->data(0, Qt::UserRole).toUInt()); + } +}; + +struct ProgressBar::Data : public QProgressBar { +public: + ProgressBar &self; + + Data(ProgressBar &self) : self(self) { + } +}; + +struct RadioBox::Data : public QRadioButton { + Q_OBJECT + +public: + RadioBox &self; + Window *parent; + QButtonGroup *buttonGroup; + + Data(RadioBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick && self.checked()) self.onTick(); + } +}; + +struct TextBox::Data : public QLineEdit { + Q_OBJECT + +public: + TextBox &self; + + Data(TextBox &self) : self(self) { + } + +public slots: + void onActivate() { + if(self.onActivate) self.onActivate(); + } + + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct VerticalSlider::Data : public QSlider { + Q_OBJECT + +public: + VerticalSlider &self; + + Data(VerticalSlider &self) : self(self), QSlider(Qt::Vertical) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Viewport::Data : public QWidget { +public: + Viewport &self; + + Data(Viewport &self) : self(self) { + } +}; + +struct OS::Data : public QObject { + Q_OBJECT + +public: + QApplication *application; + +public slots: +}; diff --git a/snespurify/phoenix/qt/radiobox.cpp b/snespurify/phoenix/qt/radiobox.cpp new file mode 100755 index 00000000..b6f3a846 --- /dev/null +++ b/snespurify/phoenix/qt/radiobox.cpp @@ -0,0 +1,37 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parent = &parent; + radioBox->buttonGroup = new QButtonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(QString::fromUtf8(text)); + radioBox->setChecked(true); + if(parent.window->defaultFont) radioBox->setFont(*parent.window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parent = parent.radioBox->parent; + radioBox->buttonGroup = parent.radioBox->buttonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(QString::fromUtf8(text)); + if(radioBox->parent->window->defaultFont) radioBox->setFont(*radioBox->parent->window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +bool RadioBox::checked() { + return radioBox->isChecked(); +} + +void RadioBox::setChecked() { + radioBox->setChecked(true); +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data(*this); + widget->widget = radioBox; +} diff --git a/snespurify/phoenix/qt/textbox.cpp b/snespurify/phoenix/qt/textbox.cpp new file mode 100755 index 00000000..ffe2c82f --- /dev/null +++ b/snespurify/phoenix/qt/textbox.cpp @@ -0,0 +1,26 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + textBox->setParent(parent.window->container); + textBox->setGeometry(x, y, width, height); + textBox->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) textBox->setFont(*parent.window->defaultFont); + textBox->show(); + textBox->connect(textBox, SIGNAL(returnPressed()), SLOT(onActivate())); + textBox->connect(textBox, SIGNAL(textEdited(const QString&)), SLOT(onChange())); +} + +void TextBox::setEditable(bool editable) { + textBox->setReadOnly(editable == false); +} + +string TextBox::text() { + return textBox->text().toUtf8().constData(); +} + +void TextBox::setText(const string &text) { + textBox->setText(QString::fromUtf8(text)); +} + +TextBox::TextBox() { + textBox = new TextBox::Data(*this); + widget->widget = textBox; +} diff --git a/snespurify/phoenix/qt/verticalslider.cpp b/snespurify/phoenix/qt/verticalslider.cpp new file mode 100755 index 00000000..98d9f22a --- /dev/null +++ b/snespurify/phoenix/qt/verticalslider.cpp @@ -0,0 +1,24 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + verticalSlider->setParent(parent.window->container); + verticalSlider->setGeometry(x, y, width, height); + verticalSlider->setInvertedAppearance(true); + verticalSlider->setInvertedControls(true); + verticalSlider->setRange(0, length - 1); + verticalSlider->setPageStep(length >> 3); + verticalSlider->connect(verticalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + verticalSlider->show(); +} + +unsigned VerticalSlider::position() { + return verticalSlider->value(); +} + +void VerticalSlider::setPosition(unsigned position) { + verticalSlider->setValue(position); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data(*this); + widget->widget = verticalSlider; +} diff --git a/snespurify/phoenix/qt/viewport.cpp b/snespurify/phoenix/qt/viewport.cpp new file mode 100755 index 00000000..7c4b0f19 --- /dev/null +++ b/snespurify/phoenix/qt/viewport.cpp @@ -0,0 +1,16 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + viewport->setParent(parent.window->container); + viewport->setGeometry(x, y, width, height); + viewport->setAttribute(Qt::WA_PaintOnScreen, true); + viewport->setStyleSheet("background: #000000"); + viewport->show(); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)viewport->winId(); +} + +Viewport::Viewport() { + viewport = new Viewport::Data(*this); + widget->widget = viewport; +} diff --git a/snespurify/phoenix/qt/widget.cpp b/snespurify/phoenix/qt/widget.cpp new file mode 100755 index 00000000..9510607a --- /dev/null +++ b/snespurify/phoenix/qt/widget.cpp @@ -0,0 +1,35 @@ +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + widget->widget->setGeometry(x, y, width, height); +} + +void Widget::setFont(Font &font) { + widget->widget->setFont(*font.font); +} + +bool Widget::visible() { + return widget->widget->isVisible(); +} + +void Widget::setVisible(bool visible) { + widget->widget->setVisible(visible); +} + +bool Widget::enabled() { + return widget->widget->isEnabled(); +} + +void Widget::setEnabled(bool enabled) { + widget->widget->setEnabled(enabled); +} + +bool Widget::focused() { + return widget->widget->hasFocus(); +} + +void Widget::setFocused() { + widget->widget->setFocus(Qt::OtherFocusReason); +} + +Widget::Widget() { + widget = new Widget::Data(*this); +} diff --git a/snespurify/phoenix/qt/window.cpp b/snespurify/phoenix/qt/window.cpp new file mode 100755 index 00000000..ef209ab0 --- /dev/null +++ b/snespurify/phoenix/qt/window.cpp @@ -0,0 +1,77 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + window->setWindowTitle(QString::fromUtf8(text)); + window->move(x, y); + + window->layout = new QVBoxLayout(window); + window->layout->setMargin(0); + window->layout->setSpacing(0); + window->layout->setSizeConstraint(QLayout::SetFixedSize); + window->setLayout(window->layout); + + window->menuBar = new QMenuBar(window); + window->menuBar->setVisible(false); + window->layout->addWidget(window->menuBar); + + window->container = new QWidget(window); + window->container->setFixedSize(width, height); + window->container->setVisible(true); + window->layout->addWidget(window->container); + + window->statusBar = new QStatusBar(window); + window->statusBar->setSizeGripEnabled(false); + window->statusBar->setVisible(false); + window->layout->addWidget(window->statusBar); +} + +Geometry Window::geometry() { + return Geometry(window->x(), window->y(), window->container->width(), window->container->height()); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + window->container->setFixedSize(width, height); + window->move(x, y); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = font.font; + window->menuBar->setFont(*font.font); +} + +void Window::setFont(Font &font) { + window->statusBar->setFont(*font.font); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + QPalette palette; + palette.setColor(QPalette::Window, QColor(red, green, blue)); + window->setPalette(palette); + window->setAutoFillBackground(true); +} + +void Window::setTitle(const string &text) { + window->setWindowTitle(QString::fromUtf8(text)); +} + +void Window::setStatusText(const string &text) { + window->statusBar->showMessage(QString::fromUtf8(text), 0); +} + +void Window::setMenuVisible(bool visible) { + if(visible) window->menuBar->show(); + else window->menuBar->hide(); +} + +void Window::setStatusVisible(bool visible) { + if(visible) window->statusBar->show(); + else window->statusBar->hide(); +} + +bool Window::focused() { + return window->isActiveWindow() && !window->isMinimized(); +} + +Window::Window() { + window = new Window::Data(*this); + window->defaultFont = 0; + widget->widget = window; +} diff --git a/snespurify/phoenix/sync.sh b/snespurify/phoenix/sync.sh new file mode 100755 index 00000000..b51e3b40 --- /dev/null +++ b/snespurify/phoenix/sync.sh @@ -0,0 +1,8 @@ +synchronize() { + if [ -d ../"$1" ]; then + test -d "$1" && rm -r "$1" + cp -r ../"$1" ./"$1" + fi +} + +synchronize "nall" diff --git a/snespurify/phoenix/windows/button.cpp b/snespurify/phoenix/windows/button.cpp new file mode 100755 index 00000000..267b446e --- /dev/null +++ b/snespurify/phoenix/windows/button.cpp @@ -0,0 +1,10 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} diff --git a/snespurify/phoenix/windows/canvas.cpp b/snespurify/phoenix/windows/canvas.cpp new file mode 100755 index 00000000..783d7042 --- /dev/null +++ b/snespurify/phoenix/windows/canvas.cpp @@ -0,0 +1,60 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->buffer = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + canvas->width = width; + canvas->height = height; + memset(&canvas->bmi, 0, sizeof(BITMAPINFO)); + canvas->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + canvas->bmi.bmiHeader.biPlanes = 1; + canvas->bmi.bmiHeader.biBitCount = 32; + canvas->bmi.bmiHeader.biCompression = BI_RGB; + canvas->bmi.bmiHeader.biWidth = width; + canvas->bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside down; negative height flips bitmap + canvas->bmi.bmiHeader.biSizeImage = canvas->pitch * canvas->height; + + widget->window = CreateWindow( + L"phoenix_canvas", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uint32_t* Canvas::buffer() { + return canvas->buffer; +} + +void Canvas::redraw() { + PAINTSTRUCT ps; + BeginPaint(widget->window, &ps); + SetDIBitsToDevice(ps.hdc, 0, 0, canvas->width, canvas->height, 0, 0, 0, canvas->height, (void*)canvas->buffer, &canvas->bmi, DIB_RGB_COLORS); + EndPaint(widget->window, &ps); + InvalidateRect(widget->window, 0, false); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->buffer = 0; +} + +Canvas::~Canvas() { + delete[] canvas->buffer; + delete canvas; +} + +static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_PAINT: { + Object *object_ptr = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Canvas &canvas = (Canvas&)*object_ptr; + canvas.redraw(); + } + } + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/checkbox.cpp b/snespurify/phoenix/windows/checkbox.cpp new file mode 100755 index 00000000..035deb56 --- /dev/null +++ b/snespurify/phoenix/windows/checkbox.cpp @@ -0,0 +1,18 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +bool CheckBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void CheckBox::setChecked(bool checked) { + SendMessage(widget->window, BM_SETCHECK, (WPARAM)checked, 0); +} diff --git a/snespurify/phoenix/windows/combobox.cpp b/snespurify/phoenix/windows/combobox.cpp new file mode 100755 index 00000000..4965a529 --- /dev/null +++ b/snespurify/phoenix/windows/combobox.cpp @@ -0,0 +1,46 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + 0, L"COMBOBOX", L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, + x, y, width, 200, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + + //CreateWindow height parameter is the height of the expanded list box; + //need additional code to override default ComboBox control height + RECT rc; + GetWindowRect(widget->window, &rc); + unsigned adjustedHeight = height - ((rc.bottom - rc.top) - SendMessage(widget->window, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(widget->window, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } +} + +void ComboBox::reset() { + SendMessage(widget->window, CB_RESETCONTENT, 0, 0); +} + +void ComboBox::addItem(const string &text) { + SendMessage(widget->window, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(text)); + if(SendMessage(widget->window, CB_GETCOUNT, 0, 0) == 1) setSelection(0); +} + +unsigned ComboBox::selection() { + return SendMessage(widget->window, CB_GETCURSEL, 0, 0); +} + +void ComboBox::setSelection(unsigned row) { + SendMessage(widget->window, CB_SETCURSEL, comboBox->selection = row, 0); +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data; + comboBox->selection = 0; +} diff --git a/snespurify/phoenix/windows/editbox.cpp b/snespurify/phoenix/windows/editbox.cpp new file mode 100755 index 00000000..2d165b3a --- /dev/null +++ b/snespurify/phoenix/windows/editbox.cpp @@ -0,0 +1,53 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | + (editBox->wordWrap == false ? ES_AUTOHSCROLL : 0), + editBox->x = x, editBox->y = y, editBox->width = width, editBox->height = height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + setText(text); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +string EditBox::getText() { + unsigned length = GetWindowTextLength(widget->window); + wchar_t buffer[length + 1]; + GetWindowText(widget->window, buffer, length + 1); + buffer[length] = 0; + string text = utf8_t(buffer); + text.replace("\r", ""); + return text; +} + +void EditBox::setText(const string &text) { + string output = text; + output.replace("\r", ""); + output.replace("\n", "\r\n"); + object->locked = true; + SetWindowText(widget->window, utf16_t(output)); + object->locked = false; +} + +void EditBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, (LPARAM)0); +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->wordWrap = wordWrap; + if(widget->window == 0) return; + + //ES_AUTOSCROLL options cannot be changed after control has been created; + //so destroy the control and recreate it with desired options + HWND hparent = GetParent(widget->window); + Window *parent = (Window*)GetWindowLongPtr(hparent, GWLP_USERDATA); + string text = getText(); + DestroyWindow(widget->window); + create(*parent, editBox->x, editBox->y, editBox->width, editBox->height, text); +} + +EditBox::EditBox() { + editBox = new EditBox::Data; + editBox->wordWrap = true; +} diff --git a/snespurify/phoenix/windows/font.cpp b/snespurify/phoenix/windows/font.cpp new file mode 100755 index 00000000..712b928f --- /dev/null +++ b/snespurify/phoenix/windows/font.cpp @@ -0,0 +1,26 @@ +static HFONT Font_createFont(const string &name, unsigned size, bool bold, bool italic) { + return CreateFont( + -(size * 96.0 / 72.0 + 0.5), + 0, 0, 0, bold == false ? FW_NORMAL : FW_BOLD, italic, 0, 0, 0, 0, 0, 0, 0, + utf16_t(name) + ); +} + +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->font = Font_createFont( + name, size, + (style & Font::Style::Bold) == Font::Style::Bold, + (style & Font::Style::Italic) == Font::Style::Italic + ); + return font->font; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) DeleteObject(font->font); + delete font; +} diff --git a/snespurify/phoenix/windows/horizontalslider.cpp b/snespurify/phoenix/windows/horizontalslider.cpp new file mode 100755 index 00000000..9f5939bd --- /dev/null +++ b/snespurify/phoenix/windows/horizontalslider.cpp @@ -0,0 +1,25 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned HorizontalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void HorizontalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(horizontalSlider->position = position)); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data; +} diff --git a/snespurify/phoenix/windows/label.cpp b/snespurify/phoenix/windows/label.cpp new file mode 100755 index 00000000..e20c7cf0 --- /dev/null +++ b/snespurify/phoenix/windows/label.cpp @@ -0,0 +1,51 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"phoenix_label", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + setText(text); +} + +void Label::setText(const string &text) { + SetWindowText(widget->window, utf16_t(text)); + InvalidateRect(widget->window, 0, false); +} + +//all of this for want of a STATIC SS_VCENTER flag ... +LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Window *window_ptr = (Window*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if(!window_ptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Label *label_ptr = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(!label_ptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window &window = *window_ptr; + Label &label = *label_ptr; + + switch(msg) { + case WM_PAINT: { + PAINTSTRUCT ps; + RECT rc; + BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &rc); + FillRect(ps.hdc, &rc, window.window->brush ? window.window->brush : GetSysColorBrush(COLOR_3DFACE)); + SetBkColor(ps.hdc, window.window->brush ? window.window->brushColor : GetSysColor(COLOR_3DFACE)); + SelectObject(ps.hdc, label.widget->font); + unsigned length = GetWindowTextLength(hwnd); + wchar_t text[length + 1]; + GetWindowText(hwnd, text, length + 1); + text[length] = 0; + DrawText(ps.hdc, text, -1, &rc, DT_CALCRECT | DT_END_ELLIPSIS); + unsigned height = rc.bottom; + GetClientRect(hwnd, &rc); + rc.top = (rc.bottom - height) / 2; + rc.bottom = rc.top + height; + DrawText(ps.hdc, text, -1, &rc, DT_LEFT | DT_END_ELLIPSIS); + EndPaint(hwnd, &ps); + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/listbox.cpp b/snespurify/phoenix/windows/listbox.cpp new file mode 100755 index 00000000..86d82255 --- /dev/null +++ b/snespurify/phoenix/windows/listbox.cpp @@ -0,0 +1,114 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT); + + lstring list; + list.split("\t", text); + listBox->columns = list.size(); + for(unsigned i = 0; i < list.size(); i++) { + LVCOLUMN column; + column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM; + column.fmt = LVCFMT_LEFT; + column.iSubItem = list.size(); + utf16_t text(list[i]); + column.pszText = text; + ListView_InsertColumn(widget->window, i, &column); + } + resizeColumnsToContent(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + SetWindowLong( + widget->window, + GWL_STYLE, + (GetWindowLong(widget->window, GWL_STYLE) & ~LVS_NOCOLUMNHEADER) | + (headerVisible == false ? LVS_NOCOLUMNHEADER : 0) + ); +} + +void ListBox::setCheckable(bool checkable) { + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT | (checkable ? LVS_EX_CHECKBOXES : 0)); +} + +void ListBox::reset() { + ListView_DeleteAllItems(widget->window); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columns; i++) { + ListView_SetColumnWidth(widget->window, i, LVSCW_AUTOSIZE_USEHEADER); + } +} + +void ListBox::addItem(const string &text) { + lstring list; + list.split("\t", text); + LVITEM item; + unsigned row = ListView_GetItemCount(widget->window); + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = 0; + utf16_t wtext(list[0]); + item.pszText = wtext; + object->locked = true; + ListView_InsertItem(widget->window, &item); + object->locked = false; + for(unsigned i = 1; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); +} + +void ListBox::setItem(unsigned row, const string &text) { + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); +} + +optional ListBox::selection() { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + if(ListView_GetItemState(widget->window, i, LVIS_SELECTED)) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + ListView_SetItemState(widget->window, i, LVIS_FOCUSED, (i == row ? LVIS_FOCUSED : 0)); + ListView_SetItemState(widget->window, i, LVIS_SELECTED, (i == row ? LVIS_SELECTED : 0)); + } +} + +bool ListBox::checked(unsigned row) { + return ListView_GetCheckState(widget->window, row); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + ListView_SetCheckState(widget->window, row, checked); + object->locked = false; +} + +ListBox::ListBox() { + listBox = new ListBox::Data; + listBox->lostFocus = false; +} diff --git a/snespurify/phoenix/windows/menu.cpp b/snespurify/phoenix/windows/menu.cpp new file mode 100755 index 00000000..789ed382 --- /dev/null +++ b/snespurify/phoenix/windows/menu.cpp @@ -0,0 +1,144 @@ +Action::Action() { + OS::os->objects.append(this); + action = new Action::Data; +} + +void Menu::create(Window &parent, const string &text) { + action->parentMenu = parent.window->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.window->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +void Menu::create(Menu &parent, const string &text) { + action->parentMenu = parent.action->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.action->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +bool Menu::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parentMenu, (UINT_PTR)action->menu, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void Menu::setEnabled(bool enabled) { + EnableMenuItem(action->parentMenu, (UINT_PTR)action->menu, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuSeparator::create(Menu &parent) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_SEPARATOR, object->id, L""); +} + +bool MenuSeparator::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuSeparator::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuItem::create(Menu &parent, const string &text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuCheckItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuCheckItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuCheckItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuCheckItem::setChecked(bool checked) { + CheckMenuItem(action->parent->action->menu, object->id, checked ? MF_CHECKED : MF_UNCHECKED); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + action->parent = &parent; + action->radioParent = this; + action->items.append(this); + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); + setChecked(); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + action->parent = parent.action->parent; + action->radioParent = parent.action->radioParent; + action->radioParent->action->items.append(this); + AppendMenu(action->parent->action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuRadioItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuRadioItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuRadioItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuRadioItem::setChecked() { + MenuRadioItem *parent = action->radioParent; + foreach(item, parent->action->items) { + CheckMenuRadioItem( + action->parent->action->menu, + item->object->id, item->object->id, item->object->id + (item != this), + MF_BYCOMMAND + ); + } +} diff --git a/snespurify/phoenix/windows/messagewindow.cpp b/snespurify/phoenix/windows/messagewindow.cpp new file mode 100755 index 00000000..b2439dc3 --- /dev/null +++ b/snespurify/phoenix/windows/messagewindow.cpp @@ -0,0 +1,41 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, UINT response) { + if(response == IDOK) return MessageWindow::Response::Ok; + if(response == IDCANCEL) return MessageWindow::Response::Cancel; + if(response == IDYES) return MessageWindow::Response::Yes; + if(response == IDNO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONINFORMATION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONQUESTION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONWARNING; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONERROR; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} diff --git a/snespurify/phoenix/windows/object.cpp b/snespurify/phoenix/windows/object.cpp new file mode 100755 index 00000000..2a5b8785 --- /dev/null +++ b/snespurify/phoenix/windows/object.cpp @@ -0,0 +1,87 @@ +struct Object::Data { + unsigned id; + bool locked; +}; + +struct Font::Data { + HFONT font; +}; + +struct Action::Data { + Menu *parent; + HMENU parentMenu; + HMENU menu; + MenuRadioItem *radioParent; + array items; +}; + +struct Widget::Data { + HWND window; + HFONT font; +}; + +struct Window::Data { + HFONT defaultFont; + HBRUSH brush; + COLORREF brushColor; + HMENU menu; + HWND status; + unsigned width; + unsigned height; +}; + +struct Canvas::Data { + uint32_t *buffer; + BITMAPINFO bmi; + unsigned pitch; + unsigned width; + unsigned height; +}; + +struct ComboBox::Data { + unsigned selection; +}; + +struct EditBox::Data { + bool wordWrap; + unsigned x; + unsigned y; + unsigned width; + unsigned height; +}; + +struct HorizontalSlider::Data { + unsigned position; +}; + +struct ListBox::Data { + unsigned columns; + bool lostFocus; +}; + +struct RadioBox::Data { + Window *parentWindow; + RadioBox *parent; + array items; +}; + +struct VerticalSlider::Data { + unsigned position; +}; + +struct OS::Data { + nall::array objects; + HFONT proportionalFont; + HFONT monospaceFont; +}; + +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + static unsigned guid = 100; + object = new Object::Data; + object->id = guid++; + object->locked = false; +} diff --git a/snespurify/phoenix/windows/phoenix.Manifest b/snespurify/phoenix/windows/phoenix.Manifest new file mode 100755 index 00000000..71013ffe --- /dev/null +++ b/snespurify/phoenix/windows/phoenix.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/snespurify/phoenix/windows/phoenix.rc b/snespurify/phoenix/windows/phoenix.rc new file mode 100755 index 00000000..89fb8dc2 --- /dev/null +++ b/snespurify/phoenix/windows/phoenix.rc @@ -0,0 +1 @@ +1 24 "phoenix.Manifest" diff --git a/snespurify/phoenix/windows/progressbar.cpp b/snespurify/phoenix/windows/progressbar.cpp new file mode 100755 index 00000000..230f12b4 --- /dev/null +++ b/snespurify/phoenix/windows/progressbar.cpp @@ -0,0 +1,18 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + PROGRESS_CLASS, L"", + WS_CHILD | WS_VISIBLE | PBS_SMOOTH, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SendMessage(widget->window, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + SendMessage(widget->window, PBM_SETSTEP, MAKEWPARAM(1, 0), 0); +} + +unsigned ProgressBar::position() { + return SendMessage(widget->window, PBM_GETPOS, 0, 0); +} + +void ProgressBar::setPosition(unsigned position) { + SendMessage(widget->window, PBM_SETPOS, (WPARAM)position, 0); +} diff --git a/snespurify/phoenix/windows/radiobox.cpp b/snespurify/phoenix/windows/radiobox.cpp new file mode 100755 index 00000000..9c49297b --- /dev/null +++ b/snespurify/phoenix/windows/radiobox.cpp @@ -0,0 +1,42 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parentWindow = &parent; + radioBox->parent = this; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + setChecked(); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parentWindow = parent.radioBox->parentWindow; + radioBox->parent = parent.radioBox->parent; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + GetParent(radioBox->parent->widget->window), (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(radioBox->parentWindow->window->defaultFont ? radioBox->parentWindow->window->defaultFont : OS::os->proportionalFont), 0); +} + +bool RadioBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void RadioBox::setChecked() { + foreach(item, radioBox->parent->radioBox->items) { + SendMessage(item->widget->window, BM_SETCHECK, (WPARAM)(item == this), 0); + } +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data; +} diff --git a/snespurify/phoenix/windows/textbox.cpp b/snespurify/phoenix/windows/textbox.cpp new file mode 100755 index 00000000..e63f2dac --- /dev/null +++ b/snespurify/phoenix/windows/textbox.cpp @@ -0,0 +1,28 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +string TextBox::text() { + unsigned length = GetWindowTextLength(widget->window); + wchar_t text[length + 1]; + GetWindowText(widget->window, text, length + 1); + text[length] = 0; + return utf8_t(text); +} + +void TextBox::setText(const string &text) { + object->locked = true; + SetWindowText(widget->window, utf16_t(text)); + object->locked = false; +} + +void TextBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, 0); +} diff --git a/snespurify/phoenix/windows/verticalslider.cpp b/snespurify/phoenix/windows/verticalslider.cpp new file mode 100755 index 00000000..43024432 --- /dev/null +++ b/snespurify/phoenix/windows/verticalslider.cpp @@ -0,0 +1,25 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned VerticalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void VerticalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(verticalSlider->position = position)); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data; +} diff --git a/snespurify/phoenix/windows/viewport.cpp b/snespurify/phoenix/windows/viewport.cpp new file mode 100755 index 00000000..21e38d79 --- /dev/null +++ b/snespurify/phoenix/windows/viewport.cpp @@ -0,0 +1,17 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + L"phoenix_viewport", L"", + WS_CHILD | WS_VISIBLE | WS_DISABLED, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)widget->window; +} + +static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/widget.cpp b/snespurify/phoenix/windows/widget.cpp new file mode 100755 index 00000000..70ac5301 --- /dev/null +++ b/snespurify/phoenix/windows/widget.cpp @@ -0,0 +1,40 @@ +void Widget::setFont(Font &font) { + widget->font = font.font->font; + SendMessage(widget->window, WM_SETFONT, (WPARAM)font.font->font, 0); +} + +bool Widget::visible() { + return GetWindowLong(widget->window, GWL_STYLE) & WS_VISIBLE; +} + +void Widget::setVisible(bool visible) { + ShowWindow(widget->window, visible ? SW_SHOWNORMAL : SW_HIDE); +} + +bool Widget::enabled() { + return IsWindowEnabled(widget->window); +} + +void Widget::setEnabled(bool enabled) { + EnableWindow(widget->window, enabled); +} + +bool Widget::focused() { + return (GetForegroundWindow() == widget->window); +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + SetFocus(widget->window); +} + +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER); +} + +Widget::Widget() { + OS::os->objects.append(this); + widget = new Widget::Data; + widget->window = 0; + widget->font = OS::os->proportionalFont; +} diff --git a/snespurify/phoenix/windows/window.cpp b/snespurify/phoenix/windows/window.cpp new file mode 100755 index 00000000..5f86d547 --- /dev/null +++ b/snespurify/phoenix/windows/window.cpp @@ -0,0 +1,96 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + 0, L"phoenix_window", utf16_t(text), + WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, + x, y, width, height, + 0, 0, GetModuleHandle(0), 0 + ); + window->menu = CreateMenu(); + window->status = CreateWindowEx( + 0, STATUSCLASSNAME, L"", + WS_CHILD, + 0, 0, 0, 0, + widget->window, 0, GetModuleHandle(0), 0 + ); + //StatusBar will be capable of receiving tab focus if it is not disabled + SetWindowLongPtr(window->status, GWL_STYLE, GetWindowLong(window->status, GWL_STYLE) | WS_DISABLED); + resize(width, height); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = font.font->font; +} + +void Window::setFont(Font &font) { + SendMessage(window->status, WM_SETFONT, (WPARAM)font.font->font, 0); +} + +Geometry Window::geometry() { + RECT position, size; + GetWindowRect(widget->window, &position); + GetClientRect(widget->window, &size); + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + RECT status; + GetClientRect(window->status, &status); + size.bottom -= status.bottom - status.top; + } + return Geometry(position.left, position.top, size.right, size.bottom); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + bool isVisible = visible(); + if(isVisible) setVisible(false); + SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED); + resize(width, height); + if(isVisible) setVisible(true); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + if(window->brush) DeleteObject(window->brush); + window->brushColor = RGB(red, green, blue); + window->brush = CreateSolidBrush(window->brushColor); +} + +void Window::setTitle(const string &text) { + SetWindowText(widget->window, utf16_t(text)); +} + +void Window::setStatusText(const string &text) { + SendMessage(window->status, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text)); +} + +void Window::setMenuVisible(bool visible) { + SetMenu(widget->window, visible ? window->menu : 0); + resize(window->width, window->height); +} + +void Window::setStatusVisible(bool visible) { + ShowWindow(window->status, visible ? SW_SHOWNORMAL : SW_HIDE); + resize(window->width, window->height); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; + window->brush = 0; +} + +void Window::resize(unsigned width, unsigned height) { + window->width = width; + window->height = height; + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + RECT rc; + GetClientRect(widget->window, &rc); + width += width - (rc.right - rc.left); + height += height - (rc.bottom - rc.top); + + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + GetClientRect(window->status, &rc); + height += rc.bottom - rc.top; + } + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + SetWindowPos(window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); +} \ No newline at end of file diff --git a/snespurify/phoenix/windows/windows.cpp b/snespurify/phoenix/windows/windows.cpp new file mode 100755 index 00000000..697f8eec --- /dev/null +++ b/snespurify/phoenix/windows/windows.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS::Data *OS::os = 0; +Window Window::None; + +static void OS_keyboardProc(HWND, UINT, WPARAM, LPARAM); +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + InitCommonControls(); + CoInitialize(0); + + os = new OS::Data; + os->proportionalFont = Font_createFont("Tahoma", 8, false, false); + os->monospaceFont = Font_createFont("Courier New", 8, false, false); + + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2)); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = OS_windowProc; + wc.lpszClassName = L"phoenix_window"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Canvas_windowProc; + wc.lpszClassName = L"phoenix_canvas"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Label_windowProc; + wc.lpszClassName = L"phoenix_label"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Viewport_windowProc; + wc.lpszClassName = L"phoenix_viewport"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); +} + +bool OS::pending() { + MSG msg; + return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); +} + +void OS::run() { + while(pending()) { + MSG msg; + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { + OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} + +void OS::main() { + MSG msg; + while(GetMessage(&msg, 0, 0, 0)) { + if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { + OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void OS::quit() { + PostQuitMessage(0); +} + +unsigned OS::desktopWidth() { + return GetSystemMetrics(SM_CXSCREEN); +} + +unsigned OS::desktopHeight() { + return GetSystemMetrics(SM_CYSCREEN); +} + +string OS::folderSelect(Window &parent, const string &path) { + wchar_t wfilename[PATH_MAX + 1] = L""; + BROWSEINFO bi; + bi.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + bi.pidlRoot = NULL; + bi.pszDisplayName = wfilename; + bi.lpszTitle = L""; + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = 0; + bi.iImage = 0; + bool result = false; + LPITEMIDLIST pidl = SHBrowseForFolder(&bi); + if(pidl) { + if(SHGetPathFromIDList(pidl, wfilename)) { + result = true; + IMalloc *imalloc = 0; + if(SUCCEEDED(SHGetMalloc(&imalloc))) { + imalloc->Free(pidl); + imalloc->Release(); + } + } + } + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetOpenFileName(&ofn); + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + return name; +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetSaveFileName(&ofn); + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + return name; +} + +static void OS_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_KEYDOWN: { + GUITHREADINFO info; + memset(&info, 0, sizeof(GUITHREADINFO)); + info.cbSize = sizeof(GUITHREADINFO); + GetGUIThreadInfo(GetCurrentThreadId(), &info); + Object *object_ptr = (Object*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + ListBox &listBox = (ListBox&)*object_ptr; + if(wparam == VK_RETURN) { + if(listBox.onActivate) listBox.onActivate(); + } + } else if(dynamic_cast(object_ptr)) { + TextBox &textBox = (TextBox&)*object_ptr; + if(wparam == VK_RETURN) { + if(textBox.onActivate) textBox.onActivate(); + } + } + } + } + } +} + +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Object *object_ptr = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(!object_ptr || !dynamic_cast(object_ptr)) return DefWindowProc(hwnd, msg, wparam, lparam); + Window &window = (Window&)*object_ptr; + + switch(msg) { + case WM_CLOSE: { + if(window.onClose) { + if(window.onClose()) window.setVisible(false); + } else { + window.setVisible(false); + } + return TRUE; + } + + case WM_ERASEBKGND: { + if(window.window->brush == 0) break; + RECT rc; + GetClientRect(window.widget->window, &rc); + PAINTSTRUCT ps; + BeginPaint(window.widget->window, &ps); + FillRect(ps.hdc, &rc, window.window->brush); + EndPaint(window.widget->window, &ps); + return TRUE; + } + + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: { + Object *object_ptr = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); + if(object_ptr && window.window->brush) { + HDC hdc = (HDC)wparam; + SetBkColor((HDC)wparam, window.window->brushColor); + return (INT_PTR)window.window->brush; + } + } + + case WM_COMMAND: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control == 0) { + Object *object_ptr = (Object*)OS::findObject(id); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + MenuItem &menuItem = (MenuItem&)*object_ptr; + if(menuItem.onTick) menuItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuCheckItem &menuCheckItem = (MenuCheckItem&)*object_ptr; + menuCheckItem.setChecked(!menuCheckItem.checked()); + if(menuCheckItem.onTick) menuCheckItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuRadioItem &menuRadioItem = (MenuRadioItem&)*object_ptr; + if(menuRadioItem.checked() == false) { + menuRadioItem.setChecked(); + if(menuRadioItem.onTick) menuRadioItem.onTick(); + } + } + } + } else { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Button &button = (Button&)*object_ptr; + if(button.onTick) button.onTick(); + } else if(dynamic_cast(object_ptr)) { + CheckBox &checkBox = (CheckBox&)*object_ptr; + checkBox.setChecked(!checkBox.checked()); + if(checkBox.onTick) checkBox.onTick(); + } else if(dynamic_cast(object_ptr)) { + ComboBox &comboBox = (ComboBox&)*object_ptr; + if(HIWORD(wparam) == CBN_SELCHANGE) { + if(comboBox.comboBox->selection != comboBox.selection()) { + comboBox.comboBox->selection = comboBox.selection(); + if(comboBox.onChange) comboBox.onChange(); + } + } + } else if(dynamic_cast(object_ptr)) { + EditBox &editBox = (EditBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(editBox.object->locked == false && editBox.onChange) editBox.onChange(); + } + } else if(dynamic_cast(object_ptr)) { + RadioBox &radioBox = (RadioBox&)*object_ptr; + if(radioBox.checked() == false) { + radioBox.setChecked(); + if(radioBox.onTick) radioBox.onTick(); + } + } else if(dynamic_cast(object_ptr)) { + TextBox &textBox = (TextBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(textBox.object->locked == false && textBox.onChange) textBox.onChange(); + } + } + } + } + } + + case WM_NOTIFY: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + ListBox &listBox = (ListBox&)*object_ptr; + LPNMHDR nmhdr = (LPNMHDR)lparam; + LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + + if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) { + unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1; + if(imagemask == 0 || imagemask == 1) { + if(listBox.object->locked == false && listBox.onTick) listBox.onTick(nmlistview->iItem); + } else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { + listBox.listBox->lostFocus = true; + } else { + if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { + if(listBox.onChange) listBox.onChange(); + } else if(listBox.listBox->lostFocus == false && listBox.selection() == false) { + if(listBox.onChange) listBox.onChange(); + } + listBox.listBox->lostFocus = false; + } + } else if(nmhdr->code == LVN_ITEMACTIVATE) { + if(listBox.onActivate) listBox.onActivate(); + } + } + } + } + } + + case WM_HSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + HorizontalSlider &horizontalSlider = (HorizontalSlider&)*object_ptr; + if(horizontalSlider.horizontalSlider->position != horizontalSlider.position()) { + horizontalSlider.horizontalSlider->position = horizontalSlider.position(); + if(horizontalSlider.onChange) horizontalSlider.onChange(); + } + } + } + } + } + + case WM_VSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + VerticalSlider &verticalSlider = (VerticalSlider&)*object_ptr; + if(verticalSlider.verticalSlider->position != verticalSlider.position()) { + verticalSlider.verticalSlider->position = verticalSlider.position(); + if(verticalSlider.onChange) verticalSlider.onChange(); + } + } + } + } + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +Object* OS::findObject(unsigned id) { + foreach(object, os->objects) { if(object->object->id == id) return object; } + return 0; +} + +} diff --git a/snespurify/phoenix/windows/windows.hpp b/snespurify/phoenix/windows/windows.hpp new file mode 100755 index 00000000..41807a94 --- /dev/null +++ b/snespurify/phoenix/windows/windows.hpp @@ -0,0 +1,288 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + struct Data; + Data *object; +private: + virtual void unused(); +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; + Action(); +//private: + struct Data; + Data *action; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool focused(); + void setFocused(); + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setDefaultFont(Font &font); + void setFont(Font &font); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; + static Window None; + void resize(unsigned width, unsigned height); +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + nall::string getText(); + void setText(const nall::string &text); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + unsigned position(); + void setPosition(unsigned position); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + nall::string text(); + void setText(const nall::string &text); + void setEditable(bool editable = true); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + static void initialize(); + struct Data; + static Data *os; + static Object* findObject(unsigned id); + friend class Object; +}; + +}; diff --git a/snespurify/snespurify.cpp b/snespurify/snespurify.cpp new file mode 100755 index 00000000..13c55278 --- /dev/null +++ b/snespurify/snespurify.cpp @@ -0,0 +1,400 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +#include +using namespace phoenix; + +static const char applicationTitle[] = "snespurify v05"; + +struct Application : Window { + Font font; + Label pathLabel; + TextBox pathBox; + Button pathScan; + Button pathBrowse; + ListBox fileList; + Button selectAll; + Button unselectAll; + Button fixSelected; + + struct FileInfo { + string filename; + string problem; + string solution; + }; + linear_vector fileInfo; + lstring errors; + + void main(); + void enable(bool); + void scan(); + void scan(const string &pathname); + void analyze(const string &filename); + void repair(); + void createPatch(const string &filename); +} application; + +void Application::main() { + #if defined(PLATFORM_WIN) + font.create("Tahoma", 8); + #else + font.create("Sans", 8); + #endif + create(128, 128, 600, 360, applicationTitle); + setDefaultFont(font); + + unsigned x = 5, y = 5, width = 600, height = 25; + pathLabel.create(*this, x, y, 80, height, "Path to scan:"); + pathBox.create(*this, x + 85, y, 335, height); + pathScan.create(*this, x + 425, y, 80, height, "Scan"); + pathBrowse.create(*this, x + 510, y, 80, height, "Browse ..."); y += height + 5; + fileList.create(*this, x, y, 590, 290, "Filename\tProblem\tSolution"); y += 290 + 5; + selectAll.create(*this, x, y, 80, height, "Select All"); + unselectAll.create(*this, x + 85, y, 80, height, "Clear All"); + fixSelected.create(*this, 595 - 80, y, 80, height, "Correct"); y += height + 5; + fileList.setHeaderVisible(); + fileList.setCheckable(); + + onClose = []() { + OS::quit(); + return true; + }; + + pathBox.onActivate = pathScan.onTick = { &Application::scan, this }; + + pathBrowse.onTick = []() { + string pathname = OS::folderSelect(application); + if(pathname != "") application.pathBox.setText(pathname); + }; + + selectAll.onTick = []() { + unsigned count = application.fileInfo.size(); + for(unsigned i = 0; i < count; i++) application.fileList.setChecked(i, true); + }; + + unselectAll.onTick = []() { + unsigned count = application.fileInfo.size(); + for(unsigned i = 0; i < count; i++) application.fileList.setChecked(i, false); + }; + + fixSelected.onTick = { &Application::repair, this }; + + setVisible(); +} + +//don't allow actions to be taken while files are being scanned or fixed +void Application::enable(bool state) { + if(state == false) { + setTitle({ applicationTitle, " - working ..." }); + } else { + setTitle(applicationTitle); + } + + pathBox.setEnabled(state); + pathScan.setEnabled(state); + pathBrowse.setEnabled(state); + fileList.setEnabled(state); + selectAll.setEnabled(state); + unselectAll.setEnabled(state); + fixSelected.setEnabled(state); +} + +void Application::scan() { + fileInfo.reset(); + fileList.reset(); + + string pathname = pathBox.text(); + if(pathname == "") { + MessageWindow::information(application, "Please specify a directory to scan"); + return; + } + pathname.transform("\\", "/"); + if(pathname.endswith("/") == false) pathname.append("/"); + if(directory::exists(pathname) == false) { + MessageWindow::warning(application, "Specified directory does not exist"); + return; + } + + enable(false); + scan(pathname); + enable(true); + + if(fileInfo.size() == 0) { + MessageWindow::information(application, "All files are correct"); + return; + } + + unsigned counter = 0; + foreach(info, fileInfo) { + fileList.addItem({ notdir(info.filename), "\t", info.problem, "\t", info.solution }); + fileList.setChecked(counter++, true); + } + fileList.resizeColumnsToContent(); +} + +void Application::scan(const string &pathname) { + lstring files = directory::files(pathname); + foreach(file, files) { + OS::run(); + analyze({ pathname, file }); + } + + //recursion + lstring folders = directory::folders(pathname); + foreach(folder, folders) scan({ pathname, folder }); +} + +void Application::analyze(const string &filename) { + if(file::exists(filename) == false) return; + + if(filename.iendswith(".sfc") || filename.iendswith(".bs") || filename.iendswith(".st") + || filename.iendswith(".gb") || filename.iendswith(".gbc") || filename.iendswith(".sgb") + || filename.iendswith(".smc") || filename.iendswith(".swc") || filename.iendswith(".fig") || filename.iendswith(".ufo") + || filename.iendswith(".gd3") || filename.iendswith(".gd7") || filename.iendswith(".dx2") || filename.iendswith(".mgd") + || filename.iendswith(".mgh") || filename.iendswith(".048") || filename.iendswith(".058") || filename.iendswith(".068") + || filename.iendswith(".078") || filename.iendswith(".usa") || filename.iendswith(".eur") || filename.iendswith(".jap") + || filename.iendswith(".aus") || filename.iendswith(".bsx") + ) { + filemap map(filename, filemap::mode::read); + unsigned filesize = map.size(); + snes_information information(map.data(), filesize); + + //the ordering of rules is very important: + //patches need to be created prior to removal of headers + //headers need to be removed prior to renaming files (so header removal has correct filename) + //etc. + switch(information.type) { + case snes_information::TypeNormal: + case snes_information::TypeBsxSlotted: + case snes_information::TypeBsxBios: + case snes_information::TypeSufamiTurboBios: + case snes_information::TypeSuperGameBoy1Bios: + case snes_information::TypeSuperGameBoy2Bios: { + string ipsName = { nall::basename(filename), ".ips" }; + string upsName = { nall::basename(filename), ".ups" }; + if(file::exists(ipsName) == true && file::exists(upsName) == false) { + FileInfo info; + info.filename = filename; + info.problem = "Unsupported patch format"; + info.solution = "Create UPS patch"; + fileInfo.append(info); + } + + if((filesize & 0x7fff) == 512) { + FileInfo info; + info.filename = filename; + info.problem = "Copier header present"; + info.solution = "Remove copier header"; + fileInfo.append(info); + } + + if(filename.endswith(".sfc") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .sfc"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeBsx: { + if((filesize & 0x7fff) == 512) { + FileInfo info; + info.filename = filename; + info.problem = "Copier header present"; + info.solution = "Remove copier header"; + fileInfo.append(info); + } + + if(filename.endswith(".bs") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .bs"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeSufamiTurbo: { + if(filename.endswith(".st") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .st"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeGameBoy: { + if(filename.endswith(".gb") == false && filename.endswith(".gbc") == false && filename.endswith(".sgb") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .gb"; + fileInfo.append(info); + } + + break; + } + } + } +} + +void Application::repair() { + enable(false); + errors.reset(); + + for(unsigned n = 0; n < fileInfo.size(); n++) { + if(fileList.checked(n) == false) continue; + OS::run(); + + FileInfo &info = fileInfo[n]; + if(info.solution == "Create UPS patch") { + createPatch(info.filename); + } else if(info.solution == "Remove copier header") { + file fp; + if(fp.open(info.filename, file::mode::read)) { + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + if(fp.open(info.filename, file::mode::write)) { + fp.write(data + 512, size - 512); + fp.close(); + } + } + } else if(info.solution == "Rename to .sfc") { + rename(info.filename, string(nall::basename(info.filename), ".sfc")); + } else if(info.solution == "Rename to .bs") { + rename(info.filename, string(nall::basename(info.filename), ".bs")); + } else if(info.solution == "Rename to .st") { + rename(info.filename, string(nall::basename(info.filename), ".st")); + } else if(info.solution == "Rename to .gb") { + rename(info.filename, string(nall::basename(info.filename), ".gb")); + } + } + + if(errors.size() == 0) { + MessageWindow::information(application, "Selected problems have been corrected"); + } else { + string output; + for(unsigned i = 0; i < 3 && i < errors.size(); i++) output.append(string(errors[i], "\n")); + if(errors.size() > 3) output.append("\n(too many errors to show ...)"); + MessageWindow::information(application, { + "Selected problems have been corrected, but there were errors:\n\n", + output + }); + } + + fileInfo.reset(); + fileList.reset(); + enable(true); +} + +void Application::createPatch(const string &filename) { + string ipsName = { nall::basename(filename), ".ips" }; + string upsName = { nall::basename(filename), ".ups" }; + + file fp; + if(fp.open(filename, file::mode::read)) { + unsigned isize = fp.size(); + uint8_t *idata = new uint8_t[isize]; + fp.read(idata, isize); + fp.close(); + + fp.open(ipsName, file::mode::read); + unsigned psize = fp.size(); + uint8_t *pdata = new uint8_t[psize]; + fp.read(pdata, psize); + fp.close(); + + if(psize >= 8 + && pdata[0] == 'P' && pdata[1] == 'A' && pdata[2] == 'T' && pdata[3] == 'C' && pdata[4] == 'H' + && pdata[psize - 3] == 'E' && pdata[psize - 2] == 'O' && pdata[psize - 1] == 'F') { + unsigned osize = 0; + //no way to determine how big IPS output will be, allocate max size IPS patches support -- 16MB + uint8_t *odata = new uint8_t[16 * 1024 * 1024](); + memcpy(odata, idata, isize); + + unsigned offset = 5; + while(offset < psize - 3) { + unsigned addr; + addr = pdata[offset++] << 16; + addr |= pdata[offset++] << 8; + addr |= pdata[offset++] << 0; + + unsigned size; + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + if(size == 0) { + //RLE + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + for(unsigned n = addr; n < addr + size;) { + odata[n++] = pdata[offset]; + if(n > osize) osize = n; + } + offset++; + } else { + for(unsigned n = addr; n < addr + size;) { + odata[n++] = pdata[offset++]; + if(n > osize) osize = n; + } + } + } + + osize = max(isize, osize); + bool hasHeader = ((isize & 0x7fff) == 512); + + uint8_t *widata = idata; + unsigned wisize = isize; + if(hasHeader) { + //remove copier header for UPS patch creation + widata += 512; + wisize -= 512; + } + + uint8_t *wodata = odata; + unsigned wosize = osize; + if(hasHeader) { + //remove copier header for UPS patch creation + wodata += 512; + wosize -= 512; + } + + ups patcher; + if(patcher.create(widata, wisize, wodata, wosize, upsName) != ups::result::success) { + errors.append({ "Failed to create UPS patch: ", upsName, "\n" }); + } + + delete[] odata; + } else { + errors.append({ "IPS patch is invalid: ", ipsName, "\n" }); + } + + delete[] idata; + delete[] pdata; + } +} + +int main() { + application.main(); + OS::main(); + return 0; +} diff --git a/snespurify/sync.sh b/snespurify/sync.sh new file mode 100755 index 00000000..853434d2 --- /dev/null +++ b/snespurify/sync.sh @@ -0,0 +1,10 @@ +synchronize() { + if [ -d ../"$1" ]; then + test -d "$1" && rm -r "$1" + cp -r ../"$1" ./"$1" + fi +} + +synchronize "nall" +synchronize "phoenix" +rm -r phoenix/test*