diff --git a/README.md b/README.md index 6084a6230..5b81d589e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ **GDDocs** is a project built to openly give advanced information and readable information for aspiring developers looking to interface with Geometry Dash. Primarily, we aim to create this as a website for people to learn more about the inner workings of geometry dash, along with it's data. -The GDDocs website can be found [here](https://docs.gdprogra.me/#/) +The GDDocs website can be found [here](https://wyliemaster.github.io/gddocs/#/) ## Running/Building **You will require Node.js `>=12` to debug and run this project.** diff --git a/assets/screenshots/admin_secret.png b/assets/screenshots/admin_secret.png new file mode 100644 index 000000000..3a8814259 Binary files /dev/null and b/assets/screenshots/admin_secret.png differ diff --git a/assets/screenshots/colour_tags.png b/assets/screenshots/colour_tags.png new file mode 100644 index 000000000..c240c6120 Binary files /dev/null and b/assets/screenshots/colour_tags.png differ diff --git a/assets/screenshots/leaked_songs.png b/assets/screenshots/leaked_songs.png new file mode 100644 index 000000000..a72d230d8 Binary files /dev/null and b/assets/screenshots/leaked_songs.png differ diff --git a/docs/CREDITS.md b/docs/CREDITS.md index 59c0b2d31..094d83565 100644 --- a/docs/CREDITS.md +++ b/docs/CREDITS.md @@ -1,12 +1,20 @@ # Credits -Thanks to the **Geometry Dash Programming** experts, and the **GDDocs** contributors for making this project a reality. Without you guys, this project wouldn't have even been possible. +> **The GD Docs Project is maintained by [Wylie](https://github.com/Wyliemaster) and is backed by [GD Programming](https://discord.gg/gd-programming-646101505417674758) which is owned by [SMJS](https://github.com/SMJSGaming)** -*-- Team professionally led by [SMJS](https://github.com/SMJSGaming)* -- [nekit](https://github.com/nekitdev) -- [Wylie](https://github.com/Wyliemaster) +## Special Thanks + +**Special thanks to all the experts in GD Programming for their contributions to this project** - [Andre](https://github.com/AndreNIH) - [Cvolton](https://github.com/Cvolton) - [Colon](https://github.com/GDColon) -- [AlFas](https://github.com/AlFasGD) -- [zmx](https://github.com/kyurime) +- [Mat](https://github.com/matcool) +- [Nekit](https://github.com/nekitdev) +- [Nora](https://github.com/naoei) +- [Rekkon](https://github.com/Rekkonnect) +- [zmx](https://github.com/qimiko) +- [13laze](https://github.com/KiFilterFiberContext) +- [iAndy_HD3](https://github.com/iAndyHD3) + + + diff --git a/docs/ProjectCard.css b/docs/ProjectCard.css deleted file mode 100644 index 7d71eb6c1..000000000 --- a/docs/ProjectCard.css +++ /dev/null @@ -1,189 +0,0 @@ -/*html, body { - font-family: Torus, "Helvetica Neue",Tahoma,Arial,"Hiragino Kaku Gothic ProN",Meiryo,"Microsoft YaHei","Apple SD Gothic Neo",sans-serif !important; - font-weight: 300; -}*/ - -*, :before, :after { - position: relative; - box-sizing: border-box; -} - -.project_cards { - display: grid; - grid-gap: 10px; - grid-template-columns: repeat(auto-fill, minmax(200px, 290px)); - justify-content: center; -} - -.projects_card { - display: inline-block; - width: 100%; - height: 120px; - background-color: hsl(160, 20%, 20%); - border-radius: 10px; - overflow: hidden; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); - transition: all .22s ease-in-out; -} - -.project_card_background-container { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; -} - -.project_card-background { - border-radius: 10px; - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 100%; - object-fit: cover; - transition: opacity .22s ease-in-out; -} - -.project_card-background-overlay { - background: hsla(160, 20%, 15%, .7); - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - border-radius: 10px; -} - -.projects_card-card { - color: white; - text-decoration: none; - display: flex; - flex-direction: column; - justify-content: space-between; - pointer-events: none; - height: 100%; -} - -.projects_card_content { - padding: 10px; -} - -.projects_card_content-details { - display: grid; - grid-template-columns: minmax(min-content, auto) 1fr; -} - -.projects_card_logo { - display: flex; - align-items: center; - width: 60px; - height: 60px; - flex: none; -} - -.projects_card_logo-image { - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 100%; - border-radius: 6px; - -o-object-fit: contain; - object-fit: contain; - background-color: hsl(160,20%,20%); - transition: opacity .22s ease-in-out; -} - -.projects_card_details { - display: grid; - grid-template-rows: 26px 1fr; - margin-left: 10px; -} - -.projects_card_title { - display: flex; - align-items: center; - min-width: 0; - font-size: 15px; -} - -.projects_card_authors { - display: flex; - min-width: 0; - font-size: 12px; - top: 0; - left: 0; -} - -.projects_card_content-stats { - display: flex; - flex-direction: row; - align-items: center; - padding-top: 0; -} - -.projects_card_stats { - display: flex; - align-items: center; - min-width: 0; - margin-right: 15px; -} - -.projects_card_language-icon-container { - display: inline-flex; - justify-content: center; - align-items: center; - flex: none; - width: 10px; -} - -.projects_card_language-icon { - border-radius: 50%; - width: 10px; - height: 10px; -} - -.projects_card_language-icon--cs { - background: #BDFF00; -} - -.projects_card_language-icon--js { - background: #FFC700; -} - -.projects_card_language-icon--html { - background: #FF7F66; -} - -.projects_card_language-icon--py { - background: #66B6FF; -} - -.projects_card_language-message { - display: flex; - flex-direction: column; - margin-left: 5px; - font-size: 12px; - min-width: 0; -} - -.projects_card:hover:after { - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 100%; - content: ""; - border: 2px solid hsl(160,40%,80%); - border-radius: 10px; - pointer-events: none; -} - -.projects_card:hover { - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25); -} - -.project_cards-compact { - display: block; -} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index e2658c90e..a7116275c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,42 +1,51 @@ # Introduction -Welcome to the Geometry Dash Programming **[Discord](https://discord.gg/jEwtDBK)** server's online iteration of documentation for Geometry Dash. Here, you'll find all sorts of information, documents, and resources for interacting with **[Geometry Dash](http://robtopgames.com)**. We aim to make **[GDDocs](https://github.com/gd-programming/gddocs)** the most comprehensive and open source of information for the game, created by many people who have interacted with the game and wish to share such with others. +Welcome to the Geometry Dash documentation project. On this website, you will find lots of information regarding the popular indie game **Geometry Dash** -The provided documentation is available on it's repository, over [here](https://github.com/gd-programming/gddocs). For all issues regarding information found here, please consult us by creating an issue. +> If you can't find what you're looking for on here, join our [Discord Server](https://discord.gg/gd-programming-646101505417674758) and we can assist you! -## Projects +## Contributions -### Geometry Dash Wrappers +> If you wish to contribute to this project, please submit a [Pull Request](https://github.com/Wyliemaster/gddocs) in our GitHub repository -A few people, or teams have gone out of their way to create proper wrappers and API's around the **Geometry Dash** servers and it's client. All of the known and endorsed projects will find their way here, as a listing of resources people can use for their own projects. -#### Python +## Community Projects -* [gd.py](https://github.com/NeKitDS/gd.py) by [NeKitDS](https://github.com/NeKitDS) +> Over the years, many community members have developed their own tools and software which interact with Geometry Dash and its servers -#### C# +### Tools and Software -* [GDAPI](https://github.com/gd-edit/GDAPI) by the [GDEdit Team](https://github.com/gd-edit) -* [GDNET](https://github.com/GDdotNET/GDNET) by the [GD.NET Team](https://github.com/GDdotNET) +- **[GD Browser](https://gdbrowser.com/) by [GD Colon](https://github.com/GDColon)** +Recreation of Geometry Dash's interface available via the browser -### Geometry Dash Projects +- **[GD Forums](https://gdforums.com/) by [Hyperbolus Team](https://github.com/hyperbolus)** +Community Forum which can interact with the Geometry Dash Servers -These are generally projects that generally interface over the Geometry Dash servers, and overall have built up their own recognition and traction along the community, and developers alike. +- **[SPWN Language](https://github.com/Spu7Nix/SPWN-language) by [Spu7nix](https://github.com/Spu7Nix)** +Programming Language designed to create Geometry Dash levels -#### Node.js +- **[GD History](https://history.geometrydash.eu/) by [Cvolton](https://github.com/Cvolton)** +Preservation of Geometry Dash's history -* [GDBrowser](https://github.com/GDColon/GDBrowser) by [GDColon](https://github.com/GDColon) -* [GD-NodeJS-API](https://github.com/SMJSGaming/GD-NodeJS-API) by [SMJS](https://github.com/SMJSGaming) +- **[GMDPrivateServer](https://github.com/Cvolton/GMDprivateServer) by [Cvolton](https://github.com/Cvolton)** +Private Server Implementation for Geometry Dash's Servers -#### C# +- **[Geode](https://geode-sdk.org/) by [Geode Team](https://github.com/geode-sdk)** +Cross Platform Mod loader and Modding framework -* [GDEdit](https://github.com/gd-edit/GDE) by the [GDEdit Team](https://github.com/gd-edit) +### Client Modifications -#### C++ +- **[Megahack](https://absolllute.com/store/view_mega_hack_pro) by [Absolute](https://github.com/absoIute)** +Modded client for Windows and Android which includes hundreds of mods -* [GDAddon](https://github.com/Keenlos/GDAddonSDK) by the [Keenlos Team](https://github.com/Keenlos/Keenlos/blob/master/ABOUT.md) -* [GDCrypto](https://github.com/Cos8o/GDCrypto) by [Cos8o](https://github.com/Cos8o) +- **[iCreate Pro](https://geticreate.pro/) by [Camila314](https://github.com/camila314)** +Modded client for iOS which includes 30+ mods -## Outside Remarks +- **[Crystal](https://github.com/ninXout/Crystal-Client/tree/main) by [ninXout](https://github.com/ninXout)** +Modded client for MacOS which includes 60+ mods -With this documentation, the Geometry Dash Programming staff team would love to see your interesting ideas with this project, and the kinds of things you create with the information provided. Make sure to hit us up on our discord, and show us of such! +- **[BetterEdit](https://github.com/HJfod/BetterEdit) by [HJFod](https://github.com/HJfod/)** +An extension to the GD editor with many Quality of life enhancements + +- **[Textureldr](https://github.com/poweredbypie/textureldr) by [PoweredByPie](https://github.com/PoweredByPie/)** +Integrated Texture pack system \ No newline at end of file diff --git a/docs/_404.md b/docs/_404.md index cbafc5a4c..b09e5a896 100644 --- a/docs/_404.md +++ b/docs/_404.md @@ -1,5 +1,5 @@ -# Uh oh, you did a fucky wucky +# Oh Dear! -Wonder how you got here. Maybe an invalid link? +There was a problem and now you're here. -I'd suggest reporting about it to [the team](https://github.com/gd-programming/gddocs/issues) as best as you can. +If you think this was a bug, Submit it in our [issues page](https://github.com/Wyliemaster/gddocs) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 54753c5d2..5674688f9 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,17 +1,24 @@ -- [Intro](/) -- [Reference](/reference.md) -- [Credits](/CREDITS.md) +- [Reference]() + - [Official Songs](/reference/songs) + - [Secrets](/reference/secrets) + - [Keys](/reference/keys) + - [Salts](/reference/salts) +- [Request](/endpoints/generic.md) +- [Credits](/CREDITS) -## **Resources** + +**Resources** - **Server** - [Comment](/resources/server/comment.md) - [Friend Request](/resources/server/friendrequest.md) - [Gauntlet](/resources/server/gauntlet.md) + - [Hashes](/resources/server/hashes.md) - [Leaderboard Score](/resources/server/leaderboardscore.md) - [Level](/resources/server/level.md) + - [List](/resources/server/list.md) - [Map Pack](/resources/server/mappack.md) - [Message](/resources/server/message.md) - [Song](/resources/server/song.md) @@ -26,101 +33,115 @@ - [Game Variables](/resources/client/gamesave/gv.md) - [Achievements](/resources/client/gamesave/achievement.md) - [Quests](/resources/client/gamesave/quests.md) + - [Platformer UI](resources/client/gamesave/dpad.md) + - [Smart Templates](resources/client/gamesave/template.md) + - [Lists](resources/client/gamesave/list.md) + - [Enumerations](/resources/client/gamesave/enums.md) - [Level](/resources/client/level.md) - [Capacity String](/resources/client/level-components/capacity-string.md) + - [Enumerations](/resources/client/level-components/enumerations.md) - [Level Colors](/resources/client/level-components/level-colors.md) - - [Inner Level String](/resources/client/level-components/inner-level-string.md) - - [Level Object](/resources/client/level-components/level-object.md) + - [Start Level String](/resources/client/level-components/level-start.md) + - [Level String](/resources/client/level-components/level-string.md) - [Color String](/resources/client/level-components/color-string.md) - [Guideline String](/resources/client/level-components/guideline-string.md) -**Endpoints** - -- Comments - - [deleteGJAccComment20](/endpoints/deleteGJAccComment20.md) - - [deleteGJComment20](/endpoints/deleteGJComment20.md) - - [getGJAccountComments20](/endpoints/getGJAccountComments20.md) - - [getGJCommentHistory](/endpoints/getGJCommentHistory.md) - - [getGJComments21](/endpoints/getGJComments21.md) - - [uploadGJAccComment20](/endpoints/uploadGJAccComment20.md) - - [uploadGJComment21](/endpoints/uploadGJComment21.md) -- Level Packs - - [getGJGauntlets21](/endpoints/getGJGauntlets21.md) - - [getGJMapPacks21](/endpoints/getGJMapPacks21.md) -- Levels - - [deleteGJLevelUser20](/endpoints/deleteGJLevelUser20.md) - - [downloadGJLevel22](/endpoints/downloadGJLevel22.md) - - [getGJDailyLevel](/endpoints/getGJDailyLevel.md) - - [getGJLevels21](/endpoints/getGJLevels21.md) - - [rateGJDemon21](/endpoints/rateGJDemon21.md) - - [rateGJStars211](/endpoints/rateGJStars211.md) - - [reportGJLevel](/endpoints/reportGJLevel.md) - - [suggestGJStars](/endpoints/suggestGJStars.md) - - [updateGJDesc20](/endpoints/updateGJDesc20.md) - - [uploadGJLevel21](/endpoints/uploadGJLevel21.md) -- Messages - - [deleteGJMessages20](/endpoints/deleteGJMessages20.md) - - [downloadGJMessage20](/endpoints/downloadGJMessage20.md) - - [getGJMessages20](/endpoints/getGJMessages20.md) - - [uploadGJMessage20](/endpoints/uploadGJMessage20.md) -- Miscellaneous - - [getAccountURL](/endpoints/getAccountURL.md) - - [getTop1000](/endpoints/getTop1000.md) - - [getGJSongInfo](/endpoints/getGJSongInfo.md) - - [getGJTopArtists](/endpoints/getGJTopArtists.md) - - [getSaveData](/endpoints/getSaveData.md) - - [testSong](/endpoints/testSong.md) - - [likeGJItem211](/endpoints/likeGJItem211.md) - - [requestUserAccess](/endpoints/requestUserAccess.md) - - [restoreGJItems](/endpoints/restoreGJItems.md) -- Relationships - - [acceptGJFriendRequest20](/endpoints/acceptGJFriendRequest20.md) - - [blockGJUser20](/endpoints/blockGJUser20.md) - - [deleteGJFriendRequests20](/endpoints/deleteGJFriendRequests20.md) - - [getGJFriendRequests20](/endpoints/getGJFriendRequests20.md) - - [getGJUserList20](/endpoints/getGJUserList20.md) - - [readGJFriendRequest20](/endpoints/readGJFriendRequest20.md) - - [removeGJFriend20](/endpoints/removeGJFriend20.md) - - [unblockGJUser20](/endpoints/unblockGJUser20.md) - - [uploadFriendRequest20](/endpoints/uploadFriendRequest20.md) -- Rewards - - [getGJChallenges](/endpoints/getGJChallenges.md) - - [getGJRewards](/endpoints/getGJRewards.md) -- Scores - - [getGJLevelScores211](/endpoints/getGJLevelScores211.md) - - [getGJScores20](/endpoints/getGJScores20.md) -- Users - - - [getGJUserInfo20](/endpoints/getGJUserInfo20.md) - - [getGJUsers20](/endpoints/getGJUsers20.md) - - [updateGJAccSettings20](/endpoints/updateGJAccSettings20.md) - - [updateGJUserScore22](/endpoints/updateGJUserScore22.md) + - [Particle String](/resources/client/level-components/particle-string.md) + + - [Music Library](/resources/client/musiclibrary.md) + - [SFX Library](/resources/client/sfxlibrary.md) -- **Accounts** - - - [Login](/endpoints/accounts/loginGJAccount.md) - - [Registration](/endpoints/accounts/registerGJAccount.md) - -- [Request](/endpoints/request.md) - -**Account Server Endpoints** +**Endpoints** -- [Backup](/endpoints/accounts/backupGJAccountNew.md) -- [Sync](/endpoints/accounts/syncGJAccountNew.md) +**Accounts** + - [backupGJAccountNew](/endpoints/accounts/backupGJAccountNew.md) + - [syncGJAccountNew](/endpoints/accounts/syncGJAccountNew.md) + - [loginGJAccount](/endpoints/accounts/loginGJAccount.md) + - [registerGJAccount](/endpoints/accounts/registerGJAccount.md) + - [updateGJAccSettings20](/endpoints/accounts/updateGJAccSettings20.md) +**Users** + - [getGJScores20](/endpoints/users/getGJScores20.md) + - [getGJUserInfo20](/endpoints/users/getGJUserInfo20.md) + - [getGJUsers20](/endpoints/users/getGJUsers20.md) + - [updateGJUserScore22](/endpoints/users/updateGJUserScore22.md) +**Levels** + - [deleteGJLevelUser20](/endpoints/levels/deleteGJLevelUser20.md) + - [downloadGJLevel22](/endpoints/levels/downloadGJLevel22.md) + - [getGJDailyLevel](/endpoints/levels/getGJDailyLevel.md) + - [getGJGauntlets21](/endpoints/levels/getGJGauntlets21.md) + - [getGJLevels21](/endpoints/levels/getGJLevels21.md) + - [getGJLevelScores211](/endpoints/levels/getGJLevelScores211.md) + - [getGJLevelScoresPlat](/endpoints/levels/getGJLevelScoresPlat.md) + - [getGJMapPacks21](/endpoints/levels/getGJMapPacks21.md) + - [rateGJDemon21](/endpoints/levels/rateGJDemon21.md) + - [rateGJStars211](/endpoints/levels/rateGJStars211.md) + - [reportGJLevel](/endpoints/levels/reportGJLevel.md) + - [suggestGJStars20](/endpoints/levels/suggestGJStars.md) + - [updateGJDesc20](/endpoints/levels/updateGJDesc20.md) + - [uploadGJLevel21](/endpoints/levels/uploadGJLevel21.md) +**Lists** + - [deleteGJLevelList](/endpoints/lists/deleteGJLevelList.md) + - [getGJLevelLists](/endpoints/lists/getGJLevelLists.md) + - [uploadGJLevelList](/endpoints/lists/uploadGJLevelList.md) +**Multiplayer** + - [exitMPLobby](/endpoints/multiplayer/exitMPLobby.md) + - [joinMPLobby](/endpoints/multiplayer/joinMPLobby.md) + - [uploadMPComment](/endpoints/multiplayer/uploadMPComment.md) +**Comments** + - [deleteGJAccComment20](/endpoints/comments/deleteGJAccComment20.md) + - [deleteGJComment20](/endpoints/comments/deleteGJComment20.md) + - [getGJAccountComments20](/endpoints/comments/getGJAccountComments20.md) + - [getGJCommentHistory](/endpoints/comments/getGJCommentHistory.md) + - [getGJComments21](/endpoints/comments/getGJComments21.md) + - [uploadGJAccComment20](/endpoints/comments/uploadGJAccComment20.md) + - [uploadGJComment21](/endpoints/comments/uploadGJComment21.md) +**Socials** + - [acceptGJFriendRequest20](/endpoints/socials/acceptGJFriendRequest20.md) + - [blockGJUser20](/endpoints/socials/blockGJUser20.md) + - [deleteGJFriendRequests20](/endpoints/socials/deleteGJFriendRequests20.md) + - [deleteGJMessages20](/endpoints/socials/deleteGJMessages20.md) + - [downloadGJMessage20](/endpoints/socials/downloadGJMessage20.md) + - [getGJFriendRequests20](/endpoints/socials/getGJFriendRequests20.md) + - [getGJMessages20](/endpoints/socials/getGJMessages20.md) + - [getGJUserList20](/endpoints/socials/getGJUserList20.md) + - [readGJFriendRequest20](/endpoints/socials/readGJFriendRequest20.md) + - [removeGJFriend20](/endpoints/socials/removeGJFriend20.md) + - [unblockGJUser20](/endpoints/socials/unblockGJUser20.md) + - [uploadFriendRequest20](/endpoints/socials/UploadFriendRequest20.md) + - [uploadGJMessage20](/endpoints/socials/uploadGJMessage20.md) +**Rewards** + - [getGJChallenges](/endpoints/rewards/getGJChallenges.md) + - [getGJRewards](/endpoints/rewards/getGJRewards.md) +**Songs** + - [getGJSongInfo](/endpoints/songs/getGJSongInfo.md) + - [getGJTopArtists](/endpoints/songs/getGJTopArtists.md) + - [testSong](/endpoints/songs/testSong.md) + - [musiclibrary.dat](/endpoints/songs/musiclibrary.md) + - [sfxlibrary.dat](/endpoints/songs/sfxlibrary.md) +**Misc** + - [getAccountURL](/endpoints/misc/getAccountURL.md) + - [getSaveData](/endpoints/misc/getSaveData.md) + - [getTop1000](/endpoints/misc/getTop1000.md) + - [likeGJItem211](/endpoints/misc/likeGJItem211.md) + - [requestUserAccess](/endpoints/misc/requestUserAccess.md) + - [restoreGJItems](/endpoints/misc/restoreGJItems.md) +- **Topics** + - [GJP](/topics/gjp.md) + - [Level Passwords](/topics/level_passwords.md) + - [Level Encoding/Decoding](/topics/levelstring_encoding_decoding.md) + - [Game Save Files Encryption/Decryption](/topics/localfiles_encrypt_decrypt.md) + - [Status Codes](/topics/status_codes.md) + - [Vault Codes](/topics/vault_codes.md) + - [Shop](/topics/shop) + - [Tags](/topics/tags) + - [CDN Tokens](/topics/cdn_token.md) + - [Endpoints](/endpoints/endpoints.md) -**Topics** -- [GJP](/topics/gjp.md) -- [Level Passwords](/topics/level_passwords.md) -- [Level Encoding/Decoding](/topics/levelstring_encoding_decoding.md) -- [Game Save Files Encryption/Decryption](/topics/localfiles_encrypt_decrypt.md) -- [Status Codes](/topics/status_codes.md) -- [Vault Codes](/topics/vault_codes.md) -- [Shop](/topics/shop) -- [Tags](/topics/tags) -- **Encryption** +- **Algorithms** + - [AES](topics/encryption/AES.md) - [Base64 Encoding](topics/encryption/base64.md) - [CHK Generation](topics/encryption/chk.md) - [RS, UDID and UUID](topics/encryption/id.md) - - [XOR Cipher](topics/encryption/xor.md) - - [Data Zipping](topics/encryption/zip.md) + - [Xor](topics/encryption/xor.md) + - [RobTop Cipher](topics/encryption/robtop-cipher.md) diff --git a/docs/assets/fonts/texgyreheros-regular.woff b/docs/assets/fonts/texgyreheros-regular.woff new file mode 100644 index 000000000..452249512 Binary files /dev/null and b/docs/assets/fonts/texgyreheros-regular.woff differ diff --git a/docs/assets/images/delay_tag.gif b/docs/assets/images/delay_tag.gif new file mode 100644 index 000000000..ffc8c2444 Binary files /dev/null and b/docs/assets/images/delay_tag.gif differ diff --git a/docs/assets/images/fadein_tag.gif b/docs/assets/images/fadein_tag.gif new file mode 100644 index 000000000..0b4436dea Binary files /dev/null and b/docs/assets/images/fadein_tag.gif differ diff --git a/docs/assets/images/shake_tag.gif b/docs/assets/images/shake_tag.gif new file mode 100644 index 000000000..2283d8ee1 Binary files /dev/null and b/docs/assets/images/shake_tag.gif differ diff --git a/docs/endpoints/acceptGJFriendRequest20.md b/docs/endpoints/acceptGJFriendRequest20.md deleted file mode 100644 index 0b8c6bda5..000000000 --- a/docs/endpoints/acceptGJFriendRequest20.md +++ /dev/null @@ -1,60 +0,0 @@ -# acceptGJFriendRequest20.php - -Accepts an incoming friend request - -## Parameters - -### Required Parameters - -**accountID** - Account ID of the user accepting the friend request - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user accepting the friend request - -**targetAccountID** - Account ID of the user who sent the friend request - -**requestID** - ID of the friend request (Returned by [uploadFriendRequest20](/endpoints/uploadFriendRequest20.md)) - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -## Response - -1, regardless of if the friend request exists or not - -## Example - - - -### **Python** - -```py -import requests - -# With this code, DevExit is accepted a friend request -# from PasswordFinders, whose account ID is 5317656 - -data = { - "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption - "targetAccountID": 5317656, - "requestID": 43248797, - "secret": "Wmfd2893gb7", -} - -r = requests.post('http://boomlings.com/database/acceptGJFriendRequest20.php', data=data) -print(req.text) -``` - -**Response** -```py -1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/accounts/backupGJAccountNew.md b/docs/endpoints/accounts/backupGJAccountNew.md index 3be1dbceb..4c0db903a 100644 --- a/docs/endpoints/accounts/backupGJAccountNew.md +++ b/docs/endpoints/accounts/backupGJAccountNew.md @@ -2,7 +2,7 @@ Saves account data -**Note:** This page is on the account server. You can find the domain name by sending a request to /database/getAccountURL.php (currently http://www.robtopgames.net) +**Note:** This page is on the account server. You can find the domain name by sending a request to /database/getAccountURL.php (currently http://www.robtopgames.org) ## Parameters @@ -12,9 +12,9 @@ Saves account data **password** - The password of the account to be added -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **saveData** - The save data for CCGameManager.dat compressed with gzip, then a semicolon `;` and the save data for CCLocalLevels.dat compressed with gzip. @@ -46,7 +46,7 @@ data = { "secret": "Wmfv3899gc9" } -req = requests.post("http://www.robtopgames.net/database/accounts/backupGJAccountNew.php", data=data) +req = requests.post("http://www.robtopgames.org/database/accounts/backupGJAccountNew.php", data=data) print(req.text) ``` diff --git a/docs/endpoints/accounts/loginGJAccount.md b/docs/endpoints/accounts/loginGJAccount.md index babec6ff3..f47c32d44 100644 --- a/docs/endpoints/accounts/loginGJAccount.md +++ b/docs/endpoints/accounts/loginGJAccount.md @@ -1,50 +1,36 @@ -# accounts/loginGJAccount.php +# LoginGJAccount -Logs into an account +> This endpoint is used to log into a players Geometry Dash account. ## Parameters -### Required Parameters +| Parameter | Explanation | Optional | +| :--------- | :---------------------------------------------------------------------------------------------------- | :------- | +| `udid` | [The user's Universal Unique Identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) | `False` | +| `userName` | The username for the account the player is trying to log into | `False` | +| `password` | The plaintext password for the account the player is trying to log into | `False` | +| `sID` | The player's steam ID | `True` | +| `secret` | Account Secret: `Wmfv3899gc9` | `False` | -**udid** - Funnily, this is the UUID of the user, [see here](/topics/encryption/id?id=uuid) +**Note** +the `sID` parameter is only sent to the servers if the player is logging into their account from the steam release of Geometry Dash. From what has been observed, it has not been utilised. -**userName** - The username of the account being signed into +## Responses -**password** - The password of the account being signed into - -**secret** - Wmfv3899gc9 - -### Optional Parameters - -**sID** - The players Steam ID - -## Response - -The account ID and the player ID of the user, separated by a comma, otherwise -1 - -## Example - - - -### **Python** +A successful login attempt will return the players accountID and player ID seperated by a `,` ```py -import requests - -data = { - "udid": "605BE9FD-300E-49EA-A45C-B272EE64D3E0", - "userName": "DevExit", - "password": "********", # This would be DevExit's password - "secret": "Wmfv3899gc9" -} - -r = requests.post("http://boomlings.com/database/accounts/loginGJAccount.php", data=data) -print(req.text) +# response = 71,16 +accountID, playerID = response.split(",") ``` -**Response** -```py -173831,3935672 -``` +If the request was not successful, there are 7 different error codes that can be returned - +| Error Code | Meaning | +| :--------- | :-------------------------------------------------------------------------------------------------------- | +| `-1` | Generic Error | +| `-8` | If the user's password is less than 6 characters long | +| `-9` | If the user's Username is less than 3 characters long | +| `-11` | If the user's login credentials are incorrect | +| `-12` | If the user's account is disabled | +| `-13` | If the account the user is trying to log into has a different steam ID to to that account **(Unused)** | diff --git a/docs/endpoints/accounts/registerGJAccount.md b/docs/endpoints/accounts/registerGJAccount.md index 0d46d5510..a82b86e78 100644 --- a/docs/endpoints/accounts/registerGJAccount.md +++ b/docs/endpoints/accounts/registerGJAccount.md @@ -1,46 +1,33 @@ -# accounts/registerGJAccount.php +# Register Account -Registers an account +> This endpoint is used to register an account ## Parameters -### Required Parameters - -**userName** - The username of the account to be added - -**password** - The password of the account to be added - -**email** - The email of the account to be added - -**secret** - Wmfv3899gc9 +| Parameter | Explanation | Optional | +| --- | --- | --- | +| `userName` | The username of the account to create | `False` | +| `password` | The password of the account to create | `False` | +| `email` | The email of the account to create | `False` | +| `secret` | Account Secret: `Wmfv3899gc9` | `False` | ## Response -1 if the account ID was successfully created, otherwise an [error code](/topics/status_codes) - -## Example +**Successful Request** - - -### **Python** - -```py -import requests - -data = { - "userName": "GDDocsTestAcc", - "password": "********", - "email": "gddocsemail@gmail.com", - "secret": "Wmfv3899gc9" -} - -req = requests.post("http://boomlings.com/database/accounts/registerGJAccount.php", data=data) -print(req.text) ``` - -**Response** -```py 1 ``` - +**Failed Request** + +| Error Code | Meaning | +| --- | --- | +| -1 | Generic Error | +| -2 | Username taken | +| -3 | Email taken | +| -4 | Username is longer than 20 characters | +| -5 | Invalid Password | +| -6 | Invalid Email | +| -8 | Password to short | +| -9 | Username to short | diff --git a/docs/endpoints/accounts/syncGJAccountNew.md b/docs/endpoints/accounts/syncGJAccountNew.md index e5480e760..92872ef5b 100644 --- a/docs/endpoints/accounts/syncGJAccountNew.md +++ b/docs/endpoints/accounts/syncGJAccountNew.md @@ -2,23 +2,23 @@ Loads account data -**Note:** This page is on the account server. You can find the domain name by sending a request to /database/getAccountURL.php (currently http://www.robtopgames.net) +**Note:** This page is on the account server. You can find the domain name by sending a request to /database/getAccountURL.php (currently http://www.robtopgames.org) ## Parameters ### Required Parameters -**userName** - The username of the account to be added +**accountID** - The account ID of the account to be added -**password** - The password of the account to be added +**gjp2** - The password of the account to be added with [GJP2](/topics/gjp.md) encryption **secret** - Wmfv3899gc9 ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -34,7 +34,7 @@ A list of values, separated by semicolons `;`: - a list of rated levels separated by commas `,`, in the format `[levelID],[stars]`, compressed with deflate and a random string of *20* characters at the front and back -- a list of map pack objects separated by pipes `|`, with keys 1, 3, 4 and 5, compressed with deflate and a random string of *20* characters at the front and back +- a list of [map pack objects](/resources/server/mappack.md) separated by pipes `|`, with keys 1, 3, 4 and 5, compressed with deflate and a random string of *20* characters at the front and back ## Example @@ -46,12 +46,12 @@ A list of values, separated by semicolons `;`: import requests data = { - "userName": "APIAccount", - "password": "********", # This would be APIAccount's password + "accountID": 23590959, # This would be APIAccount's account ID + "gjp2": "********", # This would be APIAccount's password with GJP2 encryption "secret": "Wmfv3899gc9" } -req = requests.post("http://www.robtopgames.net/database/accounts/syncGJAccountNew.php", data=data) +req = requests.post("http://www.robtopgames.org/database/accounts/syncGJAccountNew.php", data=data) print(req.text) ``` diff --git a/docs/endpoints/updateGJAccSettings20.md b/docs/endpoints/accounts/updateGJAccSettings20.md similarity index 85% rename from docs/endpoints/updateGJAccSettings20.md rename to docs/endpoints/accounts/updateGJAccSettings20.md index 867031771..8df641e25 100644 --- a/docs/endpoints/updateGJAccSettings20.md +++ b/docs/endpoints/accounts/updateGJAccSettings20.md @@ -8,7 +8,7 @@ Updates a user's account settings **accountID** - The accountID of the user whose account is being updated -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user whose account is being updated +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user whose account is being updated **secret** - Wmfv3899gc9 @@ -51,7 +51,7 @@ data = { "twitch": "devexit" } -req = requests.post('http://boomlings.com/database/updateGJAccSettings20.php', data=data) +req = requests.post('https://www.boomlings.com/database/updateGJAccSettings20.php', data=data) print(req.text) ``` @@ -60,4 +60,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/deleteGJAccComment20.md b/docs/endpoints/comments/deleteGJAccComment20.md similarity index 100% rename from docs/endpoints/deleteGJAccComment20.md rename to docs/endpoints/comments/deleteGJAccComment20.md diff --git a/docs/endpoints/deleteGJComment20.md b/docs/endpoints/comments/deleteGJComment20.md similarity index 100% rename from docs/endpoints/deleteGJComment20.md rename to docs/endpoints/comments/deleteGJComment20.md diff --git a/docs/endpoints/getGJAccountComments20.md b/docs/endpoints/comments/getGJAccountComments20.md similarity index 78% rename from docs/endpoints/getGJAccountComments20.md rename to docs/endpoints/comments/getGJAccountComments20.md index 1db147671..d27376ab0 100644 --- a/docs/endpoints/getGJAccountComments20.md +++ b/docs/endpoints/comments/getGJAccountComments20.md @@ -35,18 +35,29 @@ Returns a list of [comment objects](/resources/server/comment.md) separated by a ```py import requests -# This code returns DevExit's account comments. - data = { - "accountID": 173831, # DevExit's account ID + "accountID": 173831, # DevExit's account ID "page": 0, "secret": "Wmfd2893gb7" } -req = requests.post("http://boomlings.com/database/getGJAccountComments20.php", data=data) +headers = { + "User-Agent": "" +} + +url = "http://www.boomlings.com/database/getGJAccountComments20.php" +req = requests.post(url, data=data, headers=headers) print(req.text) ``` +### **curl** + +```plain +curl -X POST "http://www.boomlings.com/database/getGJAccountComments20.php" -H "User-Agent: " -d "accountID=173831&page=0&secret=Wmfd2893gb7" +``` + + + **Response** ```py 2~NTAwMCBzdGFycyE=~4~9~9~2 months~6~1756926|2~Qmxvb2RiYXRoIEdHISEh~4~19~9~6 months~6~1745624|2~QWxsZWdpYW5jZSAxMDAl~4~2~9~6 months~6~1744292|2~SUNEWCAxMDAlIDop~4~1~9~6 months~6~1743608|2~T2ggeWVhaCBDYXRhIGFuZCBUVVAgMTAwJQ==~4~1~9~7 months~6~1742661|2~Mi4xMSBpcyBvdXQgOik=~4~43~9~2 years~6~1295890|2~SSBsaWtlIGhvdyBzb21lb25lIGRpc2xpa2UgYm90dGVkIG1vc3Qgb2YgbXkgY29tbWVudHMgOikgU2hvd3MgdGhhdCBJJ20uLi5mQW1PdVM=~4~16~9~2 years~6~1279970|2~TmVjcm9wb2xpeCBpbiAyMTYgYXR0IGluIHByYWN0aWNl~4~14~9~2 years~6~1264265|2~IkhpIEx1bmEi~4~15~9~3 years~6~1246506|2~TyB3YWl0IG15IDUwdGggZGVtb24gd2FzIGdvaW5nIHRvIGJlIEJ1Y2sgRm9yY2UsIG5vdCByZWFsbHkgY2VsZWJyYXRvcnkuLi4=~4~7~9~3 years~6~1238082#67:0:10 diff --git a/docs/endpoints/getGJCommentHistory.md b/docs/endpoints/comments/getGJCommentHistory.md similarity index 100% rename from docs/endpoints/getGJCommentHistory.md rename to docs/endpoints/comments/getGJCommentHistory.md diff --git a/docs/endpoints/comments/getGJComments21.md b/docs/endpoints/comments/getGJComments21.md new file mode 100644 index 000000000..79759fa31 --- /dev/null +++ b/docs/endpoints/comments/getGJComments21.md @@ -0,0 +1,68 @@ +# getGJComments21.php + +Gets a level's comments. + +## Parameters + +### Required Parameters + +**levelID** - The ID of the account whose comments you're getting + +**page** - Which page of comments you want to see + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**gameVersion** - 21 + +**binaryVersion** - 35 + +**gdw** - 0 + +**mode** - Set to 0 for most recent, and 1 for most liked + +**total** - Current use is unknown, defaults to the amount of comments the user has, but leaving it as 0 works. + +## Response + +Returns a list of [comment objects](/resources/server/comment.md) separated by a pipe `|`. + +## Example + + + +### **Python** + +```py +import requests + +# This code returns the comments on VSC by Bo. + +data = { + "levelID": 60805571, + "page": 0, + "secret": "Wmfd2893gb7" +} + +headers = { + "User-Agent": "" +} + +url = "http://www.boomlings.com/database/getGJComments21.php" +req = requests.post(url, data=data, headers=headers) +print(req.text) + +``` + +**Response** +```py +2~NzQgYXR0IHdoYXR0~3~210153877~4~0~7~0~10~4~9~15 minutes~6~46232563:1~dooblom~9~91~10~19~11~11~14~2~15~2~16~23631435|2~QUFBQUFBQUFBQQ==~3~267641474~4~0~7~0~10~37~9~17 minutes~6~46232555:1~CaAlexDa~9~26~10~12~11~2~14~0~15~2~16~19216511|2~SSBzaG91bGQgc3RhcnQgZ3JpbmRpbmcgY2hhbGxlbmdlcyEh~3~263506358~4~0~7~0~10~100~9~31 minutes~6~46232489:1~0bv~9~35~10~3~11~12~14~0~15~0~16~30775227|2~MTk1NiBhdHRlbXB0cyBpbiBwcmFjdGljZSBGSU5BTExZIQ==~3~245747440~4~0~7~0~10~0~9~1 hour~6~46232231:1~artistboylo~9~9~10~6~11~16~14~7~15~2~16~28706271|2~Ui5JLlAgQm8g~3~268611032~4~-2~7~0~10~100~9~5 hours~6~46231318:1~xStixGDx~9~11~10~12~11~12~14~6~15~0~16~31063613|2~bG1hbw==~3~147836572~4~1~7~0~10~5~9~8 hours~6~46231019:1~NGDK~9~1~10~1~11~12~14~6~15~0~16~15162356|2~NzQzIGF0dCBpbiBwcmFjdGljZSx3dw==~3~263745181~4~1~7~0~10~0~9~9 hours~6~46230852:1~eskibime~9~37~10~18~11~12~14~5~15~0~16~30290292|2~d3Rm~3~267359964~4~1~7~0~10~11~9~9 hours~6~46230775:1~knoxoniaa~9~58~10~102~11~12~14~0~15~2~16~30998487|2~W3ZdZXJ0aWNhbCBbc11wZWVkIFtjXW9uc2VydmF0aW9uIGlzIGEgc3VwZXIgbWFyaW8gNjQgc3BlZWRydW5uaW5nIHN0cmF0ZWd5Lg==~3~268138873~4~1~7~0~10~0~9~9 hours~6~46230770:1~ultrabucket~9~108~10~15~11~23~14~0~15~2~16~31054190|2~R0cgMTQgYXR0LiAoSSB3b250IHNob3cgbXkgcGVyY2VudGFnZSBqdXN0IHRydXN0IG1lIG9uIHRoaXMgb25lKQ==~3~116404059~4~1~7~0~10~0~9~10 hours~6~46230718:1~themuffinthief~9~6~10~12~11~9~14~2~15~2~16~18175167#46173:0:10 +``` + +### **curl** +```plain +curl -X POST "http://www.boomlings.com/database/getGJComments21.php" -H "User-Agent: " -d "levelID=60805571&page=0&secret=Wmfd2893gb7" +``` + + \ No newline at end of file diff --git a/docs/endpoints/uploadGJAccComment20.md b/docs/endpoints/comments/uploadGJAccComment20.md similarity index 89% rename from docs/endpoints/uploadGJAccComment20.md rename to docs/endpoints/comments/uploadGJAccComment20.md index fd2e9e9a4..b9a5ec0b7 100644 --- a/docs/endpoints/uploadGJAccComment20.md +++ b/docs/endpoints/comments/uploadGJAccComment20.md @@ -8,7 +8,7 @@ Posts an account comment **accountID** - Account ID of the user posting the comment -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user posting the comment +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user posting the comment **comment** - The comment's text, converted to [URL-safe base64](/topics/encryption/base64). @@ -16,9 +16,9 @@ Posts an account comment ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 diff --git a/docs/endpoints/uploadGJComment21.md b/docs/endpoints/comments/uploadGJComment21.md similarity index 87% rename from docs/endpoints/uploadGJComment21.md rename to docs/endpoints/comments/uploadGJComment21.md index 743f97572..f481976a8 100644 --- a/docs/endpoints/uploadGJComment21.md +++ b/docs/endpoints/comments/uploadGJComment21.md @@ -8,7 +8,7 @@ Uploads a comment to a user level. **accountID** - The commenter's account ID -**gjp** - The commenter's [GJP](/topics/encryption/gjp.md) +**gjp2** - The commenter's [GJP2](/topics/encryption/gjp.md) **userName** - The commenter's username @@ -16,7 +16,7 @@ Uploads a comment to a user level. **secret** - Wmfd2893gb7 -**levelID** - The ID of the level to comment on +**levelID** - The ID of the level to comment on. If commenting on a list, the ID should be negative **percent** - The level percentage shown on the comment @@ -24,9 +24,9 @@ Uploads a comment to a user level. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 diff --git a/docs/endpoints/deleteGJFriendRequests20.md b/docs/endpoints/deleteGJFriendRequests20.md deleted file mode 100644 index 5d1825d8e..000000000 --- a/docs/endpoints/deleteGJFriendRequests20.md +++ /dev/null @@ -1,57 +0,0 @@ -# deleteGJFriendRequests20.php - -Deletes a friend request - -## Parameters - -### Required Parameters - -**accountID** - The account ID of the user who is deleting the friend request - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user who is deleting the friend request - -**targetAccountID** - ID of the person whose friend request is being deleted - -**isSender** - 1 if the user who deleted the friend request is the sender, otherwise 0 - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -## Response - -Always returns 1, regardless of whether the request exists or not. - -## Example - - - -### **Python** - -```py -import requests - -# With this code, DevExit is deleting a friend request to the person with ID 314159 - -data = { - "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption - "targetAccountID": 314159, - "isSender": 1, # DevExit sent the friend request, so this is 1 - "secret": "Wmfd2893gb7" -} - -req = requests.post("http://boomlings.com/database/deleteGJFriendRequests20.php", data=data) -print(req.text) -``` - -**Response** -```py -1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/downloadGJLevel22.md b/docs/endpoints/downloadGJLevel22.md deleted file mode 100644 index dc551a9e9..000000000 --- a/docs/endpoints/downloadGJLevel22.md +++ /dev/null @@ -1,66 +0,0 @@ -# downloadGJLevel22.php - -Downloads a user level and info so it can be played. - -## Parameters - -### Required Parameters - -**levelID** - The ID of the level to download. Use -1 for the daily level and -2 for the weekly. - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**accountID** - The account ID of the user who is downloading the level - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user who is downloading the level - -**udid** - The [udid](/topics/encryption/id?id=udid) of the user who is downloading the level - -**uuid** - The [uuid](/topics/encryption/id?id=uuid) of the user who is downloading the level - -**inc** - Unknown function. Set to 1 - -**extras** - Unknown function. Set to 0 - -**rs** - [See here](topics/encryption/id?id=rs) - -**chk** - [See here](/topics/encryption/chk?id=download-level) - -## Response - -Returns a [level object](/resources/server/level.md). - -## Example - - - -### **Python** - -```py -import requests - -# With this code we are getting the level info of Test by DevExit - -data = { - "levelID": 62687277, - "secret": "Wmfd2893gb7" -} - -req = requests.post("http://boomlings.com/database/downloadGJLevel22.php", data=data) -print(req.text) -``` - -**Response** -```py -1:62687277:2:Test:3:QSB0ZXN0IGxldmVsIGZvciB0aGUgR0QgRG9jcyE=:4:H4sIAAAAAAAAC6WQwQ3DIAxFF3IlfxsIUU6ZIQP8AbJChy_GPSZqpF7-A4yfDOfhXcCiNMIqnVYrgYQl8rDwBTZCVbkQRI3oVHbiDU6F2jMF_lesl4q4kw2PJMbovxLBQxTpM3-I6q0oHmXjzx7N0240cu5w0UBNtESRkble8uSLHjh8nTubmYJZ2MvMrEITEN0gEJMxlLiMZ28frmj:5:1:6:3935672:8:0:9:0:10:1:12:0:13:21:14:0:17::43:0:25::18:0:19:0:42:0:45:1:15:0:30:55610687:31:0:28:1 hour:29:1 hour:35:546561:36::37:0:38:0:39:50:46::47::40::27:AQcHBwEL#1bae6491cc87c72326abcbc0a7afaee139aa7088#f17c5a61f4ba1c7512081132459ddfaaa7c6f716 -``` - - diff --git a/docs/endpoints/endpoints.md b/docs/endpoints/endpoints.md new file mode 100644 index 000000000..ea86d02e3 --- /dev/null +++ b/docs/endpoints/endpoints.md @@ -0,0 +1,71 @@ +# Endpoints List + +All Geometry Dash endpoints found in the binary. + +> Some endpoints might be unused + +- `https://www.boomlings.com/databas/checkIfServerOnline.php` +- [`https://www.boomlings.com/database/uploadGJLevel21.php`](endpoints/levels/uploadGJLevel21.md) +- [`http://www.boomlings.com/database/getSaveData.php`](endpoints/misc/getSaveData.md) +- [`https://www.boomlings.com/database/getGJMapPacks21.php`](endpoints/levels/getGJMapPacks21.md) +- [`https://www.boomlings.com/database/getGJGauntlets21.php`](endpoints/levels/getGJGauntlets21.md) +- [`https://www.boomlings.com/database/downloadGJLevel22.php`](endpoints/levels/downloadGJLevel22.md) +- [`https://www.boomlings.com/database/rateGJStars211.php`](endpoints/levels/rateGJStars211.md) +- [`https://www.boomlings.com/database/deleteGJLevelUser20.php`](endpoints/levels/deleteGJLevelUser20.md) +- [`https://www.boomlings.com/database/suggestGJStars20.php`](endpoints/levels/suggestGJStars.md) +- [`https://www.boomlings.com/database/rateGJDemon21.php`](endpoints/levels/rateGJDemon21.md) +- [`https://www.boomlings.com/database/getGJScores20.php`](endpoints/users/getGJScores20.md) +- [`https://www.boomlings.com/database/getGJLevelScoresPlat.php`](endpoints/levels/getGJLevelScoresPlat.md) +- [`https://www.boomlings.com/database/getGJLevelScores211.php`](endpoints/levels/getGJLevelScores211.md) +- [`https://www.boomlings.com/database/getGJTopArtists.php`](endpoints/songs/getGJTopArtists.md) +- [`https://www.boomlings.com/database/getGJUsers20.php`](endpoints/users/getGJUsers20.md) +- [`https://www.boomlings.com/database/getGJUserInfo20.php`](endpoints/users/getGJUserInfo20.md) +- [`https://www.boomlings.com/database/getGJMessages20.php`](endpoints/socials/getGJMessages20.md) +- [`https://www.boomlings.com/database/downloadGJMessage20.php`](endpoints/socials/downloadGJMessage20.md) +- [`https://www.boomlings.com/database/uploadGJMessage20.php`](endpoints/socials/uploadGJMessage20.md) +- [`https://www.boomlings.com/database/deleteGJMessages20.php`](endpoints/socials/deleteGJMessages20.md) +- [`https://www.boomlings.com/database/getGJCommentHistory.php`](endpoints/comments/getGJCommentHistory.md) +- [`https://www.boomlings.com/database/getGJComments21.php`](endpoints/comments/getGJComments21.md) +- [`https://www.boomlings.com/database/getGJAccountComments20.php`](endpoints/comments/getGJAccountComments20.md) +- [`https://www.boomlings.com/database/uploadGJComment21.php`](endpoints/comments/uploadGJComment21.md) +- [`https://www.boomlings.com/database/uploadGJAccComment20.php`](endpoints/comments/uploadGJAccComment20.md) +- [`https://www.boomlings.com/database/deleteGJComment20.php`](endpoints/comments/deleteGJComment20.md) +- [`https://www.boomlings.com/database/deleteGJAccComment20.php`](endpoints/comments/deleteGJAccComment20.md) +- [`https://www.boomlings.com/database/getGJFriendRequests20.php`](endpoints/socials/getGJFriendRequests20.md) +- [`https://www.boomlings.com/database/uploadFriendRequest20.php`](endpoints/socials/uploadFriendRequest20.md) +- [`https://www.boomlings.com/database/deleteGJFriendRequests20.php`](endpoints/socials/deleteGJFriendRequests20.md) +- [`https://www.boomlings.com/database/acceptGJFriendRequest20.php`](endpoints/socials/acceptGJFriendRequest20.md) +- [`https://www.boomlings.com/database/readGJFriendRequest20.php`](endpoints/socials/readGJFriendRequest20.md) +- [`https://www.boomlings.com/database/removeGJFriend20.php`](endpoints/socials/removeGJFriend20.md) +- [`https://www.boomlings.com/database/blockGJUser20.php`](endpoints/socials/blockGJUser20.md) +- [`https://www.boomlings.com/database/unblockGJUser20.php`](endpoints/socials/unblockGJUser20.md) +- [`https://www.boomlings.com/database/getGJUserList20.php`](endpoints/socials/getGJUserList20.md) +- [`https://www.boomlings.com/database/updateGJDesc20.php`](endpoints/levels/updateGJDesc20.md) +- [`https://www.boomlings.com/database/likeGJItem211.php`](endpoints/misc/likeGJItem211.md) +- [`https://www.boomlings.com/database/requestUserAccess.php`](endpoints/misc/requestUserAccess.md) +- [`https://www.boomlings.com/database/getGJSecretReward.php`](endpoints/rewards/getGJSecretReward.md) +- [`https://www.boomlings.com/database/getGJRewards.php`](endpoints/rewards/getGJRewards.md) +- [`https://www.boomlings.com/database/getGJChallenges.php`](endpoints/rewards/getGJChallenges.md) +- [`https://www.boomlings.com/database/getGJDailyLevel.php`](endpoints/levels/getGJDailyLevel.md) +- [`https://www.boomlings.com/database/restoreGJItems.php`](endpoints/misc/restoreGJItems.md) +- [`https://www.boomlings.com/database/reportGJLevel.php`](endpoints/levels/reportGJLevel.md) +- [`https://www.boomlings.com/database/getGJLevelLists.php`](endpoints/lists/getGJLevelLists.md) +- [`https://www.boomlings.com/database/getGJLevels21.php`](endpoints/levels/getGJLevels21.md) +- `https://www.boomlings.com/database/submitGJUserInfo.php` +- [`https://www.boomlings.com/database/uploadGJLevelList.php`](endpoints/lists/uploadGJLevelList.md) +- [`https://www.boomlings.com/database/deleteGJLevelList.php`](endpoints/lists/deleteGJLevelList.md) +- [`https://www.boomlings.com/database/updateGJUserScore22.php`](endpoints/users/updateGJUserScore22.md) +- [`https://www.boomlings.com/database/getGJSongInfo.php`](endpoints/songs/getGJSongInfo.md) +- ``https://www.boomlings.com/database/getCustomContentURL.php`` +- [`https://www.boomlings.com/database/accounts/registerGJAccount.php`](endpoints/accounts/registerGJAccount.md) +- [`https://www.boomlings.com/database/accounts/loginGJAccount.php`](endpoints/accounts/loginGJAccount.md) +- [`https://www.boomlings.com/database/getAccountURL.php`](endpoints/misc/getAccountURL.md) +- [`https://www.boomlings.com/database/accounts/backupGJAccountNew.php`](endpoints/accounts/backupGJAccountNew.md) +- [`https://www.boomlings.com/database/accounts/syncGJAccountNew.php`](endpoints/accounts/syncGJAccountNew.md) +- [`https://www.boomlings.com/database/updateGJAccSettings20.php`](endpoints/accounts/updateGJAccSettings20.md) +- `https://www.boomlings.com/database/accounts/accountManagement.php` +- `https://www.boomlings.com/database/accounts/lostusername.php` +- `https://www.boomlings.com/database/accounts/lostpassword.php` +- [`https://www.geometrydash.com/database/joinMPLobby.php`](endpoints/multiplayer/joinMPLobby.md) +- [`https://www.geometrydash.com/database/exitMPLobby.php`](endpoints/multiplayer/exitMPLobby.md) +- [`https://www.geometrydash.com/database/uploadMPComment.php`](endpoints/multiplayer/uploadMPComment.md) \ No newline at end of file diff --git a/docs/endpoints/generic.md b/docs/endpoints/generic.md new file mode 100644 index 000000000..e1647838f --- /dev/null +++ b/docs/endpoints/generic.md @@ -0,0 +1,37 @@ +# Important Info + +> When interacting with Geometry Dash's Private API, there are a set of rules which you must follow. Failure to follow these rules will result in the error `-1` + +## Sending requests + +To make a successful request to the Geometry Dash servers, there are a couple factors to consider: + +- Cloudflare +- Request Type +- Rate Limits + +**Cloudflare** \ +The Geometry Dash servers are protected using a service called [Cloudflare](https://www.cloudflare.com/). In order to send a successful request, bypassing cloudflare is essential. In order to bypass cloudflare there are two steps. + +- You must send the request to the `www.` subdomain. +- You must send the request with an empty user-agent + +If you don't follow these steps, cloudflare will block the request and you will recieve an HTTP error code: `1020` + +**Request Type** \ +In 99% of cases, Geometry Dash requires you to send `POST` request. The request parameters require the following Content Type: `Content-Type: application/x-www-form-urlencoded`. +- The parameters required will be detailed in their respective sections. + +**Rate Limits** \ +One thing to be mindful about is the number of requests you send at a given time. Sending too many requests will result in you becoming rate limited and not being able to send any more requests for a certain duration. As the number of requests required to start a rate limit changes, we are unable to provide exact numbers, but as of November 3rd, 2023, they are roughly: +- 20x downloadGJLevel per minute, all other data-retrieval endpoints - 2 per second + +However, there are some longer-term limits applied on top of that as well. + +## **Alternative Method** +> As of May 31st, 2023. An alternative method is using IPv6 to interact with the servers + +```py +#>curl http://[2600:3c03::f03c:91ff:fe69:863b]/database/getGJLevels21.php -X POST -d secret=Wmfd2893gb7 +1:6508283:2:ReTraY:5:3:6:4993756:8:10:9:10:10:71657392:12:0:13:21:14:6541047:17::43:3:25::18:2:19:7730:42:0:45:20000:3:VGhhbmtzIGZvciBwbGF5aW5nIEdlb21ldHJ5IERhc2g=:15:3:30:0:31:0:37:3:38:1:39:2:46:1:47:2:35:557117|... +``` diff --git a/docs/endpoints/getGJComments21.md b/docs/endpoints/getGJComments21.md deleted file mode 100644 index 2266cde7f..000000000 --- a/docs/endpoints/getGJComments21.md +++ /dev/null @@ -1,57 +0,0 @@ -# getGJComments21.php - -Gets a level's comments. - -## Parameters - -### Required Parameters - -**levelID** - The ID of the account whose comments you're getting - -**page** - Which page of comments you want to see - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**mode** - Set to 0 for most recent, and 1 for most liked - -**total** - Current use is unknown, defaults to the amount of comments the user has, but leaving it as 0 works. - -## Response - -Returns a list of [comment objects](/resources/server/comment.md) separated by a pipe `|`. - -## Example - - - -### **Python** - -```py -import requests - -# This code returns the comments on VSC by Bo. - -data = { - "levelID": 60805571, - "page": 0, - "secret": "Wmfd2893gb7" -} - -req = requests.post("http://boomlings.com/database/getGJComments21.php", data=data) -print(req.text) -``` - -**Response** -```py -2~ODk4IHRvIGp1c3QgYmVhdCBpbiBwcmFjdGlzZSBtb2RlIGdnIQ==~3~133533914~4~0~7~0~10~0~9~2 minutes~6~31468976:1~depolo~9~41~10~25~11~10~14~1~15~0~16~13735168|2~bm8gY2xpcGVy~3~128127333~4~0~7~0~10~3~9~7 minutes~6~31468705:1~ghjking~9~35~10~15~11~5~14~3~15~2~16~13391416|2~d29yc3QgbGV2ZWwgaW4gdGhlIGdhbWU=~3~19792712~4~0~7~0~10~0~9~11 minutes~6~31468489:1~neondash223~9~44~10~3~11~15~14~1~15~0~16~7609353|2~SGFja2Vy~3~114829268~4~2~7~0~10~0~9~7 hours~6~31448450:1~ThomyTamato~9~35~10~16~11~12~14~4~15~0~16~13280260|2~Z2cgMSBhdHQgKEkgaGFja2VkIGFzIGEgam9rZSk=~3~57842991~4~-4~7~1~10~100~9~8 hours~6~31445587:1~redhotgamerrhg~9~11~10~11~11~5~14~0~15~0~16~8031689|2~aSBjYW50IA==~3~120037785~4~0~7~0~10~10~9~9 hours~6~31441402:1~spot1fy~9~17~10~40~11~41~14~6~15~0~16~11870350|2~cmlwIG1vYmlsZQ==~3~93559688~4~1~7~0~10~0~9~10 hours~6~31437014:1~RandomNameGMD~9~30~10~1~11~12~14~0~15~2~16~10079340|2~Z2cgRVogMiBhdHRlbXB0cyBvbiBtb2JpbGUgWEQ=~3~93957318~4~-4~7~1~10~100~9~10 hours~6~31435874:1~MixtureGD2~9~36~10~40~11~40~14~2~15~2~16~13427406|2~diBCbyB1c2VkIG5vc3Bpa2Ugc28gdGVjaG5pY2FsbHkgbm8=~3~119607081~4~-4~7~1~10~0~9~12 hours~6~31427765:1~DailyChatMoment~9~1~10~12~11~12~14~4~15~2~16~13696752|2~SSBhbSBCbyBJIHBhc3NlZCBpdCB3aXRoIG5vY2xpcCA6RA==~3~130487134~4~-5~7~1~10~100~9~13 hours~6~31421248:1~FernanDash488~9~22~10~9~11~11~14~1~15~0~16~13526460#5705:0:10 -``` - - \ No newline at end of file diff --git a/docs/endpoints/getGJFriendRequests20.md b/docs/endpoints/getGJFriendRequests20.md deleted file mode 100644 index e0e0a5a51..000000000 --- a/docs/endpoints/getGJFriendRequests20.md +++ /dev/null @@ -1,62 +0,0 @@ -# getGJFriendRequests20.php - -Gets a user's friend requests. - -## Parameters - -### Required Parameters - -**accountID** - The account ID of the person whose friend requests you're trying to get - -**gjp** - The user's [GJP](/topics/encryption/gjp.md) - -**getSent** - 0 for friend requests the user retrieved, and 1 for friend requests the user has sent - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**page** - The page of friend requests you want. Defaults to 0 if not sent - -**total** - Current use is unknown, defaults to the amount of friend requests the user has, but leaving it as 0 works. - -## Response - -A list of [friend request objects](/resources/server/friendrequest.md) separated by a pipe `|`. - -## Example - - - -### **Python** - -```py -import requests - -# This code gets all the friend requests DevExit has sent - -data = { - "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption - "getSent": 1, - "secret": "Wmfd2893gb7" -} - -req = requests.post("http://boomlings.com/database/getGJFriendRequests20.php", data=data) -print(req.text) -``` - -**Response** -```py -1:XShadowWizardX:2:10670782:9:115:10:12:11:7:14:0:15:2:16:1919857:32:40482513:35:b3dv:41:1:37:3 months|1:HyperSoul:2:8417870:9:37:10:35:11:12:14:0:15:2:16:1217980:32:16160788:35:V2h5IGRpZCB5b3UgdW5mcmllbmQgbWUgOzM7:41:1:37:3 years|1:foreverbound95:2:15015378:9:1:10:0:11:3:14:0:15:0:16:4273678:32:13187274:35:SGksIFhE:41:1:37:3 years|1:Zhen M:2:19294285:9:1:10:0:11:3:14:0:15:0:16:5610849:32:13077932:35:ZWNrcyBkZWUgcnVicnVi:41:1:37:3 years|1:xSpectrum:2:12078249:9:22:10:15:11:12:14:0:15:2:16:2786272:32:11605585:35:OzM7LyBZb3UgbWFrZSBndWQgbGV2ZWxzIFw7MzsgSEFQUFkgTkVXIFlFQVJT:41:1:37:3 years|1:Michicun:2:9389282:9:46:10:16:11:12:14:0:15:2:16:2655938:32:10263453:35:Ok8gWW91IGNvbW1lbnRlZCBvbiBteSBsZXZlbCBYRA==:41:1:37:3 years|1:Findexi:2:995430:9:127:10:6:11:3:14:0:15:2:16:22264:32:9633972:35:TXkgZnJpZW5kIGhhcyAxIG1vcmUgc3RhciB0aGFuIHlvdQ==:41:1:37:3 years|1:lSuwako:2:11304810:9:3:10:3:11:12:14:6:15:2:16:215104:32:9621020:35:R0cgT04gMTMgQU5EIFlPVSBDT01NRU5URUQgT04gTVkgTEVWRUw=:41:1:37:3 years|1:Xaro:2:3032783:9:32:10:8:11:6:14:2:15:2:16:14233:32:7255586:35:UEx6IDszOyBJJ2xsIGdpdmUgeW91IG9uZSBvZiBteSBuZWlnaGJvcidzIGNvcmdpcyEgQ29yZ2lEZXJw:41:1:37:4 years|1:KaotikJumper:2:2676052:9:110:10:12:11:12:14:0:15:0:16:129311:32:3612380:35:REVBVEggQ09SUklET1IgQ09NUExFVEUhIChwcmFjdGljZSkgS2FwcGE=:41:1:37:4 years|1:D4rkGryf:2:4053881:9:87:10:21:11:40:14:0:15:2:16:270562:32:3125311:35:SGo=:41::37:4 years|1:TrueChaos:2:464435:9:60:10:9:11:15:14:0:15:2:16:100961:32:994096:35::41:1:37:4 years|1:DiMaViKuLov26:2:4993756:9:1:10:12:11:15:14:0:15:2:16:225521:32:944653:35:aW5zZXJ0bGVubnloZXJl:41:1:37:4 years|1:Krexon:2:3488114:9:28:10:12:11:16:14:0:15:2:16:1716100:32:397053:35:Ty5PIHBseiwgSSBMT1ZFIFVSIExWTFMhISEh:41:1:37:4 years|1:TheZekenator:2:3497675:9:1:10:0:11:3:14:0:15:0:16:1028719:32:372035:35:RGVtb24gQ29sbGFiIDop:41::37:4 years|1:Z3lLink:2:114346:9:30:10:3:11:12:14:0:15:0:16:677:32:191941:35::41:1:37:4 years|1:Experience D:2:9618:9:120:10:35:11:3:14:0:15:2:16:9917:32:191927:35::41:1:37:4 years|1:ZenthicAlpha:2:214216:9:108:10:12:11:3:14:0:15:2:16:638:32:191899:35:UGx6IEknZCBsaWtlIHRvIGZyaWVuZCBwbHogOkQ=:41:1:37:4 years#:0:20 -``` - -~~Please don't hurt me I was 12/13 at the time when I sent most of those friend requests~~ - - \ No newline at end of file diff --git a/docs/endpoints/getGJGauntlets21.md b/docs/endpoints/getGJGauntlets21.md deleted file mode 100644 index 100cac5f7..000000000 --- a/docs/endpoints/getGJGauntlets21.md +++ /dev/null @@ -1,47 +0,0 @@ -# getGJGauntlets21.php - -Gets the gauntlet levels. - -## Parameters - -### Required Parameters - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**special** - 1 (fetches the 2.2 gauntlets) - -## Response - -A list of [gauntlet objects](/resources/server/gauntlet.md), separated by a pipe `|`. - -## Example - - - -### **Python** - -```py -import requests - -data = { - "secret": "Wmfd2893gb7" -} - -req = requests.post("http://boomlings.com/database/getGJGauntlets21.php", data=data) -print(req.text) -``` - -**Response** -```py -1:1:3:27732941,28200611,27483789,28225110,27448202|1:2:3:20635816,28151870,25969464,24302376,27399722|1:3:3:28179535,29094196,29071134,26317634,12107595|1:4:3:26949498,26095850,27973097,27694897,26070995|1:5:3:18533341,28794068,28127292,4243988,28677296|1:6:3:28255647,27929950,16437345,28270854,29394058|1:7:3:25886024,4259126,26897899,7485599,19862531|1:8:3:18025697,23189196,27786218,27728679,25706351#74aeff3cb009cbde1d7235e1c7e74b47d793eb82 -``` - - \ No newline at end of file diff --git a/docs/endpoints/getGJLevels21.md b/docs/endpoints/getGJLevels21.md deleted file mode 100644 index 8593be62d..000000000 --- a/docs/endpoints/getGJLevels21.md +++ /dev/null @@ -1,141 +0,0 @@ -# getGJLevels21.php - -Gets a list of levels. - -## Parameters - -### Required Parameters - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**accountID** - The account ID of the user who is searching the levels. Only sent when **type** is set to 8, 13 or when **type** is set to 5 and you are loading your own levels. - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user who is searching the levels. Only sent when **type** is set to 5, 8 or 13. - -**gauntlet** - The ID of the gauntlet that's currently being loaded. - -**type** - Controls the fetch priority. By default it's most liked. - -| type | desc | -| ---- | ---- | -| 0 | most liked | -| 1 | most downloaded | -| 2 | default for the search button (also most liked) | -| 3 | trending | -| 4 | recent | -| 5 | user's levels (uses the player ID, **not the account ID,** in the `str` parameter to get which user) | -| 6 | featured | -| 7 | magic | -| 8 | moderator sent levels | -| 10 | list of specific levels (this uses the `str` parameter as a comma separated list of level IDs), used in map packs | -| 11 | recently awarded | -| 12 | levels from people you follow | -| 13 | friends | -| 15 | most liked in GD World | -| 16 | hall of fame | -| 17 | most featured in GD World | -| 18 | unknown because it's always empty (maybe mod or robtop only?) | -| 21 | Daily History | -| 22 | Weekly History | - -**str** - Search string, required when `type` is 5 or 10. - -**diff** - Difficulty filter. A list of integers denoting the different difficulties. Negative difficulties are ignored when any positive one is specified. - -| diff | desc | -| ---- | ---- | -| -1 | N/A (doesn't work, always returns no results) | -| -2 | Demons (use the `demonFilter` parameter to denote which demon type to search) (exclusive from -3) | -| -3 | Auto (exclusive from -2) | -| 1 | Easy | -| 2 | Normal | -| 3 | Hard | -| 4 | Harder | -| 5 | Insane | -| 6–10 | Easy to Extreme Demon, respectively | - -**len** - Length Filter. - -| len | desc | -| --- | ---- | -| 0 | Tiny | -| 1 | Short | -| 2 | Medium | -| 3 | Long | -| 4 | XL | - -**page** - Which page of levels you want to see. - -**total** - Current use is unknown, defaults to the amount of levels available, up to 9999. Most likely it's a caching system for avoiding excessive execution of expensive COUNT database queries. - -**count** - Size of a single page of levels. Capped at 10. - -**uncompleted** - Uncompleted filter. 0 for off, 1 for on. - -**onlyCompleted** - Completed filter. 0 for off, 1 for on. - -**featured** - Featured filter. 0 for off, 1 for on. - -**original** - Original filter. 0 for off, 1 for on. - -**twoPlayer** - Two Player Mode filter. 0 for off, 1 for on. - -**coins** - Coins filter. 0 for no coins, 1 for any amount of coins. - -**epic** - Epic filter. 0 for off, 1 for on. - -**noStar** - Unrated filter. Not sent when off, 1 for on. - -**star** - Rated filter. Not sent when off, 1 for on. - -**song** - Official song filter. Not sent when off. - -**customSong** - Enable newgrounds song filter, **song** parameter will be songID. Not sent when off. - -**completedLevels** - A comma separated list of completed levels enclosed in brackets. Only sent if the uncompleted or completed filter is enabled. - -**followed** - Required if `type` is 12. A comma separated list of player IDs, **not account IDs** of the people you follow. - -**demonFilter** - Denotes which difficulty of demon to search for if `diff` is -2. If left out it will search for all demons, otherwise it's 1-5 for Easy to Extreme, respectively. - -## Response - -Returns a list of [level objects](/resources/server/level) separated by pipes `|` and sorted by the parameters, a list of objects denoting the authors sorted by increasing player ID (format is `PLAYERID:USERNAME:ACCOUNTID`) which are also separated by pipes `|`, and a list of [song objects](/resources/server/song) sorted by increasing song ID and separated by `~:~`. The indexers for each are `:`, `:` and `~|~` respectively. - -## Example - - - -### **Python** - -```py -import requests - -# This code gets the most recent epic-rated Hard Demons - -data = { - "secret": "Wmfd2893gb7", - "type": 4, - "diff": -2, - "epic": 1, - "demonFilter": 3 -} - -req = requests.post("http://boomlings.com/database/getGJLevels21.php", data=data) -print(req.text) -``` - -**Response** -```py -1:62282482:2:DEAD BEAT:5:1:6:28770186:8:10:9:30:10:94536:12:0:13:21:14:2990:17:1:43:0:25::18:10:19:24971:42:1:45:56373:3:YWZ0ZXIgdGhyZWUgbW9udGhzIG9mIGhhcmQgd29yayBpIGZpbmFsbHkgcHJlc2VudCB0byB5b3UgdGhlIGxvbmcgYXdhaXRlZCBzZXF1ZWwgdG8gbXVyZGVyIG1lbG9keQ==:15:3:30:60376208:31:0:37:1:38:1:39:10:46:1:47:2:35:541786|1:62028241:2:Eternelle Vehemence:5:14:6:4761912:8:10:9:30:10:89174:12:0:13:21:14:5187:17:1:43:0:25::18:10:19:24968:42:1:45:65535:3:c3VmZmVyLCB1bnRpbCBldGVybml0eSBlbmRzLg==:15:4:30:0:31:0:37:3:38:1:39:10:46:1:47:2:35:896821|1:60927712:2:PERIHELION:5:1:6:9456326:8:10:9:30:10:8864:12:0:13:21:14:725:17:1:43:0:25::18:10:19:24970:42:1:45:65535:3:VG9vayBmb3JldmVyIHRvIG1ha2UsICBidXQgaXQgaXMgZmluYWxseSBoZXJlLiBSZWxsIC0gU21pbnggLSBHYWx6byAtIFdoaXRlaGVhZCAtIFNwdTduaXggLSBXaWsgLSBLbm90cyAtIEh5cGVyZmxhbWU=:15:3:30:51261866:31:0:37:0:38:1:39:10:46:1:47:2:35:790560|1:59502709:2:Supreme:5:4:6:2595697:8:10:9:30:10:48776:12:0:13:21:14:2471:17:1:43:0:25::18:10:19:24956:42:1:45:65535:3:aW5zZXJ0ICQzNTAgVVNEIHRvIHBsYXkgW0J5IENyZXBlcyAmIEVuWm9yZV0gZml4ZWQgYnVuY2ggb2YgYnVncyBhcm91bmQgOTAlICwgYW5kIGFkZGVkIFVMRE0gd2hlbiB5b3UgYWN0aXZhdGUgTERNICwgdXBkYXRlcyBjb21pbmcgc29vbg==:15:3:30:58794967:31:0:37:2:38:1:39:10:46:1:47:2:35:754856|1:59413155:2:HASH:5:1:6:1424041:8:10:9:30:10:28010:12:0:13:21:14:1576:17:1:43:0:25::18:10:19:24953:42:1:45:52312:3:YSBsZXZlbCB3aXRoIG1hbnkgY29sb3Vycy4=:15:3:30:52863418:31:0:37:0:38:0:39:10:46:1:47:2:35:216300|1:59352979:2:RANYER:5:5:6:9441630:8:10:9:30:10:93328:12:0:13:21:14:3681:17:1:43:0:25::18:10:19:24949:42:1:45:65535:3:YW1hemluZyBjb2xsYWJvcmF0aW9uIC4uIEkgaG9wZSB5b3UgbGlrZSBpdCAuLi4gd2l0aCBhIGxvdCBvZiBkZWRpY2F0aW9uIHdlIGJyaW5nIHlvdSByYW55ZXIgOikgZ29vZCBsdWNrIGFuZCBlbmpveSBpdCArOTAwMDAgT0JKIDowIExETT8gOyk=:15:3:30:59193188:31:0:37:1:38:1:39:10:46:1:47:2:35:658059|1:59315849:2:Double Dash:5:5:6:3624826:8:10:9:30:10:102653:12:0:13:21:14:11269:17:1:43:0:25::18:10:19:24953:42:1:45:65535:3:IkR1YWwgZ2FtZW1vZGUgaXMgdGhlIGJlc3QgZ2FtZW1vZGUiIH4gSm9uYXRoYW5HRCB8IEEgY29sb3JmdWwgMiBtaW51dGVzIG9mIG9ubHkgZHVhbHMgKCsgcGxheWVyIGNvbG9ycykgfCBHTCwgSEYsIGRvbid0IGRpZSBhdCA5OSUgOCk=:15:4:30:0:31:0:37:0:38:0:39:10:46:1:47:2:35:872453|1:59309294:2:Archaic:5:6:6:13003836:8:10:9:30:10:33708:12:0:13:21:14:1700:17:1:43:0:25::18:10:19:24949:42:1:45:65535:3:QW1hemluZyBjb2xsYWIgd2l0aCBWbGFpbmUgYW5kIE1yY3lsZGUsIGdhbWVwbGF5IGJ5IEVuem9yZSBhbmQgR2FycC4gRW5qb3l5ISE=:15:3:30:59045071:31:0:37:0:38:0:39:10:46:1:47:2:35:791611|1:58994346:2:Agios:5:2:6:18682953:8:10:9:30:10:67315:12:0:13:21:14:2226:17:1:43:0:25::18:10:19:24940:42:1:45:65535:3:N3RoIE5veHR1cm5hbCBUZWFtIE1DLCBXZSB1c2UgYSBsaXR0bGUgbmljZSB0aGVtZSBpbiB0aGlzISEgIEhvcGUgeW91IGVuam95IG91ciB3b3JrLi4gIFtWZXJpZmllZCBieSBTaXJaYWlzc10=:15:3:30:58581054:31:0:37:0:38:0:39:10:46:1:47:2:35:728233|1:58932971:2:Divine Descendance:5:5:6:11876184:8:10:9:30:10:11239:12:0:13:21:14:882:17:1:43:0:25::18:10:19:24960:42:1:45:65535:3:VmVyaWZpZWQgYnkgaVRodW5kZXIxMiwgdmlkZW8gb24gaGlzIFlUIGNoYW5uZWw=:15:3:30:0:31:0:37:3:38:1:39:10:46:1:47:2:35:713127#1424041:flash:127035|2595697:CrispyCrepes:117663|3624826:Zoroa:44967|4761912:Vrymer:411964|9441630:CatronixGD:1462499|9456326:Galzo:1463681|11876184:TroxxP1:2638799|13003836:SirZaiss:3749813|18682953:TeamNoX:5594928|28770186:swwft:6434750#1~|~216300~|~2~|~Necromancy (drum n bass)~|~3~|~772~|~4~|~zirconmusic~|~5~|~6.15~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F216000%2F216300_04___Necromancy.mp3~|~7~|~~|~8~|~1~:~1~|~541786~|~2~|~NK - Fairydust~|~3~|~1895~|~4~|~Rukkus~|~5~|~7.37~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F541000%2F541786_NK---Fairydust.mp3~|~7~|~~|~8~|~1~:~1~|~658059~|~2~|~Pursuit~|~3~|~2787~|~4~|~BoomKitty~|~5~|~7.28~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F658000%2F658059_Pursuit.mp3~|~7~|~UCwHQ93ecuoQne93sgY-x8Nw~|~8~|~1~:~1~|~713127~|~2~|~Synergetic Enigma~|~3~|~1861~|~4~|~DanJohansen~|~5~|~10.21~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F713000%2F713127_Synergetic-Enigma.mp3~|~7~|~~|~8~|~1~:~1~|~728233~|~2~|~FWLR - Badass Bae~|~3~|~50638~|~4~|~FWLRmusic~|~5~|~7.75~|~6~|~~|~10~|~https%3A%2F%2Faudio.ngfiles.com%2F728000%2F728233_FWLR---Badass-Bae.mp3%3Ff1486917017~|~7~|~~|~8~|~1~:~1~|~754856~|~2~|~[Complextro] Viscerality - Upgrade~|~3~|~48232~|~4~|~VisceralSounds~|~5~|~11.22~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F754000%2F754856_Complextro-Viscerality---U.mp3~|~7~|~~|~8~|~1~:~1~|~790560~|~2~|~Forgathering Firefly~|~3~|~47526~|~4~|~Codly~|~5~|~12.79~|~6~|~~|~10~|~https%3A%2F%2Faudio.ngfiles.com%2F790000%2F790560_Forgathering-Firefly.mp3%3Ff1518710297~|~7~|~~|~8~|~1~:~1~|~791611~|~2~|~Viscerality - Bliss [Intervention EP]~|~3~|~48232~|~4~|~VisceralSounds~|~5~|~10.38~|~6~|~~|~10~|~https%3A%2F%2Faudio.ngfiles.com%2F791000%2F791611_Viscerality---Bliss-Interv.mp3%3Ff1519159342~|~7~|~~|~8~|~1~:~1~|~872453~|~2~|~Shining Sprinter~|~3~|~1068~|~4~|~megawolf77~|~5~|~3.51~|~6~|~~|~10~|~https%3A%2F%2Faudio.ngfiles.com%2F872000%2F872453_Shining-Sprinter.mp3%3Ff1562814299~|~7~|~~|~8~|~1~:~1~|~896821~|~2~|~Panda Eyes - Anybody Else~|~3~|~45754~|~4~|~PandaEyesOfficial~|~5~|~13.92~|~6~|~~|~10~|~https%3A%2F%2Faudio.ngfiles.com%2F896000%2F896821_Panda-Eyes---Anybody-Else.mp3%3Ff1575713545~|~7~|~~|~8~|~1#91:0:10#2a84bec46c4d1304c17b4c73252faf92be4dac24 -``` - - diff --git a/docs/endpoints/getGJUserInfo20.md b/docs/endpoints/getGJUserInfo20.md deleted file mode 100644 index 8e30de4e9..000000000 --- a/docs/endpoints/getGJUserInfo20.md +++ /dev/null @@ -1,52 +0,0 @@ -# getGJUserInfo20.php - -Gets info about a user - -## Parameters - -### Required Parameters - -**targetAccountID** - The account ID of the person you want the info of - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**accountID** - Your accountID - -**gjp** - Your [GJP](/topics/encryption/gjp.md) - -## Response - -Returns a [user object](/resources/server/user.md) for the player you want - -## Example - - - -### **Python** - -```py -import requests - -data = { - "secret": "Wmfd2893gb7", - "targetAccountID": 173831 -} - -req = requests.post('http://boomlings.com/database/getGJUserInfo20.php', data=data) -print(req.text) -``` - -**Response** -```py -1:DevExit:2:3935672:13:115:17:606:10:18:11:16:3:5143:46:8531:4:142:8:1:18:0:19:0:50:0:20:UCZoN2WLAooS6uhREa9Cgpwg:21:119:22:22:23:31:24:13:25:30:26:24:28:1:43:2:48:1:30:47981:16:173831:31:0:44:DevExit:45:devexit:49:0:29:1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/getTop1000.md b/docs/endpoints/getTop1000.md deleted file mode 100644 index 0b0010410..000000000 --- a/docs/endpoints/getTop1000.md +++ /dev/null @@ -1,35 +0,0 @@ -# getTop1000.php - -[getTop1000.php](http://boomlings.com/database/getTop1000.php) is one of the very few endpoints found on the servers that can be accessed via a [GET request](https://www.w3schools.com/tags/ref_httpmethods.asp). The purpose of this endpoint is to gather the top 1000 star grinders to be used on the star leaderboards. - -## Parameters - -This endpoint does not require any parameters - -## Example - -```py -import requests - -req = requests.get("http://boomlings.com/database/getTop1000.php") -print(req.text) - -``` - -## Response Structure - -The structure for this endpoint is fairly simple - Using only a single key with 3 pieces of data assigned to the key with a `,` to seperate them. Each player is seperated by a newline `(
)` making it easy to parse. - -Below is a small snippet of the response - - 1: Smiffy777, 167677, 1413859 - 2: Gormuck, 161467, 1775477 - 3: Steekmen, 158704, 4310927 - 4: Superchat, 157872, 1098021 - 5: Cool, 157561, 4825 - -The Values for each segment are: - -| Structure | -|:----------| -|`: , , `| diff --git a/docs/endpoints/deleteGJLevelUser20.md b/docs/endpoints/levels/deleteGJLevelUser20.md similarity index 95% rename from docs/endpoints/deleteGJLevelUser20.md rename to docs/endpoints/levels/deleteGJLevelUser20.md index ed1791316..640530d79 100644 --- a/docs/endpoints/deleteGJLevelUser20.md +++ b/docs/endpoints/levels/deleteGJLevelUser20.md @@ -16,9 +16,9 @@ Deletes a level from the server. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 diff --git a/docs/endpoints/levels/downloadGJLevel22.md b/docs/endpoints/levels/downloadGJLevel22.md new file mode 100644 index 000000000..9944cba8b --- /dev/null +++ b/docs/endpoints/levels/downloadGJLevel22.md @@ -0,0 +1,84 @@ +# downloadGJLevel22.php + +Downloads a user level and info so it can be played. + +## Parameters + +### Required Parameters + +**levelID** - The ID of the level to download. Use -1 for the daily level and -2 for the weekly. + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +**accountID** - The account ID of the user who is downloading the level + +**gjp** - The [GJP](/topics/encryption/gjp.md) of the user who is downloading the level + +**udid** - The [udid](/topics/encryption/id?id=udid) of the user who is downloading the level + +**uuid** - The [uuid](/topics/encryption/id?id=uuid) of the user who is downloading the level + +**inc** - Unknown function. Set to 1 + +**extras** - Unknown function. Set to 0 + +**rs** - [See here](topics/encryption/id?id=rs) + +**chk** - [See here](/topics/encryption/chk?id=download-level) + +## Response + +Returns a [level object](/resources/server/level.md) along with 2 hashes. All of this is separated by `#`. If binary version is 42 or higher, returns data in the following format: +``` +{level}#{hash1}#{hash2} +``` +where: +- `{level}` is the level object +- `{hash1}` and `{hash2}` are integrity [hashes](/resources/server/hashes.md?id=downloadgjlevel) the GD client uses to validate the response + +## Example + + + +### **Python** + +```py +import requests + +headers = { + "User-Agent": "" +} + +data = { + "levelID": 128, + "secret": "Wmfd2893gb7" +} + +url = "http://www.boomlings.com/database/downloadGJLevel22.php" + +req = requests.post(url=url, data=data, headers=headers) +print(req.text) +``` + +### **curl** + +```plain +curl http://www.boomlings.com/database/downloadGJLevel22.php -A "" -d "levelID=128&secret=Wmfd2893gb7" +``` + + + +**Response** +```plain +1:128:2:1st level:3::4:H4sIAAAAAAAAC6WXUZLkIAiGL5StEgWV2qc5wxzAA8wV9vDbielEvumprtp9if3_AiIi0l-fpW8yNI08JNsoI5sNkTnkOUyyDkkpjTZkiO2fPtLoQ_7IOLRTfqst_6qt45dME_7SxC4zFd65kMeu-n829KX-Hr6nfnq3DfvRjfTeidtM_cHM9vUhZUv7YHOoc9Dt8Z2_22TOoe_DZ_ED5eM7DRwTH3p856ykOciWfssmW95yt61sYicsEqEGqCXCGqEHaPmCfYcWZ3uYrRJhXLe2AFuKMHrVbq8eE3nrhx_5CV0C3A804Iz5gnm9zdUdW4yYtBhQ6ZjvcWvZDRgHknAiSYEbDjABF-AaIl3EI84ZOPpXmDDMmCVlDn3FestJHwEt8_jyU79jvY71HOs51vMWsKYEjBxOS7r4QRwBqReWDGxPfOhL9E8L7BdckhJviWqMt6oBx4uiJsC4ohbzQWsChn8d8XLEywswLz1ufcrAuPcpxstEgBU4-mfFgGN8zCrw4l_ZiXYs-FhlL7NXNTq0NFkkSiOhJATE436AMBL5JhC8iRX4cqLi8td5-RX4drLOanAbOKtBuokZrnITTiLFfZ7Evc_T6HfCsMpKOIiz7BQQq4S9JJa9nKWJxOLYWawSiGVzs3ytRHspsdpoIOYzuagoQ6j1pcQ3GytRSTg2pzw5YwiNIZyv8ErwGM5nu0BlIWZBX2ycFX6RUEooJRolGiTmm79INOZYY46dbUKBykow-RvTsjGmnTHtjGlnCPsawryI-N3QxKLaUqwLTTKwAcei2rIAKzAaqsKOKhb9VipwbBqawj-Ffwr_0DQ1PGLNYtFvNQGz5UP8KuLX4J9jvw57jv163G_HI9fxyPXlkTt70NhF9hKb0q4a8XzFblx7xB32HE1uuuzt_jiaOEcT52jiHH2_l5gvrgk45ouj6XON8XOL8XP8OXCDfxX-4f-B15gv3hIw_GsxX7zFfPEO_66m9C_bF5V-dQ8AAA==:5:1:6:30144023:8:10:9:30:10:2147720:12:4:13:21:14:206221:17::43:0:25::18:0:19:0:42:0:45:208:15:2:30:128:31:0:28:10 years:29:6 years:35:0:36:0_46_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0:37:0:38:0:39:2:46::47::40::27:Aw==#30c4a15cfeb12f97de69d6bd0cc9478794e6c6c4#48e36e24b267df00a9c87aed127b4a9f020ac9c1 +``` + + diff --git a/docs/endpoints/getGJDailyLevel.md b/docs/endpoints/levels/getGJDailyLevel.md similarity index 55% rename from docs/endpoints/getGJDailyLevel.md rename to docs/endpoints/levels/getGJDailyLevel.md index 4c4639549..cedbee0e7 100644 --- a/docs/endpoints/getGJDailyLevel.md +++ b/docs/endpoints/levels/getGJDailyLevel.md @@ -10,9 +10,9 @@ Gets which daily level we're on and gets how much time is left. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -34,19 +34,30 @@ Returns the index of the current daily level and the time left in seconds, separ ```py import requests - +url = "http://www.boomlings.com/database/getGJDailyLevel.php" data = { - "weekly": 1, - "secret": "Wmfd2893gb7" + "secret": "Wmfd2893gb7", + "weekly": "1" +} +headers = { + "User-Agent": "" # Empty User-Agent } -req = requests.post("http://boomlings.com/database/getGJDailyLevel.php", data=data) -print(req.text) +response = requests.post(url, data=data, headers=headers) +print(response.text) +``` + +### **curl** + +```plain +curl -X POST http://www.boomlings.com/database/getGJDailyLevel.php -d "secret=Wmfd2893gb7&weekly=1" -A "" ``` + + **Response** ```py -100146|121576 +2959|19186 ``` - \ No newline at end of file + diff --git a/docs/endpoints/levels/getGJGauntlets21.md b/docs/endpoints/levels/getGJGauntlets21.md new file mode 100644 index 000000000..59355a15a --- /dev/null +++ b/docs/endpoints/levels/getGJGauntlets21.md @@ -0,0 +1,58 @@ +# getGJGauntlets21.php + +Gets the gauntlet levels. + +## Parameters + +### Required Parameters + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +**special** - 1 (fetches the 2.2 gauntlets) + +## Response + +A list of [gauntlet objects](/resources/server/gauntlet.md), separated by a pipe `|`, followed by a `#` and then a [hash](/resources/server/hashes.md?id=getgjgauntlets). + + + +### **Python** + +```py +import requests + +url = "http://www.boomlings.com/database/getGJGauntlets21.php" +data = { + "secret": "Wmfd2893gb7", + "special": 1 +} +headers = { + "User-Agent": "" # Empty User-Agent +} + +response = requests.post(url, data=data, headers=headers) +print(response.text) + +``` + +### **curl** +```plain +curl -X POST http://www.boomlings.com/database/getGJGauntlets21.php -d "secret=Wmfd2893gb7&special=1" -A "" + +``` + + +**Response** +```plain +1:1:3:27732941,28200611,27483789,28225110,27448202|1:2:3:20635816,28151870,25969464,24302376,27399722|1:3:3:28179535,29094196,29071134,26317634,12107595|1:4:3:26949498,26095850,27973097,27694897,26070995|1:5:3:18533341,28794068,28127292,4243988,28677296|1:6:3:28255647,27929950,16437345,28270854,29394058|1:7:3:25886024,4259126,26897899,7485599,19862531|1:8:3:18025697,23189196,27786218,27728679,25706351|1:9:3:40638411,32614529,31037168,40937291,35165900|1:10:3:37188385,35280911,37187779,36301959,36792656|1:11:3:37269362,29416734,36997718,39853981,39853458|1:12:3:27873500,34194741,34851342,36500807,39749578|1:13:3:43908596,41736289,42843431,44063088,44131636|1:14:3:38427291,38514054,36966088,38398923,36745142|1:15:3:44121158,43923301,43537990,33244195,35418014|1:16:3:105693414,86517581,92149907,95484955,106517747|1:18:3:37925002,68048356,110772842,15619194,90809996|1:21:3:57871639,82135935,81764520,80044470,92971865|1:22:3:110679874,110398067,108372523,70511594,82977900|1:29:3:75243812,57953054,102341639,97472588,85257263|1:34:3:91380905,68790607,75603568,57066554,49941534|1:35:3:75101593,62995779,93878047,71496773,58964029|1:36:3:68265721,41092171,99230232,96419926,78878411|1:37:3:92555035,94887416,85219434,89465157,82357008|1:38:3:41429267,66057230,113410989,58968008,66001175|1:40:3:39113837,64896078,95819007,56026863,94266027|1:41:3:96732638,70680001,69487890,89886591,8660411|1:42:3:82261821,97102482,28165621,103877520,108627188|1:43:3:80218929,95436164,64302902,65044525,66960655|1:46:3:83313115,83325036,83302544,83325083,81451870|1:47:3:83294687,83323867,83320529,83315343,83324930|1:48:3:83323273,83025300,83296274,83256789,83323659|1:49:3:89521875,90475659,90117883,88266256,88775556|1:50:3:90459731,90475597,90471222,90251922,90475473#d096d273a40aa7e302764919bcdb76c8abaa8e21 +``` + + diff --git a/docs/endpoints/getGJLevelScores211.md b/docs/endpoints/levels/getGJLevelScores211.md similarity index 98% rename from docs/endpoints/getGJLevelScores211.md rename to docs/endpoints/levels/getGJLevelScores211.md index db130aafd..9ba64ba6d 100644 --- a/docs/endpoints/getGJLevelScores211.md +++ b/docs/endpoints/levels/getGJLevelScores211.md @@ -8,7 +8,7 @@ Fetches the leaderboard for a level and submits your level stats to the server **accountID** - The user's account ID -**gjp** - The user's [GJP](/topics/encryption/gjp.md) +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) **levelID** - The ID of the level @@ -16,12 +16,18 @@ Fetches the leaderboard for a level and submits your level stats to the server ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 +**time** - Your time in milliseconds. Always 0 for this endpoint + +**points** - Your points. Always 0 for this endpoint + +**plat** - Always 0 for this endpoint + **percent** - The percent the user has on the level. Will not update if left out, but still retrieves data **type** - 0 for Friends, 1 for Top, 2 for Week. Defaults to 0 if left out diff --git a/docs/endpoints/levels/getGJLevelScoresPlat.md b/docs/endpoints/levels/getGJLevelScoresPlat.md new file mode 100644 index 000000000..53af064a0 --- /dev/null +++ b/docs/endpoints/levels/getGJLevelScoresPlat.md @@ -0,0 +1,102 @@ +# getGJLevelScoresPlat.php + +Fetches the leaderboard for a platformer level and submits your level stats to the server + +## Parameters + +### Required Parameters + +**accountID** - The user's account ID + +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) + +**levelID** - The ID of the level + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +**time** - Your time in milliseconds + +**points** - Your points + +**plat** - Always 1 for this endpoint + +**percent** - The percent the user has on the level. Will not update if left out, but still retrieves data + +**type** - 0 for Friends, 1 for Top, 2 for Week. Defaults to 0 if left out + +**mode** - 0 for Time, 1 for Points + +**s1** - User's attempts + 8354 + +**s2** - User's clicks + 3991 + +**s3** - User's attempt time in seconds + 4085 + +**s4** - levelSeed + + - it can be generated like this +```py +def generate_leaderboard_seed( + clicks: int, percentage: int, seconds: int, has_played: bool = True +) -> int: + + return ( + 1482 * (has_played + 1) + + (clicks + 3991) * (percentage + 8354) + + ((seconds + 4085) ** 2) - 50028039 + ) +``` + +**s5** - Random number -> `(((GJGameLevel->0x1B8 ? 2000.0 : 0) + rand()) * 4.6566e-10) * 1999.0` + +**s6** - List of PB differences (For example from 0 to 50, then 69, it would be `50,19`) [XOR'd](topics/encryption/xor.md) with `41274` and [Base64](topics/encryption/base64.md) encoded + +**s7** - Randomly Generated 10 character string + +**s8** - Attempt Count + +**s9** - The amount of coins the user got + 5819 + +**s10** - Timely ID -> for dailies and weeklies + +**chk** - [See here](/topics/encryption/chk?id=level-leaderboard) + +## Response + +Returns a list of [Leaderboard Score](/resources/server/leaderboardscore.md) objects separated by a pipe `|`. + +## Example + + + +### **Python** + +```py +import requests + +data = { + "accountID": 173831, # DevExit's account ID + "gjp": "********", # This would be DevExit's password encoded with GJP encryption + "levelID": 98170000, + "type": 1, # Leaving this out would only show friends. This shows global. + "secret": "Wmfd2893gb7" +} + +req = requests.post("http://boomlings.com/database/getGJLevelScoresPlat.php", data=data) +print(req.text) +``` + +**Response** +``` +1:masonreyes:2:45182155:9:93:10:54:11:19:14:3:15:2:16:7990002:3:95:6:1:42:3 months|1:isabced:2:151237994:9:1:10:9:11:3:14:0:15:0:16:23561530:3:250:6:2:42:4 weeks|1:nohackedmeghac:2:247724179:9:172:10:15:11:3:14:0:15:0:16:28468442:3:416:6:3:42:3 months|1:winklywinkle:2:235238621:9:1:10:7:11:12:14:4:15:0:16:26247723:3:508:6:4:42:2 months|1:BlueSpaceUnive:2:238683163:9:35:10:84:11:5:14:1:15:2:16:28438697:3:533:6:5:42:3 months|1:DEADYE:2:237977180:9:30:10:106:11:83:14:0:15:2:16:26978146:3:583:6:6:42:4 months|1:Darkness50:2:97835992:9:93:10:98:11:70:14:3:15:2:16:21263931:3:662:6:7:42:2 months|1:fathom14:2:127866214:9:117:10:94:11:17:14:0:15:2:16:13487689:3:762:6:8:42:3 months|1:aquarelaniqqa:2:244851389:9:1:10:0:11:3:14:0:15:0:16:27976920:3:820:6:9:42:4 months|1:gaggaaaaaa:2:242445614:9:20:10:89:11:3:14:0:15:2:16:27672366:3:1779:6:10:42:3 months|1:SpectatorGaming:2:238179370:9:4:10:11:11:12:14:0:15:2:16:26616945:3:1945:6:11:42:5 months|1:lowlvtank:2:246598790:9:89:10:29:11:9:14:1:15:2:16:28282295:3:1945:6:12:42:3 months|1:vladesss:2:238558729:9:233:10:15:11:9:14:0:15:0:16:26692003:3:1950:6:13:42:3 months|1:Ang3LPC:2:216997894:9:1:10:15:11:8:14:8:15:0:16:24737089:3:1954:6:14:42:2 months|1:angelito78amay:2:242875539:9:143:10:104:11:15:14:1:15:0:16:27668380:3:1958:6:15:42:5 months|1:JauhariXD:2:234283978:9:4:10:0:11:3:14:0:15:0:16:26113897:3:1958:6:16:42:5 months|1:Reazxcccccccc:2:238474887:9:105:10:53:11:12:14:1:15:2:16:27252667:3:1962:6:17:42:4 months|1:ValeriaCastGG:2:218894550:9:204:10:98:11:70:14:0:15:0:16:24532364:3:1962:6:18:42:3 months|1:Gdcmvn261:2:237184894:9:98:10:17:11:12:14:0:15:2:16:26526935:3:1966:6:19:42:5 months|1:TheGamerNoo2YT:2:240243246:9:27:10:14:11:12:14:0:15:0:16:27619991:3:1966:6:20:42:3 months|1:Nuxxany:2:242757140:9:38:10:12:11:15:14:7:15:2:16:27637062:3:1975:6:21:42:5 months|1:franchy:2:244546855:9:459:10:12:11:15:14:0:15:2:16:27924817:3:1979:6:22:42:4 months|1:MFguy23:2:243031528:9:3:10:0:11:3:14:0:15:0:16:27669567:3:2162:6:23:42:5 months|1:XattGD:2:178531425:9:1:10:0:11:3:14:0:15:0:16:27810400:3:2195:6:24:42:4 months|1:1hat0neguy:2:236879379:9:34:10:15:11:12:14:7:15:0:16:27074550:3:2220:6:25:42:4 months|1:MunPhil:2:56467054:9:314:10:12:11:0:14:0:15:2:16:8492418:3:2229:6:26:42:4 months|1:vovaadidaski:2:238335890:9:5:10:107:11:107:14:8:15:2:16:27298371:3:2358:6:27:42:5 months|1:havilgd:2:198095157:9:106:10:3:11:6:14:2:15:2:16:25446396:3:2362:6:28:42:5 months|1:abduvx77:2:241907460:9:60:10:107:11:107:14:5:15:2:16:27655321:3:2416:6:29:42:2 months|1:jddddre:2:239361905:9:2:10:0:11:3:14:8:15:0:16:28013902:3:2454:6:30:42:4 months|1:waos59294:2:203329596:9:1:10:0:11:3:14:0:15:0:16:18508924:3:2458:6:31:42:5 months|1:franellocoXd:2:242399711:9:1:10:12:11:12:14:3:15:0:16:26948298:3:10254:6:32:42:4 months|1:sedvv8:2:244951081:9:1:10:98:11:43:14:8:15:0:16:27992986:3:10525:6:33:42:4 months|1:bloody2017:2:249251172:9:115:10:7:11:3:14:0:15:2:16:28961373:3:11258:6:34:42:1 month|1:QmrdMeleesF:2:243654561:9:92:10:15:11:9:14:1:15:0:16:27771775:3:11366:6:35:42:4 months|1:sp4rkiGD:2:235450165:9:14:10:16:11:12:14:6:15:2:16:27368912:3:11604:6:36:42:3 months|1:RedCircleGuy:2:238458866:9:117:10:15:11:9:14:2:15:0:16:26673094:3:12554:6:37:42:3 months|1:ruborubik:2:235798171:9:1:10:0:11:3:14:2:15:0:16:26609657:3:12900:6:38:42:5 months|1:dylan373:2:244908513:9:25:10:9:11:0:14:0:15:0:16:27989289:3:15829:6:39:42:4 months|1:shoprime:2:234190215:9:338:10:63:11:12:14:0:15:0:16:27293798:3:17908:6:40:42:5 months|1:sandyhookvictim:2:239239633:9:55:10:40:11:12:14:5:15:0:16:27796684:3:18458:6:41:42:4 months|1:lauttygdfSRRT:2:228368610:9:1:10:35:11:3:14:0:15:0:16:26255864:3:20291:6:42:42:4 months|1:Kyovki:2:216723736:9:1:10:3:11:3:14:0:15:0:16:24342713:3:21520:6:43:42:4 months|1:maucit0:2:227257195:9:158:10:20:11:18:14:1:15:2:16:27677649:3:22387:6:44:42:5 months|1:HoshiXYZ:2:237838765:9:383:10:42:11:12:14:0:15:2:16:26559690:3:22525:6:45:42:1 week|1:masterkill009:2:248926141:9:164:10:94:11:12:14:1:15:0:16:28644580:3:22837:6:46:42:2 months|1:DirtyBlonde12:2:231455140:9:141:10:13:11:11:14:0:15:0:16:25726325:3:23654:6:47:42:2 months|1:Ev212:2:241065335:9:4:10:11:11:7:14:0:15:2:16:27290448:3:24216:6:48:42:4 months|1:Xintu:2:242146348:9:459:10:12:11:15:14:0:15:2:16:27501641:3:25179:6:49:42:5 months|1:GeoKink:2:211397249:9:97:10:4:11:3:14:3:15:2:16:24041843:3:25237:6:50:42:2 weeks|1:EstebanGd29:2:224818168:9:43:10:9:11:12:14:7:15:2:16:25170955:3:27441:6:51:42:4 months|1:P1iko:2:119382587:9:30:10:16:11:12:14:4:15:0:16:11935532:3:30350:6:52:42:3 months|1:Druzz:2:20628604:9:114:10:54:11:15:14:0:15:2:16:5951333:3:33362:6:53:42:5 months|1:ghermanteg:2:233711857:9:1:10:3:11:12:14:6:15:0:16:26005052:3:35725:6:54:42:1 week|1:KaosKiDD:2:238054916:9:2:10:16:11:12:14:1:15:0:16:27139283:3:38379:6:55:42:3 months|1:CerealCamera:2:147668407:9:129:10:41:11:40:14:0:15:2:16:20583889:3:40075:6:56:42:5 months|1:n0va3:2:184538865:9:314:10:40:11:12:14:0:15:2:16:20502560:3:48808:6:57:42:5 months|1:Alonzie:2:247006049:9:161:10:4:11:3:14:1:15:2:16:28375277:3:52066:6:58:42:2 months|1:WeedWTF:2:126012661:9:32:10:18:11:17:14:6:15:2:16:13267979:3:66008:6:59:42:4 months|1:Cronos1954H:2:245503420:9:4:10:35:11:2:14:0:15:0:16:28084121:3:67116:6:60:42:4 months|1:zLxvxndxr:2:228864792:9:459:10:5:11:3:14:0:15:2:16:26187405:3:69616:6:61:42:4 months|1:SmileRR1608:2:165853358:9:30:10:12:11:13:14:4:15:0:16:18066269:3:76087:6:62:42:4 months|1:egrmir228:2:239084882:9:233:10:11:11:9:14:0:15:0:16:27920496:3:79170:6:63:42:3 months|1:PetrosAlt:2:247134244:9:1:10:0:11:3:14:0:15:0:16:28522306:3:83475:6:64:42:3 weeks|1:ThomasGaming64:2:157109234:9:31:10:11:11:18:14:0:15:0:16:15825883:3:87029:6:65:42:2 months|1:gercc:2:247353001:9:37:10:44:11:12:14:1:15:0:16:28388950:3:87658:6:66:42:3 months|1:Suovin:2:12852289:9:35:10:35:11:8:14:6:15:2:16:4943703:3:87745:6:67:42:6 months|1:GMDfrostbyte:2:244930781:9:2:10:86:11:5:14:2:15:0:16:27989605:3:88887:6:68:42:3 months|1:iKronnoi:2:163174203:9:2:10:12:11:15:14:8:15:0:16:17062684:3:90433:6:69:42:2 months|1:hectorpro03:2:238235908:9:49:10:0:11:3:14:1:15:0:16:26465216:3:93525:6:70:42:4 months|1:DashSans:2:206031573:9:175:10:9:11:1:14:0:15:2:16:26289314:3:93633:6:71:42:3 months|1:egordruzisteam:2:231573558:9:2:10:12:11:19:14:8:15:0:16:26146950:3:95962:6:72:42:4 months|1:thehattrickhero:2:236214082:9:1:10:29:11:3:14:0:15:0:16:26287695:3:96575:6:73:42:3 months|1:sweetiefemboy:2:246402023:9:2:10:8:11:8:14:8:15:2:16:28590595:3:97870:6:74:42:3 months|1:killua333333:2:238680925:9:60:10:15:11:12:14:0:15:0:16:27658945:3:99754:6:75:42:3 months|1:jdd0606:2:216780052:9:1:10:0:11:3:14:0:15:0:16:24326809:3:100720:6:76:42:5 months|1:Lianju23:2:240365767:9:35:10:89:11:40:14:6:15:2:16:27104642:3:101479:6:77:42:3 months|1:hahayeahbot2:2:138319396:9:394:10:52:11:12:14:0:15:2:16:16839912:3:101762:6:78:42:4 months|1:F1azGD:2:244860380:9:117:10:12:11:18:14:2:15:0:16:28122964:3:102491:6:79:42:4 months|1:ZyphonoxX:2:229148151:9:21:10:11:11:3:14:0:15:0:16:27335565:3:105091:6:80:42:5 months|1:ApcPlay:2:219831214:9:38:10:9:11:12:14:6:15:2:16:24629818:3:108858:6:81:42:3 months|1:nexenGD450:2:244990822:9:154:10:54:11:9:14:0:15:0:16:28259714:3:109483:6:82:42:2 weeks|1:cheshet:2:237061734:9:219:10:3:11:3:14:0:15:0:16:26408801:3:109541:6:83:42:1 week|1:valentin7893:2:245906348:9:98:10:94:11:12:14:0:15:2:16:28148148:3:109645:6:84:42:1 month|1:rainbow72148:2:243789071:9:3:10:11:11:14:14:8:15:2:16:28231089:3:110062:6:85:42:2 months|1:GatoKittypro1:2:216695479:9:3:10:15:11:6:14:8:15:2:16:24318885:3:110087:6:86:42:5 months|1:play331:2:240985569:9:107:10:2:11:3:14:0:15:0:16:28140154:3:110491:6:87:42:2 months|1:Mozani:2:243739009:9:1:10:49:11:41:14:8:15:2:16:27785322:3:111350:6:88:42:4 months|1:purkey803:2:126832365:9:2:10:2:11:12:14:4:15:0:16:26972877:3:111845:6:89:42:3 months|1:hoholock1992:2:242601071:9:7:10:31:11:18:14:7:15:0:16:27607172:3:113125:6:90:42:3 months|1:Collection:2:216547449:9:368:10:102:11:15:14:0:15:0:16:24326733:3:115650:6:91:42:6 months|1:GoldMimmo:2:123451382:9:111:10:12:11:106:14:0:15:0:16:20548195:3:115983:6:92:42:6 months|1:demonrobot:2:1322539:9:116:10:10:11:3:14:0:15:2:16:3725519:3:116354:6:93:42:5 months|1:molemelvindash:2:11858394:9:31:10:9:11:12:14:5:15:2:16:3183046:3:116558:6:94:42:6 months|1:CoYzN:2:207867574:9:132:10:12:11:106:14:0:15:0:16:23347587:3:116754:6:95:42:6 months|1:kevle:2:238433022:9:48:10:15:11:15:14:0:15:2:16:27098663:3:117416:6:96:42:6 months|1:BradenbowlPro:2:49064662:9:22:10:12:11:9:14:7:15:0:16:7516531:3:117429:6:97:42:6 months|1:Nomiix:2:2717875:9:407:10:2:11:43:14:0:15:2:16:3786069:3:118004:6:98:42:6 months|1:ShInigamI4ever:2:134024259:9:21:10:15:11:15:14:7:15:2:16:18097714:3:118512:6:99:42:4 months|1:d0nK:2:42863178:9:62:10:13:11:9:14:4:15:2:16:7113081:3:119420:6:100:42:6 months|1:HekkaPekka:2:23028544:9:1:10:0:11:3:14:7:15:0:16:12273185:3:119595:6:101:42:6 months|1:TheRealKimizan:2:21031323:9:467:10:90:11:3:14:0:15:2:16:6776466:3:119625:6:102:42:6 months|1:GD Baio:2:21651324:9:141:10:8:11:16:14:0:15:2:16:7129730:3:119662:6:103:42:6 months|1:LonelySpy:2:12278574:9:334:10:18:11:70:14:0:15:2:16:2955213:3:119929:6:104:42:5 months|1:mrspoh:2:241261972:9:2:10:9:11:3:14:8:15:0:16:27350485:3:119962:6:105:42:4 months|1:PHon7om:2:236027378:9:482:10:63:11:6:14:0:15:0:16:28934192:3:120037:6:106:42:2 months|1:pi6:2:240771687:9:1:10:0:11:3:14:0:15:0:16:27225105:3:120170:6:107:42:5 months|1:577Nik:2:249293888:9:35:10:0:11:20:14:1:15:2:16:28753094:3:121216:6:108:42:2 months|1:Jimenezmigue:2:11133617:9:463:10:9:11:11:14:0:15:2:16:2194402:3:121845:6:109:42:6 months|1:nnum:2:241633053:9:140:10:94:11:107:14:0:15:2:16:27389698:3:122258:6:110:42:5 months|1:Penguinguy333:2:15290962:9:109:10:40:11:3:14:0:15:0:16:4469484:3:122691:6:111:42:6 months|1:wartouk:2:225800534:9:1:10:98:11:12:14:7:15:0:16:25280409:3:122770:6:112:42:3 months|1:Saturnb4:2:195255958:9:61:10:40:11:74:14:5:15:2:16:26246569:3:122958:6:113:42:6 months|1:PicorGD:2:99096154:9:98:10:0:11:12:14:0:15:2:16:10614877:3:123037:6:114:42:6 months|1:Kaal:2:50172055:9:92:10:30:11:17:14:0:15:2:16:9214388:3:123041:6:115:42:6 months|1:eXp3rienc:2:167991384:9:5:10:0:11:16:14:8:15:2:16:19672755:3:124395:6:116:42:3 months|1:BartektusOtPL:2:6913271:9:246:10:17:11:1:14:0:15:2:16:2027679:3:124466:6:117:42:6 months|1:retromanGD:2:163856600:9:41:10:18:11:12:14:7:15:0:16:17341242:3:124812:6:118:42:5 months|1:BestEliteGamer:2:222263605:9:84:10:3:11:12:14:0:15:2:16:26893674:3:124912:6:119:42:3 months|1:Cade2005:2:79209836:9:78:10:6:11:23:14:3:15:0:16:10924116:3:125287:6:120:42:6 months|1:SUSLICEK:2:247945575:9:41:10:18:11:12:14:7:15:2:16:28484245:3:125375:6:121:42:2 months|1:MiniJimmy123:2:234250821:9:35:10:9:11:15:14:0:15:0:16:26073291:3:125887:6:122:42:1 month|1:imnotrex:2:34234252:9:407:10:14:11:11:14:0:15:2:16:6779570:3:125995:6:123:42:6 months|1:picoil99:2:243070280:9:52:10:1:11:14:14:6:15:2:16:28057949:3:126328:6:124:42:2 months|1:EndFriends:2:138591659:9:66:10:18:11:17:14:0:15:0:16:13736559:3:126712:6:125:42:3 months|1:CallMeSnow:2:27485488:9:78:10:6:11:43:14:2:15:2:16:6975428:3:126729:6:126:42:6 months|1:SettingMark:2:247912868:9:132:10:16:11:12:14:0:15:0:16:28522319:3:126729:6:127:42:1 month|1:BankIt:2:148650715:9:37:10:12:11:12:14:0:15:0:16:16032202:3:127041:6:128:42:3 months|1:pinpunch:2:8154467:9:88:10:29:11:15:14:1:15:2:16:1164835:3:127333:6:129:42:6 months|1:Kilesk:2:168793062:9:13:10:12:11:12:14:4:15:0:16:26434355:3:127438:6:130:42:4 months|1:drahnoel:2:60203890:9:342:10:85:11:73:14:0:15:2:16:8130568:3:127483:6:131:42:6 months|1:Nobume:2:28403056:9:1:10:0:11:3:14:2:15:0:16:6701563:3:127600:6:132:42:6 months|1:sfdasantiga:2:158288219:9:88:10:98:11:12:14:1:15:0:16:15966193:3:128079:6:133:42:5 months|1:MgeeN:2:19042963:9:138:10:38:11:0:14:0:15:2:16:5637936:3:128558:6:134:42:6 months|1:NinSam:2:176809988:9:107:10:4:11:16:14:0:15:2:16:20754112:3:129270:6:135:42:6 months|1:Mars122:2:243796135:9:35:10:9:11:12:14:0:15:2:16:27894030:3:130350:6:136:42:1 month|1:monotyper:2:231890264:9:1:10:5:11:11:14:8:15:0:16:25763510:3:130779:6:137:42:2 months|1:persona45:2:246250839:9:394:10:3:11:7:14:0:15:2:16:28332532:3:131391:6:138:42:3 months|1:Draksou2:2:148982809:9:90:10:12:11:9:14:1:15:2:16:14687276:3:131395:6:139:42:6 months|1:sonixray:2:3834218:9:25:10:16:11:12:14:0:15:0:16:1137675:3:132437:6:140:42:6 months|1:IzzyBoiYT:2:147826314:9:79:10:4:11:3:14:0:15:0:16:15150005:3:132608:6:141:42:6 months|1:deimostrx:2:247640406:9:13:10:12:11:102:14:4:15:0:16:28432515:3:133054:6:142:42:3 months|1:SwaggyRabbit:2:141263944:9:8:10:8:11:10:14:6:15:2:16:17307505:3:133062:6:143:42:6 months|1:Tertawa:2:241750964:9:140:10:94:11:3:14:0:15:2:16:27417195:3:133141:6:144:42:5 months|1:Fritor9:2:143832918:9:163:10:12:11:11:14:0:15:0:16:14319046:3:133158:6:145:42:4 months|1:excelluijGD:2:113083743:9:368:10:12:11:17:14:0:15:0:16:11085475:3:133662:6:146:42:5 months|1:LithiumGD:2:142126978:9:5:10:25:11:12:14:8:15:0:16:14225177:3:133883:6:147:42:6 months|1:KNOEPPEL:2:12348083:9:80:10:15:11:15:14:4:15:0:16:3009121:3:134083:6:148:42:6 months|1:barreradash:2:9604468:9:24:10:5:11:11:14:6:15:0:16:2740614:3:134675:6:149:42:5 months|1:fromager:2:234339502:9:47:10:11:11:18:14:0:15:0:16:26368501:3:134870:6:150:42:6 months|1:khalllkh:2:243314432:9:2:10:15:11:3:14:7:15:0:16:27822896:3:135208:6:151:42:4 months|1:RandomGuy888:2:224136174:9:233:10:15:11:20:14:0:15:0:16:25223299:3:135220:6:152:42:5 months|1:BEHOLDER19871:2:242797177:9:32:10:53:11:18:14:6:15:2:16:27630367:3:135312:6:153:42:4 months|1:buramtyplayer:2:236077295:9:1:10:0:11:12:14:0:15:2:16:27222891:3:135758:6:154:42:4 months|1:Fictinium:2:48287882:9:426:10:12:11:16:14:0:15:2:16:7602382:3:136004:6:155:42:5 months|1:ShaderMan:2:37845304:9:31:10:10:11:9:14:0:15:0:16:6877985:3:136700:6:156:42:6 months|1:ItsBullseye:2:7040242:9:59:10:8:11:3:14:5:15:2:16:1060689:3:137875:6:157:42:6 months|1:gameboyemm:2:183506706:9:368:10:17:11:17:14:0:15:2:16:20577750:3:137879:6:158:42:2 months|1:D0inky:2:116789992:9:64:10:13:11:6:14:1:15:0:16:11549766:3:138325:6:159:42:6 months|1:Gryff64:2:146960676:9:78:10:15:11:3:14:3:15:2:16:16528711:3:138362:6:160:42:5 months|1:p0up0u:2:245155666:9:22:10:2:11:6:14:7:15:2:16:28033929:3:138412:6:161:42:2 months|1:Pazu42:2:228104804:9:32:10:63:11:4:14:1:15:2:16:26824977:3:138583:6:162:42:4 months|1:Davcity:2:37783728:9:123:10:41:11:40:14:0:15:0:16:6786848:3:139079:6:163:42:6 months|1:Lake:2:16516212:9:76:10:7:11:3:14:2:15:2:16:4861064:3:139129:6:164:42:6 months|1:stevenoct:2:14518486:9:123:10:15:11:12:14:0:15:2:16:4778390:3:139145:6:165:42:2 months|1:nng122:2:225314792:9:1:10:0:11:3:14:0:15:0:16:25476944:3:139429:6:166:42:6 months|1:eqekto:2:225890052:9:22:10:12:11:12:14:7:15:0:16:25291366:3:139575:6:167:42:5 months|1:Sisup:2:30132853:9:16:10:12:11:59:14:5:15:2:16:6676029:3:139795:6:168:42:6 months|1:nobik33:2:138315212:9:31:10:3:11:11:14:0:15:0:16:16619280:3:140250:6:169:42:5 months|1:BobTheBylder:2:191956136:9:13:10:18:11:37:14:3:15:2:16:21267772:3:140350:6:170:42:6 months|1:Woofdog90:2:100569144:9:1:10:12:11:12:14:4:15:0:16:11973055:3:140412:6:171:42:5 months|1:czumpi:2:44290064:9:2:10:6:11:18:14:7:15:0:16:8281323:3:140550:6:172:42:6 months|1:Blobflop:2:58199190:9:31:10:27:11:19:14:4:15:0:16:8771053:3:140883:6:173:42:6 months|1:BimFlauschiq:2:234287333:9:48:10:86:11:70:14:1:15:0:16:27099292:3:140883:6:174:42:6 months|1:minebox230:2:5758295:9:133:10:3:11:12:14:0:15:0:16:441312:3:140916:6:175:42:6 months|1:HueVesuvius:2:182496168:9:30:10:9:11:12:14:0:15:0:16:20507827:3:141350:6:176:42:3 months|1:therealmrnuggz:2:174941428:9:1:10:12:11:12:14:0:15:2:16:19281325:3:141379:6:177:42:1 month|1:Kammantop:2:238529976:9:1:10:0:11:3:14:0:15:0:16:26686302:3:141475:6:178:42:3 months|1:PyroMan1a:2:115665575:9:371:10:29:11:10:14:0:15:0:16:13467645:3:141658:6:179:42:5 months|1:JamesGDlol:2:238032234:9:1:10:15:11:12:14:6:15:0:16:26802260:3:141937:6:180:42:2 months|1:TheZeroStar:2:246107699:9:1:10:0:11:3:14:0:15:0:16:28184814:3:142104:6:181:42:3 months|1:Qualpyn:2:129095315:9:1:10:47:11:12:14:8:15:0:16:13459350:3:142204:6:182:42:4 months|1:EnzoGDYtbs:2:244666314:9:107:10:15:11:12:14:0:15:0:16:28133822:3:142375:6:183:42:4 months|1:KobaGD:2:14683518:9:98:10:5:11:3:14:0:15:2:16:4948816:3:142433:6:184:42:6 months|1:lolorob7017:2:218382479:9:18:10:12:11:15:14:6:15:0:16:26573344:3:142550:6:185:42:6 months|1:J1oToS:2:27003063:9:16:10:20:11:23:14:0:15:2:16:8261398:3:142575:6:186:42:3 months|1:Uuniverse:2:184882843:9:32:10:15:11:8:14:2:15:2:16:21212394:3:142604:6:187:42:6 months|1:mrstealuroculus:2:116744714:9:1:10:9:11:12:14:7:15:0:16:15983207:3:142800:6:188:42:6 months|1:DenDanDen:2:93090716:9:108:10:98:11:12:14:0:15:2:16:10052796:3:142825:6:189:42:5 months|1:kirbycube:2:109802289:9:18:10:74:11:40:14:7:15:0:16:10858186:3:143079:6:190:42:6 months|1:KvasirGD:2:239251151:9:1:10:0:11:3:14:0:15:0:16:26840022:3:143533:6:191:42:6 months|1:5Live:2:229674653:9:1:10:0:11:3:14:6:15:0:16:25731871:3:143854:6:192:42:4 months|1:GDshowcased:2:230995371:9:483:10:16:11:1:14:0:15:0:16:25656277:3:143883:6:193:42:2 months|1:N4thack3r:2:44243030:9:3:10:54:11:12:14:6:15:2:16:10306571:3:143970:6:194:42:3 months|1:Rakks:2:5192461:9:28:10:89:11:17:14:5:15:0:16:251518:3:144158:6:195:42:6 months|1:TrevellGDT:2:236658582:9:482:10:12:11:18:14:0:15:2:16:26418824:3:144425:6:196:42:3 months|1:spoakygd:2:212891504:9:11:10:12:11:78:14:6:15:0:16:24846941:3:144441:6:197:42:4 months|1:FlyingKnife:2:31122725:9:3:10:103:11:19:14:8:15:2:16:6396462:3:144812:6:198:42:6 months|1:SuperAppleMeow:2:238149034:9:5:10:9:11:11:14:8:15:2:16:27223906:3:144925:6:199:42:3 months|1:RelaxUndertale:2:194073544:9:2:10:12:11:19:14:0:15:0:16:21544523:3:145016:6:200:42:1 month +``` + + diff --git a/docs/endpoints/levels/getGJLevels21.md b/docs/endpoints/levels/getGJLevels21.md new file mode 100644 index 000000000..74ff72ac5 --- /dev/null +++ b/docs/endpoints/levels/getGJLevels21.md @@ -0,0 +1,174 @@ +# getGJLevels21 + +> This endpoint is used to search levels by name and or filter + +## Parameters + +| Parameter | Description | Required | +| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------- | +| `secret` | [Common secret](/reference/secrets.md) `Wmfd2893gb7` | Yes | +| `gameVersion` | [Game Version](#), 22 on 2.2 | | +| `binaryVersion` | [Binary Version](#), 42 on 2.206 | | +| `type` | Search type, [see values](#type). Defaults to most liked | | +| `str` | Search query, user ID or level list depending on `type` | Only on types 5 and 10 | +| `page` | Which page to request, defaults to 0 | | +| `total` | Currently unknown. Defaults to the amount of levels available up to 9999, but 0 or leaving it out also work. | | +| `gjp` | The [GJP](/topics/encryption/gjp.md) for the `accountID`. Not present in 2.2 | | +| `gjp2` | The [GJP2](/topics/encryption/gjp.md) for the `accountID`. 2.2+ | | +| `accountID` | The ID for the account which is doing the search. Only sent on types 8, 13 and 5 | | +| `gdw` | Whether the request is for GD World, either 0 or 1 | | +| `gauntlet` | The ID for the gauntlet being requested | | +| `diff` | Difficulty filter, [see values](#diff) | | +| `demonFilter` | Selects which demon difficulty to search, [see values](#demonFilter). If not sent will search all demon types | | +| `len` | Level length, [see values](#len) | | +| `uncompleted` | Uncompleted filter, either 0 or 1 | | +| `onlyCompleted` | Completed filter, either 0 or 1 | | +| `completedLevels` | Comma separated list of completed level IDs surrounded by `()` | Only when using (un)completed filters | +| `featured` | Featured filter, either 0 or 1 | | +| `original` | Original filter, either 0 or 1 | | +| `twoPlayer` | Two Player Mode filter, either 0 or 1 | | +| `coins` | Coins filter, either 0 or 1 | | +| `epic` | Epic filter, either 0 or 1 | | +| `legendary` | Legendary filter, either 0 or 1. | | +| `mythic` | Mythic filter, either 0 or 1. | | +| `noStar` | Unrated filter, either 0 or 1 | | +| `star` | Rated filter, either 0 or 1 | | +| `song` | Official (or custom) song ID, not sent when off | | +| `customSong` | Newgrounds song, 1 when on and uses `song` as the ID, not sent when off | | +| `followed` | Comma separated list of the followed user IDs | Only on type 12 | +| `local` | Whether to fetch "My Levels", either 0 or 1 | Only on type 5 | +| `udid` | Your [UDID](/topics/encryption/id.md) | | +| `uuid` | Your [UUID](/topics/encryption/id.md) | | +
+ type + +| Type | Description | +| :--: | --------------------------------------------------------------------------------- | +| 0 | Search query | +| 1 | Most downloaded | +| 2 | Most liked | +| 3 | Trending | +| 4 | Recent | +| 5 | User's levels, uses `str` as the **user ID** | +| 6 | Featured | +| 7 | Magic | +| 8 | Moderator sent levels | +| 10 | List of levels (not to be confused with type 25, which is for in-game lists), uses `str` as a comma separated list of level IDs | +| 11 | Awarded | +| 12 | Followed (see `followed` parameter) | +| 13 | Friends (login required) | +| 15 | Most liked in GD World | +| 16 | Hall of fame | +| 17 | Featured in GD World | +| 18 | Unknown (always empty, perhaps robtop only?) | +| 19 | Unknown (same as type 10 but this type has pagination and no star rate filter) | +| 21 | Daily history | +| 22 | Weekly history | +| 25 | Level list, uses `str` as the list ID | +| 26 | Unknown (same as type 19 but each page has up to 100 levels instead of 10 on it) | + +
+ +
+ diff + +| diff | Description | +| :--: | ---------------------------------------------------------- | +| -1 | N/A | +| -2 | Demons (see `demonFilter` for specifying demon difficulty) | +| 1 | Easy | +| 2 | Normal | +| 3 | Hard | +| 4 | Harder | +| 5 | Insane | + +
+ +
+ demonFilter + +| demonFilter | Description | +| :---------: | ------------- | +| 1 | Easy demon | +| 2 | Medium demon | +| 3 | Hard demon | +| 4 | Insane demon | +| 5 | Extreme demon | + +
+ +
+ len + +| len | Value | +| :-: | ------ | +| 0 | Tiny | +| 1 | Short | +| 2 | Medium | +| 3 | Long | +| 4 | XL | +| 5 | Platformer | + +
+ +## Response + +A successful level search will return the data in the format + +``` +levels#creators#songs#page info#hash +``` + +where: + +- `levels` is a list of [Level Objects](/resources/server/level.md) separated by `|` +- `creators` is a list of creators separated by `|`, each in the format `userID:username:accountID` +- `songs` is a list of [Song Objects](/resources/server/song.md) separated by `:` +- `page info` is in the format `total:offset:amount` where: + - `total` - total number of levels for the query + - `offset` - offset from which the current page starts + - `amount` - number of levels per page (always 10 for the gd servers) +- `hash` is a hash of every level, see [Hashes](/resources/server/hashes.md?id=getgjlevels) + + + +If the request is not successful, it will return `-1` + + + + +### **Python** + +```py +import requests + +headers = { + "User-Agent": "" +} + +data = { + "str": "bloodbath", + "star": 1, + "type": 0, + "secret": "Wmfd2893gb7", +} + +url = "http://www.boomlings.com/database/getGJLevels21.php" + +req = requests.post(url=url, data=data, headers=headers) +print(req.text) +``` + +### **curl** + +```plain +curl http://www.boomlings.com/database/getGJLevels21.php -A "" -d "str=bloodbath&star=1&type=0&secret=Wmfd2893gb7" +``` + + + +### Output + +``` +1:10565740:2:Bloodbath:5:3:6:503085:8:10:9:50:10:44138442:12:0:13:21:14:2375318:17:1:43:6:25::18:10:19:10330:42:0:45:24746:3:V2hvc2UgYmxvb2Qgd2lsbCBiZSBzcGlsdCBpbiB0aGUgQmxvb2RiYXRoPyBXaG8gd2lsbCB0aGUgdmljdG9ycyBiZT8gSG93IG1hbnkgd2lsbCBzdXJ2aXZlPyBHb29kIGx1Y2suLi4=:15:3:30:7679228:31:0:37:0:38:0:39:0:46:1:47:2:35:467339|1:21761387:2:Bloodbath Z:5:1:6:3277407:8:10:9:20:10:4556435:12:0:13:20:14:200369:17:1:43:4:25::18:10:19:17840:42:0:45:0:3:UmVtYWtlIG9mIEJCLCBidXQgU2hvcnRlciBhbmQgbXVjaCBlYXNpZXIgWEQgTW9yZSBvZiBhIGdhbWVwbGF5IGxldmVsISAgSnVzdCBhIGZ1biBlYXN5IGRlbW9uLiBWZXJpZmllZCBCeSBYaW9kYXplciEgRW5qb3kgOkQ=:15:3:30:0:31:0:37:3:38:1:39:10:46:1:47:2:35:223469|1:64968478:2:Bloodbath but no:5:1:6:19747356:8:10:9:50:10:652130:12:0:13:21:14:51559:17::43:6:25::18:8:19:24992:42:0:45:23233:3:Qmxvb2RiYXRoLCBJdCdzIG5vdCBldmVuIHRoaXM=:15:3:30:0:31:0:37:0:38:1:39:8:46:1:47:2:35:706340|1:75795864:2:Bloodbath:5:2:6:12348083:8:10:9:40:10:88763:12:0:13:21:14:4326:17::43:5:25::18:7:19:25025:42:0:45:55947:3:VGhhbmtzIHRvIGV2ZXJ5b25lIGluIG15IGRpc2NvcmQgc2VydmVyIHRoYXQgY29udHJpYnV0ZWQ=:15:3:30:0:31:0:37:0:38:1:39:6:46:1:47:2:35:513064#503085:Riot:37415|3277407:Zyzyx:88354|12348083:KNOEPPEL:3009121|19747356:Texic:6152129#1~|~223469~|~2~|~ParagonX9 - HyperioxX~|~3~|~31~|~4~|~ParagonX9~|~5~|~3.77~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F223000%2F223469_ParagonX9___HyperioxX.mp3~|~7~|~~|~8~|~1~:~1~|~467339~|~2~|~At the Speed of Light~|~3~|~52~|~4~|~Dimrain47~|~5~|~9.56~|~6~|~~|~10~|~http%3A%2F%2Fgeometrydashcontent.com%2Fsongs%2F467339.mp3~|~7~|~~|~8~|~1~:~1~|~513064~|~2~|~EnV - Uprise~|~3~|~149~|~4~|~Envy~|~5~|~8.71~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F513000%2F513064_EnV---Uprise.mp3~|~7~|~UCaRqE7rKwJl1BvMRU4FFVJQ~|~8~|~1~:~1~|~706340~|~2~|~-At the Speed of Light- (8 bit Remix)~|~3~|~46724~|~4~|~ThaPredator~|~5~|~4.78~|~6~|~~|~10~|~http%3A%2F%2Faudio.ngfiles.com%2F706000%2F706340_-At-the-Speed-of-Light--8-.mp3~|~7~|~~|~8~|~1#4:0:10#1664b8bb919b0822a4408752c37a9fb5f651f813 +``` diff --git a/docs/endpoints/getGJMapPacks21.md b/docs/endpoints/levels/getGJMapPacks21.md similarity index 77% rename from docs/endpoints/getGJMapPacks21.md rename to docs/endpoints/levels/getGJMapPacks21.md index 8f9de4fea..3c25fb254 100644 --- a/docs/endpoints/getGJMapPacks21.md +++ b/docs/endpoints/levels/getGJMapPacks21.md @@ -10,9 +10,9 @@ Gets the map packs. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -20,7 +20,15 @@ Gets the map packs. ## Response -A list of [map pack](/resources/server/mappack.md) objects, separated by a pipe `|`. +The response is formatted as follows: + +`packs#page#hash` + +where: + +- Packs is a list of [map pack](/resources/server/mappack.md) objects, separated by a pipe `|` +- Page is the page data in this format: `{total packs}:{current offset}:{page size}` +- [Hash](/resources/server/hashes.md?id=getgjmappacks) used to validate the request by the GD client ## Example @@ -47,4 +55,4 @@ print(req.text) 1:59:2:Cyclone Pack:3:1566116,946020,1100161:4:8:5:1:6:5:7:255,75,255:8:255,75,255|1:60:2:Colossus Pack:3:1350389,1215630,1724579:4:8:5:1:6:5:7:100,255,175:8:100,255,175|1:61:2:Diamond Pack:3:1267316,1670283,1205277:4:8:5:1:6:5:7:255,255,255:8:255,255,255|1:11:2:Chaos Pack:3:329929,188909,340602:4:9:5:1:6:5:7:255,125,0:8:255,125,0|1:44:2:Magma Pack:3:882417,884256,551979:4:9:5:1:6:5:7:255,100,50:8:255,100,50|1:62:2:Paradox Pack:3:1447246,1132530,1683722:4:9:5:1:6:5:7:50,255,75:8:50,255,75|1:63:2:Funky Pack:3:1728550,1799065,1311773:4:9:5:1:6:5:7:50,175,255:8:50,175,255|1:19:2:Remix Pack 4:3:341613,358750,369294:4:10:5:2:6:6:7:255,255,0:8:255,255,0|1:20:2:Demon Pack 1:3:70059,10109,135561:4:10:5:2:6:6:7:255,0,125:8:255,0,125|1:21:2:Demon Pack 2:3:57730,308891,102765:4:10:5:2:6:6:7:255,0,0:8:255,0,0#65:40:10#79c437d2cf75d2edf36a5094e0cc650c54440ba3 ``` - \ No newline at end of file + diff --git a/docs/endpoints/rateGJDemon21.md b/docs/endpoints/levels/rateGJDemon21.md similarity index 95% rename from docs/endpoints/rateGJDemon21.md rename to docs/endpoints/levels/rateGJDemon21.md index c251ddca2..391e27791 100644 --- a/docs/endpoints/rateGJDemon21.md +++ b/docs/endpoints/levels/rateGJDemon21.md @@ -6,9 +6,9 @@ Rates the demon difficulty of a demon level - only works for Geometry Dash moder ### Required Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **secret** - Wmfp3897gc3 @@ -88,4 +88,4 @@ print(req.text) 4284013 ``` - \ No newline at end of file + diff --git a/docs/endpoints/levels/rateGJStars211.md b/docs/endpoints/levels/rateGJStars211.md new file mode 100644 index 000000000..30eaa5a71 --- /dev/null +++ b/docs/endpoints/levels/rateGJStars211.md @@ -0,0 +1,63 @@ +# rateGJStars211.php + +Sends a star suggestion for a level + +## Parameters + +### Required Parameters + +**secret** - Wmfd2893gb7 + +**levelID** - The ID of the level receiving the star suggestion + +**stars** - The amount of stars being suggested + +**rs** - 10 randomly generated characters from `[A-Za-z0-9]` + +**accountID** - Your account ID + +**gjp2** - Your password, encrypted with [GJP2](/topics/gjp.md) + +**udid** - Your UDID + +**uuid** - Your player ID (different from account ID) + +**chk** - [See here](/topics/encryption/chk.md?id=rate) + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +## Response + +Always 1 if the parameters `secret`, `levelID` and `stars` are specified. `chk`, `udid`, `uuid`, `rs`, `accountID` and `gjp2` are technically not required but the star rating will not go through if these parameters aren't specified + +## Example + + + +### **Python** + +```py +import requests + +data = { + "secret": "Wmfd2893gb7", + "levelID": 15254724, + "stars": 3 +} + +req = requests.post('http://boomlings.com/database/rateGJStars211.php', data=data) +print(req.text) +``` + +**Response** +```py +1 +``` + + diff --git a/docs/endpoints/reportGJLevel.md b/docs/endpoints/levels/reportGJLevel.md similarity index 100% rename from docs/endpoints/reportGJLevel.md rename to docs/endpoints/levels/reportGJLevel.md diff --git a/docs/endpoints/suggestGJStars.md b/docs/endpoints/levels/suggestGJStars.md similarity index 86% rename from docs/endpoints/suggestGJStars.md rename to docs/endpoints/levels/suggestGJStars.md index d8a1acc16..42a7d39c0 100644 --- a/docs/endpoints/suggestGJStars.md +++ b/docs/endpoints/levels/suggestGJStars.md @@ -1,4 +1,4 @@ -# suggestGJStars.md +# suggestGJStars20.php Endpoint used by moderators to send levels to RobTop @@ -20,7 +20,7 @@ Endpoint used by moderators to send levels to RobTop **binaryVersion** - the binary version -**feature** - 1 for feature, 0 for star rate +**feature** - 0 for star rate, 1 for feature, 2 for epic, 3 for legendary, 4 for mythic **gdw** - 0 @@ -48,7 +48,7 @@ data = { "secret": "Wmfp3879gc3" } -req = requests.post("http://boomlings.com/database/suggestGJStars.php", data=data) +req = requests.post("http://boomlings.com/database/suggestGJStars20.php", data=data) print(req.text) ``` @@ -56,4 +56,4 @@ print(req.text) **Response** ```py 1 -``` \ No newline at end of file +``` diff --git a/docs/endpoints/updateGJDesc20.md b/docs/endpoints/levels/updateGJDesc20.md similarity index 88% rename from docs/endpoints/updateGJDesc20.md rename to docs/endpoints/levels/updateGJDesc20.md index e8cfd1974..c3ed04210 100644 --- a/docs/endpoints/updateGJDesc20.md +++ b/docs/endpoints/levels/updateGJDesc20.md @@ -8,7 +8,7 @@ Updates the description of a level **accountID** - The account ID of the level's author -**gjp** - The [GJP](/topics/encryption/gjp.md) of the level's author +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the level's author **levelID** - The ID of the level @@ -18,9 +18,9 @@ Updates the description of a level ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 diff --git a/docs/endpoints/uploadGJLevel21.md b/docs/endpoints/levels/uploadGJLevel21.md similarity index 60% rename from docs/endpoints/uploadGJLevel21.md rename to docs/endpoints/levels/uploadGJLevel21.md index 74df84c41..1bf23f058 100644 --- a/docs/endpoints/uploadGJLevel21.md +++ b/docs/endpoints/levels/uploadGJLevel21.md @@ -6,11 +6,11 @@ Uploads a created level to the servers. ### Required Parameters -**gameVersion** - The game version. Currently 21 +**gameVersion** - The game version. Currently 22 **accountID** - The uploader's account ID -**gjp** - The uploader's [GJP](./topics/encryption/gjp.md) +**gjp2** - The uploader's [GJP2](./topics/encryption/gjp.md) **userName** - The uploader's username @@ -22,9 +22,9 @@ Uploads a created level to the servers. **levelVersion** - The version number of the level -**levelLength** - The length of the level as a number, where 0 is tiny and 4 is XL +**levelLength** - The length of the level as a number, where 0 is tiny, 4 is XL and 5 is Platformer -**audioTrack** - The [official song number](./reference.md) used in the level. Set to 0 if a newgrounds song is used +**audioTrack** - The [official song number](/reference/songs?id=table-of-official-songs) used in the level. Set to 0 if a newgrounds song is used **auto** - Unknown (0) @@ -42,7 +42,7 @@ Uploads a created level to the servers. **requestedStars** - The requested star rating for the level. The number is not limited to 10 -**unlisted** - Set to 1 if the level should be unlisted and only viewable by friends +**unlisted** - Set to 2 if the level should be unlisted and to 1 if the level should be only viewable by friends **ldm** - Set to 1 if the level should have a low detail checkbox @@ -64,7 +64,7 @@ Uploads a created level to the servers. **levelInfo** - A random gzip compressed string -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -87,39 +87,46 @@ import requests levelString = "H4sIAAAAAAAAC6WQwQ3DIAxFF3IlfxsIUU6ZIQP8AbJChy_GPSZqpF7-A4yfDOfhXcCiNMIqnVYrgYQl8rDwBTZCVbkQRI3oVHbiDU6F2jMF_lesl4q4kw2PJMbovxLBQxTpM3-I6q0oHmXjzx7N0240cu5w0UBNtESRkble8uSLHjh8nTubmYJZ2MvMrEITEN0gEJMxlLiMZ28frmj" data = { - "gameVersion": 21, - "accountID": 173831, # This is DevExit's account ID - "gjp": "*******", # This would be DevExit's password encoded with GJP encryption - "userName": "devexit", - "levelID": 0, - "levelName": "Test", # The level name is Test - "levelDesc": "QSB0ZXN0IGxldmVsIGZvciB0aGUgR0QgRG9jcyE", # "A test level for the GD Docs!" - "levelVersion": 1, - "levelLength": 0, - "audioTrack": 0, # This uses a newgrounds song - "auto": 0, - "password": 314159, - "original": 55610687, - "twoPlayer": 0, - "songID": 546561, # NK - Jawbreaker - "objects": 1, - "coins": 0, - "requestedStars": 50, - "unlisted": 1, # This level is unlisted, but does exist! - "ldm": 0, - "levelString": levelString, # The level string for the level described above - "seed2": generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg"), # This is talked about in the CHK encryption, - "secret": "Wmfd2893gb7" + "gameVersion": 21, + "accountID": 173831, # This is DevExit's account ID + "gjp": "*******", # This would be DevExit's password encoded with GJP encryption + "userName": "devexit", + "levelID": 0, + "levelName": "Test", # The level name is Test + "levelDesc": "QSB0ZXN0IGxldmVsIGZvciB0aGUgR0QgRG9jcyE", # "A test level for the GD Docs!" + "levelVersion": 1, + "levelLength": 0, + "audioTrack": 0, # This uses a newgrounds song + "auto": 0, + "password": 314159, + "original": 55610687, + "twoPlayer": 0, + "songID": 546561, # NK - Jawbreaker + "objects": 1, + "coins": 0, + "requestedStars": 50, + "unlisted": 1, # This level is unlisted, but does exist! + "ldm": 0, + "levelString": levelString, # The level string for the level described above + "seed2": generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg"), # This is talked about in the CHK encryption, + "secret": "Wmfd2893gb7" } -req = requests.post("http://boomlings.com/database/uploadGJLevel21.php", data=data) +headers = { + "User-Agent": "" +} + +url = "http://www.boomlings.com/database/uploadGJLevel21.php" + +req = requests.post(url=url, data=data, headers=headers) print(req.text) ``` + + **Response** -```py +```plain 62687277 ``` - diff --git a/docs/endpoints/lists/deleteGJLevelList.md b/docs/endpoints/lists/deleteGJLevelList.md new file mode 100644 index 000000000..c09c66f67 --- /dev/null +++ b/docs/endpoints/lists/deleteGJLevelList.md @@ -0,0 +1,61 @@ +# deleteGJLevelList.php + +Deletes a list from the server. + +## Parameters + +### Required Parameters + +**accountID** - The list author's account ID + +**udid** - The list author's UDID + +**uuid** - The list author's UUID + +**gjp2** - The level author's [GJP2](/topics/encryption/gjp.md) + +**listID** - The ID of the level being deleted + +**secret** - Wmfv2898gc9 + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +## Response + +Returns 1 if deleted, -1 if it failed or the list does not exist. + +## Example + + + +### **Python** + +```py +import requests + +# With this code, DevExit is deleting the list with ID 414808 +headers = { + "User-Agent": "" +} + +data = { + "accountID": 173831, # DevExit's account ID + "gjp2": "********", # This would be DevExit's password encoded with GJP2 encryption + "listID": 414808, + "secret": "Wmfv2898gc9" +} + +req = requests.post("http://www.boomlings.com/database/deleteGJLevelList.php", data=data, headers=headers) +print(req.text) +``` + +**Response** +```py +1 +``` + + diff --git a/docs/endpoints/lists/getGJLevelLists.md b/docs/endpoints/lists/getGJLevelLists.md new file mode 100644 index 000000000..8b01bea93 --- /dev/null +++ b/docs/endpoints/lists/getGJLevelLists.md @@ -0,0 +1,142 @@ +# getGJLevelLists + +> This endpoint is used to search level lists by name and / or filter + +## Parameters + +| Parameter | Description | Required | +| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------- | +| `secret` | [Common secret](/reference/secrets.md) `Wmfd2893gb7` | Yes | +| `gameVersion` | [Game Version](#), 22 on 2.2 | | +| `binaryVersion` | [Binary Version](#), 42 on 2.206 | | +| `type` | Search type, [see values](#type). Defaults to most liked | | +| `str` | Search query, user ID or level list depending on `type` | Only on types 5 and 10 | +| `page` | Which page to request, defaults to 0 | | +| `gjp2` | The [GJP2](/topics/encryption/gjp.md) for the `accountID`. | | +| `accountID` | The ID for the account which is doing the search. Only sent on types 8, 13 and 5 | | +| `diff` | Difficulty filter, [see values](#diff) | | +| `demonFilter` | Selects which demon difficulty to search, [see values](#demonFilter). If not sent will search all demon types | | +| `star` | Rated filter, either 0 or 1 | | +| `followed` | Comma separated list of the followed user IDs | Only on type 12 | +| `udid` | Your [UDID](/topics/encryption/id.md) | | +| `uuid` | Your [UUID](/topics/encryption/id.md) | | +
+ type + +| Type | Description | +| :--: | --------------------------------------------------------------------------------- | +| 0 | Search query | +| 1 | Most downloaded | +| 2 | Most liked | +| 3 | Trending | +| 4 | Recent | +| 5 | User's lists, uses `str` as the **account ID** | +| 6 | Lists button | +| 7 | Magic (returns the same levels as most liked) | +| 11 | Awarded | +| 12 | Followed (see `followed` parameter) | +| 13 | Friends (login required) | +| 27 | Sent lists | + +
+ +
+ diff + +| diff | Description | +| :--: | ---------------------------------------------------------- | +| -1 | N/A | +| -2 | Demons (see `demonFilter` for specifying demon difficulty) | +| 1 | Easy | +| 2 | Normal | +| 3 | Hard | +| 4 | Harder | +| 5 | Insane | + +
+ +
+ demonFilter + +| demonFilter | Description | +| :---------: | ------------- | +| 1 | Easy demon | +| 2 | Medium demon | +| 3 | Hard demon | +| 4 | Insane demon | +| 5 | Extreme demon | + +
+ +
+ len + +| len | Value | +| :-: | ------ | +| 0 | Tiny | +| 1 | Short | +| 2 | Medium | +| 3 | Long | +| 4 | XL | +| 5 | Platformer | + +
+ +## Response + +A successful list search will return the data in the format + +``` +lists#creators#page info#hash +``` + +where: + +- `lists` is a list of [List Objects](/resources/server/list.md) separated by `|` +- `creators` is a list of creators separated by `|`, each in the format `userID:username:accountID` +- `page info` is in the format `total:offset:amount` where: + - `total` - total number of levels for the query + - `offset` - offset from which the current page starts + - `amount` - number of levels per page (always 10 for the gd servers) +- `hash` is a hash of every level, see [Hashes](#) + + +If the request is not successful, it will return `-1` + + + + +### **Python** + +```py +import requests + +headers = { + "User-Agent": "" +} + +data = { + "str": "my mcdonalds order", + "type": 0, + "secret": "Wmfd2893gb7", +} + +url = "http://www.boomlings.com/database/getGJLevelLists.php" + +req = requests.post(url=url, data=data, headers=headers) +print(req.text) +``` + +### **curl** + +```plain +curl http://www.boomlings.com/database/getGJLevelLists.php -A "" -d "str=my mcdonalds order&type=0&secret=Wmfd2893gb7" +``` + + + +### Output + +```plain +1:10834:2:my mcdonalds order:3:aSB3YWxrZWQgdG8gbWNkb25hbGRz:5:1:49:6061424:50:tricipital:10:143479:7:10:14:5334:19::51:78111123,80840474,20556675,71480069,51008389,26108947,59604964,190626,53898587,72443435,47499900,89638158,43758774,90640638,96282081,86742812:55:0:56:0:28:1703082168:29:0|1:4406:2:my mcdonalds order:3:aW0gYXQgdGhlIGRyaXZlIHRocnUgLy8gcGFydCAyIG91dCBub3cgYXQgNzIxMTQ=:5:7:49:17062290:50:GD2G:10:90039:7:2:14:4457:19::51:25147297,82785965,11171792,30261946,31496121,71189946,19716898,14456417,4050125,79412478,1442329,67287373,61350256:55:0:56:0:28:1703048969:29:1703631457|1:22325:2:My McDonalds Order:3:YmFuZ2VyIGxpc3QgaWNs:5:1:49:10722530:50:Jexamania:10:8469:7:9:14:222:19::51:25147297,83671207,46104803,72867858,65500565,16683338,32666588,25717922,84904479,92727112,69043485,4846999,58038680,67287373:55:0:56:0:28:1703125026:29:0|1:72114:2:my mcdonalds order 2:3:aSB0aGluayBoZSBsaWVkIGFib3V0IHRoZSBtYWNoaW5lIGJlaW5nIGJyb2tlbiAvLyB0aGUgc2VxdWVsIG5vIG9uZSBhc2tlZCBmb3IgdG8gbXkgbWNkb25hbGRzIG9yZGVy:5:3:49:17062290:50:GD2G:10:4732:7:2:14:156:19::51:25147297,65037091,88758014,59604964,67993675,59966737,56102262,97933043,2056444,85312317,91482208,58038680,46160451,75940156,95959832,81466909,68064189,61350256:55:0:56:0:28:1703631267:29:1703638822|1:16619:2:my mcdonalds order:3:SSBLTk9XIElUJ1MgQkFELCBUSEVSRSBXRVJFTidUIE1BTlkgT1BUSU9OUyE=:5:1:49:14542509:50:ZohMyGoodnessGD:10:2652:7:6:14:44:19::51:47499900,57410100,57474996,84904479,38557238,59157328,87981410,60001202,77367261,41551990,67287373:55:0:56:0:28:1703101621:29:0|1:12460:2:My McDonalds Order:3:bWNkb25hbA==:5:1:49:4236858:50:tim55:10:1198:7:4:14:32:19::51:82931130,69309640,56455492,36619357,55320441:55:0:56:0:28:1703087812:29:0|1:29628:2:My mcdonalds order:3:YmFzZWQgb2ZmIGEgc3BvdGlmeSBwbGF5bGlzdCBvciBzb21ldGhpbmc=:5:3:49:10055542:50:RuSsiaNrobToP:10:1254:7:10:14:17:19::51:78111123,34889235,80840474,11171792,71480069,84904479,97589710,443669,4460853,72443435,97590104,13550658,2056444,86742812:55:0:56:0:28:1703180209:29:1703614964|1:45077:2:My McDonalds Order:3:UmVhbA==:5:1:49:1839061:50:CreatorFreeze:10:503:7:5:14:17:19::51:80840474,30963660,79412478,14456417,61293573,71971062,86929245,86742812,58038680:55:0:56:0:28:1703307069:29:0|1:33953:2:My mcdonalds order:3::5:3:49:14092610:50:RealGDVerse:10:347:7:2:14:16:19::51:82785965,11012303,38557238,18931295,11255719,61408958,26162113,72014001,42633903,13752832,69768064,49186967,77554979,22294605,70549760,66416136,50525701,89933948,37039661,58976282,6988127,86449162,3134009,87727825,82995551,73004601,57585857,82139948,95318968,69925593,1203245,69765381,50647963,67254591,14370474,96968200,93201502,92509265,65430141,56495221,76489404,91963243,41035356,62427241,68752244,69996378,26880009,7360312,79013891,65269818,76963460,88732322,11171792:55:0:56:0:28:1703206770:29:1703428545|1:68104:2:My McDonalds Order:3:TXkgcmVhbCBNY0RvbmFsZHMgb3JkZXIu:5:1:49:25642445:50:Anjixdude25z:10:393:7:9:14:12:19::51:78111123,59413153,12664426,97172976,87995257,85508683,89490621,72811779,10558915,9007089,84904479,27742076,61718673,27090448:55:0:56:0:28:1703571483:29:0#10532982:CreatorFreeze:1839061|14827098:tim55:4236858|15479163:tricipital:6061424|92900676:RuSsiaNrobToP:10055542|98535835:Jexamania:10722530|139957548:RealGDVerse:14092610|147859835:ZohMyGoodnessGD:14542509|25220930:GD2G:17062290|221186876:Anjixdude25z:25642445#9999:0:10#f5da5823d94bbe7208dd83a30ff427c7d88fdb99 +``` diff --git a/docs/endpoints/lists/uploadGJLevelList.md b/docs/endpoints/lists/uploadGJLevelList.md new file mode 100644 index 000000000..70aacb608 --- /dev/null +++ b/docs/endpoints/lists/uploadGJLevelList.md @@ -0,0 +1,94 @@ +# uploadGJLevel21.php + +Uploads a created level to the servers. + +## Parameters + +### Required Parameters + +**gameVersion** - The game version. Currently 22 + +**accountID** - The uploader's account ID + +**gjp2** - The uploader's [GJP2](./topics/encryption/gjp.md) + +**listID** - The ID of the list if updating to a newer version, otherwise 0 + +**listName** - The name of the list, in plain text + +**listDesc** - The description of the list, in [URL-safe base64](./topics/encryption/base64.md) + +**listVersion** - The version number of the level + +**original** - The ID of the original list if the list was copied, otherwise 0 + +**difficulty** - The list's difficulty face. -1 is N/A, and then it's 0-10 where 0 is Auto and 10 is Extreme Demon + +**unlisted** - Set to 2 if the list should be unlisted, and to 1 if the list should be unlisted and only viewable by friends + +**listLevels** - All level IDs included in the list. Each ID is separated by `,` + +**seed** - A mandatory [chk value](./topics/encryption/chk.md) generated from the first 50 characters of the listLevels parameter, followed by the accountID. TODO!!! + +**seed2** - 5 randomly generated characters from \[A-Za-z0-9] + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**binaryVersion** - Currently 42 as of 2.206 + +## Response + +Returns the ID of the uploaded list, or `-1` if the request was rejected. + +## Example + + + +### **Python** + +```py +import requests +import random +from string import ascii_letters, digits # so we don't have to type [A-Za-z0-9] by hand + +possible_letters = ascii_letters + digits +seed = ("").join(random.choices(possible_letters, k=5)) + +data = { + "gameVersion": 22, + "accountID": 173831, # This is DevExit's account ID + "gjp": "*******", # This would be DevExit's password encoded with GJP encryption + "listLevels": "128,132,133,134,136", # These are the IDs of the levels in the list + "listID": 0, + "listName": "First Levels", # This is the list name + "listDesc": "QSB0ZXN0IGxpc3QgZm9yIHRoZSBHRCBEb2NzIQ==", # "A test list for the GD Docs!" + "listVersion": 0, + "original": 0, + "difficulty": 3, # This indicates a Hard difficulty face + "unlisted": 2, # This list is unlisted, but does exist! + "levelString": levelString, # The level string for the level described above + "seed": seed, + "seed2": '', # TODO, + "secret": "Wmfd2893gb7" +} + +headers = { + "User-Agent": "" +} + +url = "https://www.boomlings.com/database/uploadGJLevelList.php" + +req = requests.post(url=url, data=data, headers=headers) +print(req.text) +``` + + + +**Response** + +```plain +297650 +``` + diff --git a/docs/endpoints/getAccountURL.md b/docs/endpoints/misc/getAccountURL.md similarity index 58% rename from docs/endpoints/getAccountURL.md rename to docs/endpoints/misc/getAccountURL.md index 4d83a658d..100b8d709 100644 --- a/docs/endpoints/getAccountURL.md +++ b/docs/endpoints/misc/getAccountURL.md @@ -1,12 +1,12 @@ # getAccountURL.php -Gets the URL for the data server. Official domain name is [http://geometrydash.com](http://geometrydash.com), but the server returns [http://69.164.210.48](http://69.164.210.48). +Gets the URL for the data server. ## Parameters ### Required Parameters -**accountID** - Anyone's account ID +**accountID** - Any valid account ID **type** - used to decide which endpoint is used after the data server is found - 1 = backup data/ 2 = sync data @@ -14,7 +14,7 @@ Gets the URL for the data server. Official domain name is [http://geometrydash.c ## Response -[http://69.164.210.48](http://69.164.210.48) +Usually [https://www.robtopgames.org](https://www.robtopgames.org). However, without a valid accountID and at type 2, [https://www.robtopgames.net](https://www.robtopgames.net) is returned instead ## Example @@ -26,7 +26,7 @@ Gets the URL for the data server. Official domain name is [http://geometrydash.c import requests data = { - "accountID": 173831, + "accountID": 71, "type": 2, "secret": "Wmfd2893gb7" } @@ -37,7 +37,7 @@ print(req.text) **Response** ```py -http://69.164.210.48 +https://www.robtopgames.net ``` - \ No newline at end of file + diff --git a/docs/endpoints/getSaveData.md b/docs/endpoints/misc/getSaveData.md similarity index 99% rename from docs/endpoints/getSaveData.md rename to docs/endpoints/misc/getSaveData.md index 55a3deebd..c0f59174b 100644 --- a/docs/endpoints/getSaveData.md +++ b/docs/endpoints/misc/getSaveData.md @@ -10,15 +10,15 @@ Unknown ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 ## Response -Returns seemingly random [urlsafe base64](/topics/encryption/base64.md) encoded text +Used to return seemingly random [urlsafe base64](/topics/encryption/base64.md) encoded text, but as of 2.2 just returns nothing. The base64 text can be found below ## Example @@ -42,4 +42,4 @@ print(req.text) UmysuWpDzKaglTFZR65eeJxEnVei5DoOQzd0P2xl7X9jzQPA1ZOex9dlK1CMIPm2_r5jnL_597Z29xntb9Vlf29v-7tcbf5dLsd7zvk7XD7rzP63ddlX63_vo1fMOa5-1859n_03uKxH5vDbev2fqVe8e-0x8uXR-_LlrOFMvfjdp97hl627dy7b80x_oj9ttlVDO-M-47xcnfd5nsWf37Xa24-f5AvjN7V3_HXN5-z3zeWt2xptf56ahu-ufv2yGsHcZ3mWoz3t1cM1hzGaHn536-edPPzM-rhGfu5aI_OtNajRefVmZwx-2bP3eTT3d549M8t7dp_1ifvW43v-tfpC_XzUMtUb6tl3ZUH6O98agl72zvZkpUerb18t5HPedpp_N976Hz_w3H6z3fveN6_ojQ1nPO-9o_ZArxjPu2em_9x9m99cDz_9avBPZ_6-bKsW_O_VTGuJv2fbs9jvIrN3j62XvTW3WmV-9tRKvN-cavZP42e1kLVwJojxzpGZvmOde59aoDbq0Tm-oZ07dx4es32XczdT2iiqmMf7_NbK5Xe1G3OHhsc87fWJGGO9W4Mrwu3nud875s0xqFOyV9a7jZtX9HVqZn54v4N7_Kd2tNZDPxy1l_3ROCDC4QP23FWnMcM446ycsLft0bNpfdSlTuOZRV56G_RWZ8Lf7vtZ27fbWe2EMnY9rg8WGY33-lyMXUdoehh1Glv77hbd69Nz1uE3ddY-sXRa0NrX5zsMNb0rsn_HM8b0asyabPOJHqtWqekb8_S9PPp5e43D31gc6frZ5oHXbGfesetX2uvasztWnp21zh5E_X779BbTOUM08sw6u-d7tvf5ZiWKIF8PuIa2ThboGdvEV0xhPkffu099-Ojhycl6s7CzWFOtCtMes0gur1vPOR9za08tNNN72-3wOS7Pe9azPzrbtd3-4Hh-m1Mb9f6GN4oBevFHkegy3dYE4BjanV4blR-OYg8rBDP3Hh-F1vaNvK9IuKjOL9n18q3lL65U_9b7VhH2zhyLY9ZT3oDTz_IOzrffne2upYap-nqd_pH8LKb5mkZrcW7PDOaEw-olY59mvl1Evl5T0qyJb5_HYuXn-sDWeo11M-biRitMoTiPqaeGUNSmPVxFoo83fBXLm82Xc57Rv0nB0U11o8FstYy3KLdZ5tTBDOt6R00pcmDUIy2rLwptGk-JgiaqWxw2n421dltet6LV3V-Pslj_44VYZ-3HR38Vy1yZ5y7CDn8ZTzthwWu-iFwdqVruzbLWKhXP3DWGU2Q26-B7CJc1W6bDkkV-7buKefhd-xR7ZftPf_YRn6lPsiLcq5PbJJ6KpIrWGStHbUrKL32dVYZFFw3UX8eG17DctTynnq2rdvZ9nhzK2otuMqj5vmbcT7GkGR5eZ7wEhZby6SV_YAwlXOb7-CjvWdLFUrTI-YTt7aKjsJNV293Np0qMtOWt33Xo-3ci68Q172EJ7SfUU0y25LYYYG1QcR-9rVgBR0IEoVNtvjCLjXQJzF1U13LCSs2owxLl5Z5PHUDYvZDErcmVUpOH5__B1Vo9O9T6PmYAJZH7c8TgS-q9s47x0KrdohtdloQ44TJIwfua1uptJfs9vSIa6wb1tnuLKCzY1huZWyTRzNTZgD3EDZ86nmySv1HiM9NbddKunnhL5l4rWaVMTTQGCbASy8WqNJEjOZiplmx5vDunVIZX524he7bpvH5WwjgPn_3kvN6n6NCTuo9Uy1yiaviSE5vLIqz2m9Tju0V4t3GeS8QUp-B4lIwtSQeFlVy5EFhRPxKtZtw18FvUFIGMHivJU2SAiKnPduQ1Z3KVfvBKaawVqHdBJevU7j2w5NJ-933Nz04Jg24qO_O2bz032pPpF83tiKCK4kqMmuJ2MYpzPurc0rrqzOy-2d4xJbXrar4l0h-IYsJcDqOr7xT1aPGely2VhH1qwqUDaaKl3Ozi61zWo6WEQ6HF3Up5M9crihK3vBzlq6nU2oi3SESsZ0YulFQtVmRRWootxCy63u3j7iWtS8573pBAC2mUWC1e_12XfqOVv0Vcda2Z310D_Z6YiEtYVH2k-DOMqRj5GjmLpR3cN0pFEbyVn9JiSzRbhPTS8t_cRePxcS_y84jf2ryc4Bf29H5KYZHkNVmX3jE5n4dF6zenD13CDLWU4lJZLUTrugwEva3kR_MS1j966SY6lEVpRVc-qqUbdLPAy9HwNIqGWG4fjPXUpL2aJRi2BEcZI7U1Zo23OFH3wa9LlDW97dwWOY0SL3LWID5hemEMOu31strIlveW_PfP6rT1bYm1i-NPRGzt1bN8kMuMKDpFL68Xldj8-NtdZQ56NLU-yC4t-2G_9V2U--gVRVYlBEL_A4V_aQzP_yeKmfUc9YlF4yf6Lo7yewKTR0_0kjyZ3QvH9JQmvCq7L81EK9Eb5-R4JdbNqtWG9J5L7EgeqH92bAaReY3tG2aR_Fm-HLUfj5-tB27ze1ftqJhXf7Afry832-iR1amOfVUqzLO_ARdRjCeirCxnk2DRPXbkNHdm7EvCslTi_KwWMCYF85gx4oqSig9omG8tRDfRnFLuGkRTx6T4fvPYi8gtQurRjiLIyyCPdUJ2xaP6yu06oraC6tPFZl8vRVlarz_HsX7ePNzQnv2VUs9P9LVXvMSrObBn8xE2TKxklAqW-b-rllE6cqk8y7YxXy57vh44JW_LEsli1veGKb52_z4nB6zUPJ-kVepeP54pngfvnVT1_l3WpDWcukS_7LocNzq2rqdNNRSZ4q7XRLH3jEbyMDSd8l5SHYFcqnN9DXnEC94pXbuYS5m4w4cK6RT75hRRrbCUYv5TIgFrv0Yv7lJy_sTwv01G8vCyv0_LsqNBLa8ULgeTYB3AR1ZDrUhxohER3UesuzoNzUtd-gM6lKb2lqm7fxSKANZunnp0f3watVIrVcQxLVDeLhs4R7FUv-0Fro0dO8PBExGV6EH4iFjLzDHd4njon304eLNNuuJJ28uDGHxMPC_UOnIqS1Xa_nCf83f-yoCJdlESroXesUFR1mojcFeFzaxuJxiOADxFHm7vXvX6FXLbl6XLPUN7UQrwiBAqFrHvyAN7SAiVDdbgLF7Ie01x0oB8tIoVlEDLVhURPeFCxQoaq1DDR1DqVxxN6wql3hUh5si-thb1BvSBxzv4yonmJ8pcHTlDJXeevKNoYFsovKV4fD6TdeNxQ3qifIg0Nta6dYpzureC90IPJsTidNs_m_Cpz4-Gx8R-mTpv8SRhwL_Hp2yWom5VqH5XjDxDK2Z6s1lFBj0yuIwSmzqQzs52FUHtG1lRe9jMb4tdr_NddkSwl2JvvDV6cx0SM_LartoaWy8lV1Z2vMGFwgBxXH2LdTknfqLOt5wOowbcPmZZj36HoBSs-T55dmNBT_-sOKcUE1xbMpRrMDUg_V03X7-gZAJ-K5vMH5_vcKl1v6_V__NBhGNFj6rr4tPfRrdukwQdtEjW5FjK7jYDLJUCZlmHtTam-cuQ7bIB3rnX4laoWfQ3g6gl8dBe_DsnMqHkptl8wxCWFsSz5_2WpzavZyGwpx6PvUi6h5YWqoeHXlwrRFMW5opuVBrM-gykiULjz5W8aubzZcg-NuHR32trPIjFyvuBUsC9rF1mN187WOoRCaUasLEaI5rdN_RSxGwqytaYksAb4-fZZtgsaphTnZlQWjHFEsYoR_UGlCq_4aD2xgookrII32iqM6fynZLFZ9UhsAOnjngbvWc4ZcRat8QXf_tHEfW2aD51RlZ7Qkkbw1RTKtVoebs6ur4fQA3_DmvN49i_C-28tpQ74nw_v6XA2DK3Y65aisd-UUhtFC9bYUCtNlyeWr5SmuiTb9dDJkbM4N4--kC39ZgH1OIdQS3_bcOaw9oY3oYwe6gtHB5d1MZ0LcZ7bejip8U3qbftE89PXY4boSZOsXOWynSd3bbyi78NQ6TEyenNf69XFF1dH7vX1PZgRLXPkVbKdR_RCdgIH7vN4dZilUVS-lqR5ipe3m3ml91aZtq3kbX9LcNBV49sqIdvaPeR78YKWLdViJOshJaffWsfL2eiprVwlZoplV0Xkd_xFXsdUHG8Zp3ADGYjvhMzpTIQipV7CkWYxRtsa-56gXWKOjonpCb949vNAwMXr6vltaVecqPkzbu_Y4tzX9ZNie7tHUSgn--s4jv6uFqpauxKrWKtnzWUjvWXHYRXdetcKP3vR9rrqWlij5W8beISZe6_j-0Uwj2r2eQthYoxlNKFvRd-y3qeiNtSGMa3QXAtueInJGv_aCshXGZlnL1ocKxfGXxFyXkFxkZ_fZwvLDMfObj42W1CJn64NLaJ3aA1eeEDltJjJbLRpU37ZXVS4bLbl79F6-x8dqZjL7CfcNuRZyfGgC9LZ7ALty6LnUFFJVNGj1TtePqtIpYOMx2-QEV6FDVUgEHhjRJQZVLfTIxQVc5oqXLhIKV-1G54i88pCWpXeem5LZRRQoeAk5UONHu-W5ZPC2tm-adZWr_8V4s30CRG7uJA0ci5u3yWhv7gu-g4w-_FaZafvRJLkhkPklDBvYN_SvZJzRfj2ltV7MO-HI5lD_kOVjqSuJ4g_tX8RB0NP1zS5vU36rLJs1pir1i3j-Mo2gwXLNX3SeCtlNjxDqueRZz7xmzBV_BmsaUv6hUlz2Cqmhwb47t4zMzDCNUcH72nobN-qj0iOgy4RJePbB2XJePqcHIUmCwlBsXPlNG0qBpCnUCvOpbiMauprR_Nvs6u4N52wK_eFu_sQbBHhS_Wes20ixwwAupwlip1PnWsBoYGifG58CtqGYqKiz1YNcVz_23AA9t4vVsKiVrzLOXPvkmUwkfaQA1ljs8cv-fYmQ7JjSi3Hf9SxlsMaPDaovR32pC7JRS2ot8LYSH3AmGRu7MI8_00u1LJiAnJcVuqurWRF3O4J75ZP_-04AfF98QLsD7FrhP2WGHZZWH5WQY7jofwoI2EVa1PBGHBvnn2YAuG_8ta1XosuGsWjw2wvOrzcxkUS34Zmk9pnaFhjrvEcbW6aIQ5miWwohyi_TSWbHVQAvP_AbEHDEcNIUHrZbMP71qN7Ikn68E5O6K1EuTwQpR4e0xbxQg-EQ23uVmeu27Y7BwYX7l7no9_lDUaTaNsu9omT-hgdZkGyvB3AJmjCQ7gu5z9u1x3mpUUS152Rx5ReITBgzPjO8d1MBLzHrfHVVGDX-K5-CaLVBz-xnJ1QKWuym58c1mSzlTFGwzKKMHR9vpcyyWUZLiX3XVbIqn1z43LwAwLD-ifA559SwLuhRvTWvBAjbbCRFAnNFyLsmPYl2zC6yMmVoe7VGNtF76KrCW8Il6vUqSGD1TJAJzYiPG3RJ64-S663xEYQxyYwRx0Vnb-4OLs0O8kKC4JUfSBEgrjLMMFF2lWCyVVbLrY-M6WIF7lnyy-O39m3cAT9wV3y1jdwlsQHcFa-Vvyyr8ayS2m9f2qDKcdxbXMNrOyDmTkUwzQuuzL4_LKoETVwqhfqMm8rn4FImIRW-LIAImA2Mq226uH7Iqyu1la6VdoDYTnik0oukOovGYDdyzGV4crfksMkpyRRiBPb2JO2QqcVTvEjK0qXU8BSEc4XiJ8Ese3GO38kD1F9a8P0ThoIL4squ9d0cX60RvpVvSWoHQ98MV3Qdg81vhfWQeZ40VQRWS19vmzcKlNzMeFgTnlXSst-X0-E6RIb3dLn-KxI76XOkstXosyVe_9whd1_q05Iuns1i3edpd93ZegyqMoKAFwTR4_U7AInVCE48o1hLrrUAvh0dW-lcamst-gDIpv-9oKf8XRIflZ0hzjSANY2FGR1tgpN9438ENx_7YejaHjDIXU6vCUGLPmVDYGoZMihTJiofDrsRzjd-omMWNEW-mzNUn9feIcsNE08aT8NmpuB7twVli8nmOqqA8AD7HuB8TIi0kwV64ufbY9N5ieB0UUiwbt8Xl_RuPA4o-GBD15YmWObK146VWTkIN2epbcsWo2a2YxQfcrn4L3X3I5Kh0hCv-uWHpO5CQ0YJE2B_guu8Nr-6Z1gBJB70dYpfEdERO2yfPFRkvbFYEwHhQg0S5CKhyw-N8-nwO2TN-Oq-GWMtuaIu5l4F0b-R3WbR9xR0_-JEER0LDtzxhO1nJshZ819LK2orvV5W3f8cAsz9E-D_gTzahMysCHOsG_9uFgnm01q554ACkVpREHuzbzSud2vFSAKM0dafQFL6ATM7ySbO1NfJvJF1fy8uFnyFks9cKhbsldOyEJCj3rkYDB7x1hfbY1o1nyeSbAUvt-P0V8olpZzbvNd2sJxrIvCRNjjO-k4ULPTtXdrOhlxbyV63kMMKi7y1CNBb7wybdKFwiehN098Q4U-7SLGQHzxuXY5ba1k-YIPMl4wEAs-wEenuBZqe69SYKsZndqCav99gywjt_9lrP23BEHghf7izY2rLrQIOHE14RXs5jfD8tCMDKobr7LKAnCWMsYrzo9guMsa0I9GstLaDy0UupacWndruHuuNTqchjAiXf8ia9p4n1ceXaiuXZfDgdf6rK0rRx5uTUd7-BRI2-eUi8_w2yCtntiubXARepnTCXTKzOiTz9xcOhEVIxvl4EvLY9tb9zLHhAO85AykWnz6VrkdWMolgreLcSKJEc80nKqWWssU5aIrpB2HU7qob1bXtbv7JoRL2TBVYSnuIM9JcRvXuEYVv2rfXYDwAq59UoQPU_IG-biiREUim-qww4jde5F5k85XeRrsDjEjPcDtTT2DtRsBIvTxEori4kA7qTNbJskuaZbOo_hm7Vp54nrtpYI5bL58nye7dI8Zpa_Llu8IzWJdrIKtxZhhNBuTIsGisdoDb7bHb8vZWidrXUsQS0nx_GuPXEjLawxm8jAI_sHg3ve-xEXvglzjWJvT4hy15qcj3YmDiZjAEooPXm4JLS4a-3aXiF2kBhWnbsCRD4vC4hwyPPiTw2Fv8NBx5qRwLsZj8TQq0UZ86fsT7mf_JEzjHvrdYZAyw5forWLN4H8srK_0aNPdA7cc35xje1TMzf0ZVa6QX0YStQFeYpE6W-2FN_n-KJHC9C0vkzgw4rT_oUa6sslsyORkYE7S_x8bAZcUkJVWDgOmNfhG2_YDBAXR7AxMVE8r_jQu98vmHPBfGcexkW_evOPdzw4v6LglR61Pi_tBFkesw1LFf2pvi3sbc0Km0HYgfrtmnLMgnm8WpKiuWJ7DGyB_Rshd6AUWdNzCTCGg8DSs7UHEyDDLU64HNgoS2gZ_F0jmNdAYPgtMUrfBZKY2Xe-0hXNJYKnHajF2wk5Q3XvNwywkNmNiVOMIdf6v3EfgFnLcSfQbO5WDEXkfEyXN_6L0kK-mNaA-yR8cHZD3dCAaxrRsHCpGRAL3PecYN9Z_xj8OGqycUchmnykuHreXJ-zAYyN_pNvwHA-mx9oXJw9JTR34tZlNj_xFG4UV5-wiy1rnlGc8UyfDgS3Y-N19xeWIcB1LZBQ_j9n2nyJh7PcpY3doATrYRG_Xkw8qH0U8HSbX0S7cEFqmE2hMy1t3XOw_hEm05H0MoP253IqneH54BCXeefVHT6eJa8j9o0fJd5s_mJYSeQUAQ1jz1gMnBm_V0-jZLoQUgHcLjlm9YpxP0_crZdEquN4NGr84XR2k3WNEhB9Blo2aaJMdwOB9zvwMsl9iYj6_AFIwQRB64GS23qg_oy-ppUrSa6ZLiHkbeIiSWRYQ-uTAdfDuGMStRwPi_wRUWmXcXuOpz8fRKXe_IApEWAdzL7Wgu-hizYNaHC4Xv0OEesBSaj52WKcWnowMF0xI8yYraBtEcU517rbeoLrZmCPjOMJdl4aFsZsjgdKCag1f2cPw-_Awi97E_EMg8cduoty6IFglXT_DJPawu6U5Rk2MEA-CPp2CKT8nBkDM1oTBNzyeohGFflnZcI31mgV19pmbXW08BizZ6BqmxEMdVm8t3tkpSQbi_Bs_t5yBo79iTz73PVtw5BX3w7qWuXrKdcbrJuguccTDZpyyJdXW11j9IeVQeTNwyJfGfo7bjJg6ogvUFfy5xIhsIu1BjQVSC-FEkeEPyaFxC8TlkOrUwr3tDNwEOBZUv_R03v3Zr0ABmBryJ3i3zp4pSYE_zGKqHoLgZUp_DSPHFHZM9w1p6POQzHWjzGCYR7fIyehqgGIJGelNuPYY8vlu0yapZasBEgGbzNokbSY13pVvUJcdHh0WAaa6awxbx_CS-6JBtoefAkR9AetUvO7ANCyoYKZ62VlQVmqFnHdJnQiYQ6MaZb9Yr_G8sbjPcRl4QkttlItazNzHuhojl4MwKinmb5Qw4VfV8YEHxu1NF2wmgXGwErXg9YW7FUdO45GWEJ74B_iwfU1u0qBmrZwgVLHibV0M_pi1JpwW4M4uTjQg6nrvC2Q9yfy_xZfznF4ZmwzxO0wiBq7PPHpehv4_zgqn_VN8wp6F46Pd9tPd_CvH2_vy_yiTtpaT_PObRxCOUeExK00kRqyswIXS9xUTPKFX6FIsoANAy-QQyCoe8MyfQh1IGZbD4Bw_AY0SZ7T70b7HNLFtl6g_CxXHaVuoV5c9Y73i-RefFEeBhHk5XdsRUn1bZjGm8u13-8Qn2YnQd2teYi_4CAh40SfOzue9bokyu83lPJie3N0QZY1JwZk2VaXwqX5sqySaQJY7KXJsTbbfrqBwrVXAnqHgOswmb83kvRRWEf62CkV44lmUmTTMvQysmdmPF8HPGAqscdxZ82VnWlYQVnt0uMMhsJaVeBYakBx3fhiMcIj8UmT6pKUYLcwZ_gI6XoGJSgO4KhKPbuaMSU1rvVaDSAShj3IsyWr1zbvw2Af30RPD58sOn4NKwZaMG0JFx1DOd7l_Yas8K8uo6o7gHyrJZDVuqGIuXAaaH2K636kKy-e2VLxtRGLfoCy_bgjjzhRrvb7acYwEBz7MqbI1wnMnYj9Gud3LAibCllwFTuQ8xWUWfeWrPdDoBDpwKvbsiWjRV_BQfQneP9DPkxN_MVPw2trE881vO0h7VUYUsLqwXM_AC0EbQB6s-VPX8U8xue8KmK3yAeuihFlNnvf_Z05YlWxdUpp6e_viBanCO03sOum8hrZsFd4bY5uHh6gvmxgbPBm3qX13wdGbDjsA8PWfK5ObU_A6x7dFY0Spc0JBV5pTlJ01J3wVLopGXq67MS7PtMRyJrHUzJ_fDs3iZbO0OuOdTQgvcgfIL82K4hr348c57vHl_iHuhpo2XynJXAp2iXPpk8KyT62PwmM2ocLa3ssO7E_t47gwpe2fPaPNOZMFMU3foHnOl24rCu8CJ5GncYRF0itzsHclRx9iOzHtGv4s_w0-ls8ghcRXaMA-z-i2MJ0cYXiXS_DN6lOdRTXVZgUTdRA0tp5Icsi-7AY6l3FGuT5qiscEmazJTNHSyCgTqJtD-VPmnoA7rb5qViEorpfVfrLiGoCltjKKhEk-VqJ2HwuwussXOi5hMmOS7qsAKx7bXEdPJtTA7dmpHMdh9doNJj-cFIbZ7Pvb2ZlxDiXGhK_jrQPQlAjKg9-6rCyl9hYDGfSkrTOOFvNntCJcDlPc5zeLRYn2aoWFmXTv-fbqrWSUVRPrI8bgv51wivYm99pRYHqMxJpXPu1a6cUtQu5A8jSEyULjuGMtSrjcS5ayaAimGwCjkj7Vgc-n_sxqjrczgcE9oFbs04bZoAdSEPK9M1yLhQRaS1zjRwpXOlOTR5SF2xe1MEPVJq8y-OUnfrCl-M_OHw93mI8OnJOwA4COcLQ-jD3pW3IPa23oWSYY8zzoEFmZd8kgUEXnP7sNWDLlcdJUdHx2agcFiREwZyzTjbUsnpfpuIrz8ifwgMtD9SHP4UTn3PELyn_prLSQtreJrhFtNgCfCnDx3oYDNbDAd0dNaSeXfalDVREQ6eLju_nOhmE0h45bcD2kFrqF7f1ibVBwtvn_ztEwfSS7URUXxb_DDGUpHoT4j81jPbDMjxGOLCEj_3DY5PGZ2sLjNYjo7Hublxrot89LMv4xk5Ng6FEzvNpIr1nz7qSlPXeA-V4PBu8wEjM6X4piEz7dxwIXJ6A2DCnDV8EwRAN7oBelU9pkBGxP40In8kIn6mfPf8dUJY2IMjeb0i4qHJJqp4p_CgxRTMhG8KW8MGFnMXfgEtMTyRTxFczQI_AQUscKPtxCu-946jEAD8ri_UoaK-XlSb2Rbzq7_-56KnZvZEQWNkxGQ9R8kTuamEeCQg0m2u03JBH3ioYPqzvFRxmp6kM_P8O4NeTyv9mJrWy28nnA03caLdaFBLXw6HKZAnaAkTIytEupXoaacRH1vjceOS7-3Vld2wxkgk4tHuykyyF-CFKPVLGvF9NCMWf4aCceKZgbSQwilwPIAJ5WGAxUrweIEqp3VCrpPwcff2pxfbaXTQunyXyQBy9KtsNt4EfQOK-Hz9e70ccteExXkqd6M-H2tzrW11ivieSHU_Q-b1j-NzhVJ7xME9ViEioj7Tv_huokYfyTEXh4XoYBz_hbT0k_wt_DLRXQVfIAWpWON-iVnKQ_bZnBDc1KZ_gyhNTAQQlc6guhoOb-Ace-_GItEQpLwpt7fsE8G_bWyXH-7eBZW2d8ancHeX5Ux_XB6IY8Fh7C4t8HoPMNKfogZtzmo0vA-axnl3_PMN1HMpmGdsgKLbqOlxawlR2glb5kW_VNICSwGLcg34yP9nyJLpVLy7l15ek10RVnsol_OiyDNQc1AmSONOahPmXDOwp2Jk441TkZ4RCBaRkofEM2eTBG3jD-ckatL-QEP_coZJJxq4vNwogxFokUCfSu0bVl3ytjjZAMp15MgF1okFEdgN6J56oc7zvF-eqXlZviLceTj6c6FBERJTewwF_dTLnB-LQ2pOf2wN6x0r-yJNDn01roFQsJMq2vcmgfhDbMV2XCM0RjwtyT1MtxjTsDp5UUnnMWosPBDxeS1-UdnKJpv_tyEiWPZUzlh0TE1eqzep5hIkLna0khRO4jj0PUgvce9dlqUyP33ABVeYbRSLTelSpjl-AYVLjwy5_Cu1cJ8PX3XYNUwAzsWxsTkH-viGX9J_-dj06Iu2mUcXe4Xqz42RTdqy-PbEnDImp1dpvGAyb-dhoK1YJQMR2fK1cBHCxT5JwxXvnCZx5kpFqjYFvrHgVa7lx-2g12lBtGDvr3y961fazUjqETw8BHsAqn5AciVXv4xFvbLy82PANWd5zriigIGCtCE80UGOWyMoDCKxBzA9DW6ehCEBW9atkD-NOcLbN5mcfXFrfPFY7cXh20PeaP5p5YsO3jklUUI510scn2uHzDaiUChNRI5nOivkpgfk8tlRLCxrb2nExzbatJSzqeHhAFzQuvIpTZJ8q5Q8SNRtgVwLMuQYgMc96f_cC14XKUGRk7XFOhC6X2SEOTUNooP7jIFZp-8qM4ayfCShQPyv5SrDgaud-KLhZp_QNw6knmtkFYZ-9EqohcHs-W4yyB11Kxj39k9GlzMUfNeVtMfGWAH4-bo4l8LFOglHOi8DltZ24WSKuHSuVE6CwY3cyQSJ4p5LMccu_lLD6VLWFXnv1uS_bZ5ZgWnYJwiMf41VmM-pSdxfVvszscB_mEA5isy38EoPa87gkOFvmXJClIia5S2zaSRfJ5JAp8xN9ADVMWWT3J12whN37eXIn-F3XyJmYh1Hua9ve4yA9IBNwoXxygl7Ju08XfsUrB6LQmjmZgd8pel6zdp2GcHmZFboEd3A_l8ImlGIBikX3_naqKPSJ9kLQS6s7yVuGsxPQeByimRDPp7Bg9R1RWvGhc52JiKNuWHMnbDM_atD1-_mFy0h2GBnjxuUiOLe-XFSF8olZAp5qJxbpkHbfLZjOkd6KwbMCm0Did3nU7wZ6-GQQQcsytL3soOLu20IkIBKtYGwyNedP5gx72UtQsl42Axq4b_vDiMhPv7i2z-YfaLppKE9dvo-R1ROIlOEhdYoIw3_S4JLQrIdxpYTZL1BGFndFImdmx96dxE7VtbOeAwud7_c7-TIs5NCot-C4uMs_jQfuGR5I9SLLO1ANmFL6HZhQD3QZ4SRmdZ4nQndh6D7Z6bK8P12-f4HeWlpVhpC29d4V5goFvdZcLsk_3RtZrMgitTSn69UqOUwikOUQsdFECieBZxda4LJ9C37bjFSmiJStEvjBdWIBEqfLo3VlasbzVKrPzYyo0WaqKG1uhqsVTxt2f7AKM25EAjWv_UbUilt2QZCW96ysGrqMyZgiEQZKQSgtiilF16LMDGbxXrM1JPkwSZe17USHRag2oFdO0BtXF-b_zVKUrFuWgaStxiHIbZWh0xNEA5oXq47TCmrtlX7sFBYhcptJbyQMCfCjeZuKCc7kCRc5zhspsICY2e9FGlk0sCFF3SQJgm_7nC2knIdR-3i6NRRca9DszjA-Dwjum2Mqgx_4mAH16BH3TWrntoDutvtKyHBbE9nCh4qmi7gB58u7AaY6KZYPcit7dvv8XNQKc8dPM5z0P0DK2RVLqYXp6iyT0NT6FBigw7pbooCKhRLFVDMKORGZ9pE-wCL9YbB0AnXyM4LxfgM18czS6-ef9xhvxc6aDOJ7EfeEEaxH47vzuWJGb8BdeKFKyfPDk_BF9HmqYzSvVVlbz-sncA6EbNDAPWkgb04gnVQJcXonfsXkZ8HcWyxP4rux8Kmb9sSyhJbCcMHK92yNzEldHv0hb6Msm4U5HnNLcDIKHXSYFPexbTqpWfNExwdllQEDStYaL2CAzqYDuNvs7YcFfz4JmRzvtwsPBSCv7qqsIpJHpaGyecSNzDUvaCAfPGz0YB8kASx5672PsyMY8DWQCwD1Yz82Jkc74bBgr-yBG1SelKeDacSxtSiMkNgs8tU6i0CQRk8SZF6uxbF8zjWGV0kcmecRw9L1C9vwMJWFez_9TUVHdJvUZXPIQ9bY8gfLGrejA3ncrZ8CfkwwlIp0ibPgVTm26yixE7zVUpHRcH8SwTOIMlx31KxXeVOW6C4WJxY68UH6FUPVGAdglvPRaANWGKXhUh_EPhOKS7xmqyXFZg7mVcB6-gvHmAn8Zs0yrL6K9aGfqRhC8yXpH7EFpepp8rtMYZsNqxllozcoqdTSYeKtNQ9Eae--fNprG6TEh9KfzCSpvWFa7M_OeMuyHgp445td9tVTi_Fk0YthJC-eKrPNfvQFMuZTKUiNeeVQmXKDfRiE-ybVdAC6tAFJOY9ECOuczA-kV_zrpq7lSzrmsAeEvPiAp-qyDZtpWGBBj3AJ4x--jPm7ziFLOG8mR7jlmqSonMwDTNEKJLX6vlQgko6SU1mXLSh-0f1XPljId-thKhJlSkbwDLOT-hF-B28bkBvJevIzk_eNy-HjyJek3_e_IeVynDUgahZE3mKsR_YeuKhs0Tq27Qs0T-oCe80W0U3H8KmPED8bMNHRU_UAYwv6wpl8XXjngU6wRKkhBZZazuiS2MeFQqksnAqymFmJ1FOH7QQKV8PZ9rLi4079gEkhVNnkpOx11-h5XvKYbTk_fX7FgUZTgp8DHQSpgmrAzx0H_KAowucTBOwax-kmLzoBG7nj5Jau9QkkQ3ZrQiG1DLbggNve43Ca_OefL1EYNvvU3nU-F9VdMRiRD6N_njSlIVsB3jOVpLEd--6_25-3lGyVx3JKdb0kcLbS94J5p0LGyAO9xbHDN7qN2WVUXMjwpRaKSWuK4CUZQHJ_T6zbE0f0tfXImkt2sESc9CmRk9iriA9vxbTft_RwYySBoX1lKhal_lz5jdTmx14XDOLzqQBn_2pMIn57jE6yXWvP9RVgQpbxmzjEsLpDOMgG0SZeYg_LIlWxRd71VkYVbyhOcR4HEDbFxOwOB8m27N5cYGBdtHhR3DAq7iIPwbHBhTfYTn4Ute5ybQuu-LkmsF-c-reE_4qwKpn5eSY2nlVz9Ak8yMK4jlw3SJvlGqqigVqL_1xz4oFPfg7ifR6-jFz_bhGSt4W6ps6v775Ju-Vus3_IVsvH55T18fE8FV3W0yrPoOEjx5wtpVSaQFCoeeZUV23m26J_k_hkNWcBLApZcvczj8mK--ayiTacz4sGkYqBK3axPg6qzMouEUf4RWsDGsp-_jo-J0n9NTHQlP76wKWiu6X0jkhRUjesQpXVTC0KEQSRhSHXN1iFFpTuRmX12zbs1goAEZtjI4tSblS_fnmgn2ksBs7SL6ezyIwSHJkrUHPPdbPQrvS8lSFiwqT2lgPuunScaWsm_XvgdSGBuiQFMg8AOdNUQUvYtFybXAgrF5uytd6qPUEwhrzO-rxhqn2qnx3OgSV7jXjFTVia4On5BEkYOaEg8Y1k4CQ9IRjgqG_OIrllHg9gipltx80WJwteQgfG6nK-5h8bfMPz0YitPrMV3OMs-CKYZ2hkbf9pv--VrPbZuJiLGSexBgfZF1hRR9YXyXzG2S-8izYAAAHt2Jv3fOFmdqvfEKcMHNPbAeSouxsv2_j0hecDQokC4ssBsPNlqC1K9tsZvSCX1DajePnjFCky1ILx2s-gkFleQhEQmQl1eJ5pC32DOrX-SD3QuXMX6JHZ8N0g330XH7QngJVpy5-i_tsJt1QEHNaCt5AKfhmZpK8PIimNFr-1iIJ-Su0i4fzkayr-oqNT3Mp5C2ULq1yK5lyc2aEbJUu5uHYJ3LX93lnne5rLEQ4YjgZucEGWK5s4vSvx4EJrTpelJt1w0SuqUzeXm1-k1tpiL3LtKWQ_YY429MGS7x6ChNNOM8HV508Y4ZXLPuLg6B9nWyd5D0VY-8YRtVXhpUV3HUbgvapFbiTcKgXwrqigxeA-0YvHdA4z9yaznQmW-TmzY4ihJEpSXGWt90dPzU0gNkV_fq4oGUc6O-iZ3p2jel96lvNpR1Jxc-wRLTMiNixaZTLsuNTvnvCGR7kAQ8eP0AOEU3bASMOIWpUV-QfS4TWKs3RhHBCfSG-u_bAHWtDxl2vbkhJMAcL3huAYnMB54O0_UYKpOT5NG3oyYgSdM8D_TVZorAJyqp6PTap4clwb7zXHQDW8ISOAfhYOaJ2uR0gm5etaDOg8PygOpX8i3hHZAVTs2t_4jCUIv9ra04VJ0OHIunNGHNkFiSmTBrNXirY8b-rXUXNsGLpf9hhpodDteVRG3-Ry1TqEb5xGuZ2sEa4kp1OV1iLohxlsD4YWyFj74R-OqotrhoBD5IMAB91jTw0A9AZFk-niYtB10hQ6GtpLFbEQ1VIJ2GyEHJ8YnGSp7Rj0_aZ3AEj611q8ykX7LG6JM3kE2b1r0CaF01_rUBOssbOHFvCqyBf6mqww4622EmEZBEByhg_ItO0h1zBcyhAHwRNPB4BQwwBggnNH5INyO16KGrR53KK-nNNL9sTgCzNXICDHiDpdcT0QnbLFTBWjAJNKRfo8NsXWXhfs5hXvjhq5sfiefHrdbORoMr-3yfODqgLGe-ODbujzER6A0OdHJBvoiC3_JZSeB837cgKnyqVZGSKhNEuKt8-BmPqOKiox1PkVMmE9GuaSCO1h6jYMu0zMJcTPTTlo9npG7aM82wxDpuqFzyKFVLqDoEQxb1S9OgVT76Vgx2dv11btr6Zf8WNiPKLgOvgj0Q66xADQeWXdfJk-hAwJu9Uw8Ak_9tscqi-YqRwi5knc2OAcYrQBRLyfxuvabzoRZQkYOE0d_8dmA0UQqKd3vHAk5UsDBHjrxVgCQ2X4bBcLU-ofiRz2xDe5uI4S6SnR7U9QpNgaPwCKcVxlTBLQ77VzSCeN6smm4E1bAfFJkH7obDxASOrTU4F6-4TC2ZqN3a2uNna-kgC3bYIBjLWXdWP9jNDyFMZfqGLqDFkCEQCyqgBv_Ar1jakyAyIbEtDckEiC3Nu4l6AWbAd1T5w6Uq-d0yK49NEJnANZVN_64sE43FLSkafHhxzatYLLpY84UDcycf8y7UvW0t_FygsJBE6CJfaw4tRpbwqIHjKE7XFHgSoRbLuTIoDz0_rvsUOBGhjd-E25iOIBQlW3xJM37_00YBVI9TA55fF28JWkQdAIwmybMl0G6WGM7ucLuXblEy7J4L6yyghQVQeE1Z5lDZ_E3xSB30cpwZBQ8VCyp-quoJdueHEIdIW3AHUe8etyYIOoJe0V7fxzkhl4hFndo7JJfXes95EH34yv3895jIJoJaYeRWn7DiEAZOtVW74_XVIZxLIfcPSJjlKK5St_msoyWeun-unXZ6rMO-q0eibYhkZkHuDZzvqjycN1yt3B3rB2R834ZZd6_Ww-roIvIPz5QmD3pn9XqYRPTzCXIIgz52twNwkLhzlb9qM-rpalX0oT4cVlkSTvjdqej7WcQx3P-FI5eSm1QtmkEZ5NyYzHSgOWbDd9A-bJrh_aFyRWc_BrxcYF3xU_E4Uimy4pvDus05KoH417q9BP0h_KhDDaagPXdAlYcGevbYFFsb8VIqNAlLeMeqRx_HflFHuByECL7OTNNpx1pm0h4Gh3XLuGs76hH1o2eUb1xEi7FS7fIABnzdSoquIc5x0f9bq-Zrjwc-K9XsQ73AgHGZEGTpea99YKQdCm_OXuonEbcv35ivehmp73U9_5k6XkdTEsA4seZxILWNfHZxiqVu53uhR30hyPylpe8W2SSk0w94TRHCBiMxM_5PeF8z_DaQW4lj6-Rp03KyZXxYwlyuh59zjzCj2-x5NaFzQziVpBcPoLxpKh8mnwR1Uzr9XQ4cr4e9EEbxpj0oWikauUMufWN2s7KZwi2f7KD8LPSGsxqOssp0KILRf9meuu8QvdFcNPJhTWxzg57zWRx-bJEczlxmh5r_UqFJ9UqyBK9RrOX0qokv71XpXV0Rgwvu0coLzEc6Ox4j00U2LCjhSdl4iXROMhr92QPRqvuJrqqwCD84gBVwzLWcqiDB9UyixPK-aqeb3zsvZfBSdZw0UDCPoETHQocCTT62gBreBQ0s-yU90xvBdHNZhMNKQNbXN4QQhfGxvkkjimQ_ptzL-OMLenusv_mU_0gAcoE9bs3IRvTJfnoMb7SUSsZEK376JMSeXhewv3dCYuhfzD_2ZzAXrZCfIqeqE46t7NRhmnmdAKe-C45hWQbsnEaTubtV2J1DO-w8zkoAtYIanDQwSEu6DAht2dmyxK69bQDoh3CXrAN8_nbws6vowyJU9OGSUjbFd-rBnF66g_mb-2p3UC7IMAddisZmPg0O3BBUEfPKH2Rx21TszQgdaYJuEeLe_FKeb1rUcTCyaiMRymOaruaqqmVKHTGbhMtQUun1jRSruIU7pWzPrcpSqEs_m38lTfhCRJQ8oYpisP4umm5K2dGUO1sbo-oQToLV1Upd0UXFxnGtBAlf50GTnU9p4f2Y_kwB2qJjl8STCn2cYGCnyDAyj-fWOQHrJeHfGhh9swcGPLL2n96wJ5Mzb1US65Y5nUz4i7RAsQ0_P_A8KPmaQc99TQKC3p3SoRokpQlpzTB44mBcOZbZPUSbvb0SeG4_kI05RJpcbkkGNXWk0PT-0z7wIqfAzMJbVmGtBKib6RjWUP7UEEFvU66RRzJYARnE09_hgO__1MQAKy0RDqpM_P80ItYfvnDtUfV8A1tKKyRS8PrU8UGNyc9ZImgIoUyEFeHavtBOZNzYziJ7P3dOwUVanbxY1_5fEWULw9UfUtG8ii66EorR_AFHfFvEt1AaUe8gY1t-JlGzDjzct-2XyX0IY9DJTD7M7QKKUZc94sgAIEy98gX9tHdcHofBf3nSOrl2i8I9r4rR6bHigbVFZnPCSX2CPE4bzxCh6qvXr3Aa11A-cv9VlcQ4vK0zcaCxUIrpue0U80QP6reqCPf0dwtHnSoEC9vXQ9GVFTSEigJ0uu6YPp4ckv7_EXA195CUkX3j2q8dkHwCpP-8YPtShzSSVeNzY5YHrRbsQ-KPLkaRNYElsC7XZtDd3uYAYPHGo7e074aKyFAtaLE7mUc46eHqCfqOMaNJKK_4WM1xZWc53Ld6TSzqPqq_UsQl4RbnSLYO4AX13rQZcojA4m2TRrxtdYNtpJshH19Laxy8V9pCiYmoRB1kdwAKS_GKX4HhE96XTvxyoQSV4rDIhpw_dSVs9MlV2KeKoZrR739KXgqsN41EJ5Am1pD1UB9I0XbdmsX217LaevDFGr97S6svCgin6Om9vZWth1IFMaGvLUXbOInAzHIKH_aZ8vroVUySIVZp-ggC8LEwv3ADqww47S3VSb5S2XGn2uvViWF-vxqWZP677blVY1RZuwLa_4-JXWPgPBkWM0FUD0ZDEZIYf2qOzq0WbSKEIaGe1oVqxLmsg-rjxM5kFzzOyC77eetsi-tvZG07jjcq2XcgMyWxsK_VTpZl02SxtCKM_rHXmctgR1NoV79DN1U_tYC8FG330l2cW-znUlNui0pNf00B-1Kpq6S7VLPdAds9c0sahMvp36Go-fpT-vsrsfFY_rusSGttJ3gbCFFYKANJ9H8sSmq0Gu50TeT-o2-WXtDT4XN_ITuAcfAbJuWmhAu-BZ3Fb3U4l88vsk30qhGu5jhsy8rtt9H-V71aPAKY4jnHRJW94tauHvm6AYtdeijFNKvUWqqj310FtV21AjKw1S5Zls5yHaLRXFY69Mz9Wt61GZkEMqvkJZbi8gR0L1LhrQsuf9zHQs_SzrTlkO9I8AR-ktcaIX9rIdVCUSTxnVcAyGXKpNmFHS3kYEv1UayrcXzTdzrPbzpFbCFQateaNpjXqzCWTUm5vg1UrUkm6GVw3SANoAIcyY1N0IJYOqTyfYtqlGnrJdev_MI4K9r5z08JiUPscVcB0-oBPUcYGAhz7iqgrFQSDT04yuNKHt8wOMQ-oRKa-3xU_Dst2IbITGNUmrIGvoeBHZt65PEWMPYt2ZkuEMYtzPskBh8dC6ZiT_T1_B4VCWbriGCYGR46wx3ImYu5ev0Y3i5Gfv54SpsQCsafK8rZvqKeokmkOBrqnM_0adqtbWd3sY2I1Lq_SNbLujGJrepqbS9xI5tnVbUA8Ea1P_BdlTevV2TXGSjpIQQd_dp7VcU3tLEPWikYeGsDmc84t9w2-eZiZCu83XvGf1n7_kOGPV71Or6ZDlKAHmbmwqtebswUN5I_liwN-eHKkSxdfgb-j3iskSFEOKCK2FAut-9_BdxXjRoEZmekBEDw1S7ajM29mT7VWht6_rlT-o8wQGeAA11W9AeQn3KoN2ut4T1sOJyoZy9q28sDKZMXnfzohh6N2hJHzMpFDR-lMVFH2zq0Sw9KNXBfik81DN10QP4io-RGa0WgCoNDtWTOxiXboOEFkijucRMDqfhUbKp6rqNNqfB5dKMaOY9PR1fpyEiC-Uwmk8W-wVBeKVXjXtZESW0ESISxW_N3wF6slZuZzi13LyoWrNFvdSRWmMLvlMr-HQlx40uNVYP0dVm3j3jhoAOPc8LrYCKD09wQQ5tL_rEBNcsqSWAiSpl01K8pZ9VdY2pt8VOQ_jUBCfysvqSp0BcmIjsDNrfZkixs9ntm5FXeXAOlh8PFHb2W3EXLJ2VhA7rbnyF_YHCAt5GAmSenkIrN2vZgo1_ZzCWO86R12wcTTNz2F58TwnutMfQYolrrtCh12ESX8vh1CaWt_5PLdfPVVULZiMPFuUTtF6q_WtoHLk9c0QxYEEwxPAcVjLycPxJiuT16MjuaYFXIR__uS00UHBjIz6DWYUGwhx7m5yjezeQbX0gNKXtvZ60v9aMqHGQOZm4rPERi0ILsEHvwsd1TozSeTeXarqLEXY9Nrm-CYLfARo5L1f49KGjyp1HHiaMnq53ygSrMgKRYQNIpGtkNMmfOnVMNAD1RSbeNRpzl8jLr8CBTnqiP5-eg4AlnzlUv_aqyxvscanSulW3rYaN-RhGdxhqxOkie8DXpDE5RG1j1QJpJKXzroh2Jx6VMCm7QJAd6DTtu2QLi-ZICKULl6e1lVaht6LyyKubvTj7sni-Io_mtB9MnYvvGFmhlMQTqu9TpJesqiBZX7B03YdtCf_cTRXLKcDw45QcilJr81CPfCyy-RFnDQV-lRuPE1JSXAyD1NRP_1MPT9j5FLEQkZnU1UAcSPq4FAQV5egMrKg0q_N5JWQt0I2Ov9Z_xIT2wlAgLNf6WNNXeNUUBA7og3lzMqwMfy10f9mia_gugMgJOJlTWfkpYLaXjsqaDpKV2ukvs5swH5UF1XWg1mToFvkZ31KHJ2AwhbI5WhmBYdMPLN0DN3P7KLKfxRg9cHMmSYnT_lZjTxS16dsqlyrTJyGB3Q1Uwp9ANSL-qgBoFefqKczsqgJMNYnmMnZtxnUBFPXPkA9XvyFTWhSO_Q7shZLVTH5jGqDVaBJS0h1Ndv2ZaJPFwinXmJP4wucGdf8AQe_TF_itupYxMewBp3Xu1VcUf1uMSQygobUtgNL0WHtMk7_I42RST5rxbleTLCZSBGdyj1XuperJkEyyw0DKGtwezfdYf_ID9zgrkfVVxkYexuDtH8eN9qXjhY3rnohREt41X1KrgHAK7LrVC3KhPKCjut-M3XUorUg5ub0YYI6QoI4SfuywL_jRjwyJtXYad7bJFmpxEv7XogPQtaWGspNxUfV5PKoEDFH8hjbwHaQNu4TiZbrRRLyR0QKgv5pPiBbHahtwzaaKeQBakBxaeMQ6c8lxaE1zEbldW8JJd8VE26CaMuz0agNm3xxfreNcyDYhdpkKsZQ9sFTt-HXm0YJSYu2Z6qQWj3QsLXfKPH0lXxz_Gl11fIRim7O2DZ3jm_Fi69_nL30ld29g4eAvZRUTKxjikFquLAQNLLdGko7dV38gd6nIAC0MBDg9aWUhYyj4Yx4fRunpz9yVNFT9DBIbJt-gCZiO_xNtcb9ClCU4hX0xx374_2b2Kpf15YLHjY1G_dOkKnsduRar3PyuqKBfrzQS91ItKS1Ddv4Hb4yQxosvk87s5qGKTZ12nryMG1uVByLbVHxZJGJSs3rzWRKxYn6UGwc9oVlM9yciBfTa8yfK_IVlK7J4dzNwAmD9jyAFPBakO8bPYM-WK-0bN62Z5jdJVKaSBW6wLRGrnjOZ4X3lFY68japhh24oGNNnwis0jBIABKqTkeHymdeH0qhxNChJPc0faoFnTZdjWHjT1D3X6vL9KhclvMUm0F10iWucK0PFR3PY1EGGnP5cwNRNuJJe1PaEwCxo2xNnaSlkrEZgjbWG9Cwt1eYplYpeQKTUY-ubCMtP_g0ltlV3x76Ka5uGoMjpVhOo5PI102aBm5hjxQDV1vBxi_pYKqRlpC4rh1tyymeyvrem7ovFItUs9GhT5KTxCFQcV4hEiHaGKdUDFAvrKEFHVtJIhQ_UXa03qYka42a-q_WvRj04He8GDaBpeFLp083tLDlZxsVUEPggHGQniwHXeq-241i31KvKWc9lY3ctL5q_N6or3AUTVeRA4cK1ap2WdVmFZ0M0mh0mihBffptbl1Bqe895ZRvxLceE2VXw77rjaUMkBegv8sdiCnZI4yWV-is8b14qcRR96fHtdSjBo5MtAaa37WKeDbVqNiQPYX30Kpc9bhtimeGHFRv1QokgiLmw5BbRoNXjpt541C-pHgcUvOdO0JjxNVBx0DcFOwNCdLbpNCF5_LLHlvtzG5S8c6DWFg7xxRSMqhn0l1BPu0eaQ4mnCE0bH5Izq4xSjx9tjR8tTU3gENsuSvMrVo_Q9W0eKA3eeUaqXlvVgsYu9s3QG7OTGwKLwxJTdzX5_pnxKstX0qBVXty-bvIoDFPAmBp7jIO6321Fqcp54rFInqoeLVG5jwCtkmFAjXnJvSKPZGoD2K5KqJtPoL9vHaOxVF7Ji8nKru4PVViunGZDdtwWWKAx3XyI4mzr6t_4f0_DtryLOa_nqWMt7kBFS0IImvZgN17Q2gkpnKFUKHceSIhZIsUSMCxU7VTmf_72slLAbvXqPVLjEGdbdqrDjRSV6hU51a0UGnvFrR4TrdgKngZz1Zv16ZaKiEVbE4rscT69rHwoQKHYjIEgIeUgLoEBio3E148NZLpPEB1Kn9NZf93llV5golgobuFFTeQT2Zf9UUCe_5M324xU-_GErp5YqoUvFSzrar62oVHQDk9QQWEMNcS8SvNgQlRThc5YgoIPf1yqE0GG0HdNDdnYCulHRwd1Tavf4alOe3oqTWY91tnEoeshbX1BRJoHGkkCEDKyHt86s_PdKeKgAU-fQFVgZd27cONErgkYyGXYM3ZHxUVixyZFCYUAofba4egtno4ZeVew1ga5fs-E2wqmrLMiUC-m_0c6fv-HvUJdH7Zbof562e1EF327aRsmjKEG3UOHkXuGKaSqvmZfFDfLpBwKjUdP-IrlCYjTlQdMoHnxlTsx8FNSEQsXqNf0jvEftEUnyzAkd91ahjg1fxtgsPDd2EO-R65OT5cxBVjQVKLLG7koqzpPAGxV3dqbNTmICf3wg2X7O0jFkBQ2N8gc9YkRFupK_OF1InuSAE57I9dVxOkivV7kk6HYDg8e0ip6Brah10lOWZEp59KCRn-3aYBRy6f9vhtoLiu2Aw9xHvLlOajvHRfy2zJdVcFKqh6eA0ZKIAOZXchXZDhet8g2XeZysiu1o5QNNSpV1zeHebQ1RBCdzEF7E292j5TMgnlVu4mgZKYuPRvCiORn1cybpbhPc1FSzKqqpjUK-qZRYml-IhjbFOpaWEjWyaoRixnoMl3NeWuDY2iP_G4zOFOvLomCbWNSD6Vvrw6RcMltnC8UQ4m2lFT0tPSx6VaeqSqnpNVpve6fgiyxnrxFEj0hGiptyeKA1gU9fV1QqiN4HZa2BNZVNeSZqjTno0Jha7lQwQFJm16SiCYGupQ325uQL9OC5Kp4J2pcw45J7v2n8S4LMXGg5Cx12KYt1PH0U6UqUrMO9_YDtNr-3bLPHF1asA4aV2fiPdKY3w1HtSnEDij-D7tEoreShqqhG0VqxrWOyfNTrKyA9yYTq2YVsbf1KkqP-TkW6LTovBMUxpJSvbRcfJ7__GUc1Ymjmaxo1g2Nw1qopdts29o2P7dZXg6MZRBxqnEK4o-iTp0PdDOxxLrQFBtvKay6Dv6mIwWSmZ-V_MzJZIbv9RNs-7ias44L6Wnvd3AhO2vICbtRLS20o5aPKw-4nAfcq0LK9vk8XROTwMt1yMpibe571dTysMwrV5VadA4S5Y8728YYFzzQ9RCcRUSma5wopRzV6MRv25sCxOmTaPwy7ePIrDe-9vdRpqHd5dbkSIt-_24Bs10RHZ0ryDO3bUw092UGmVWnpU3lAbXzHdIRlVmCsRIRpKpgZbUXlrhOf0Jyo7YfFw4Kw2C4CD1uDY3sLSThaOE9AcEn-sjVmr5LSdIQDFi9SLR4qt-4SLiYDWbmPJylBbLqtncm1Q9Uko65T8o3_xRcOvmtvhBtreVaoxvqAjlxotMb2xvnzr12sAmy1cl0vS1uxUuZVPHfRNrOclJg_mrr5v1o-1q1J6c9YOlon3SpUqeAc3xhg2lHGolOjSibVoAMe3Gxgkw1M254aR7Q9W4Ldz-gdvvczJ4IOrD37Oy64dfgds0okcpd92k7LoY9cBVjTwtZp-p6kekcRn0QMITFeN12UjUjpq5FXsJn9-fPw1QqlMCRTrdx48METdqaktoNW8kaTUtJ5E6hMPDEI14c8b8tD5QwYpHsi4PxXavNkd2je4OoSaHVmg6obUtdeVsfpamS8eOEPQGRcPVTs0sbnX36tP8i5Z6Jt2JwJu3cw79CmEmfPQeTO4lYQSAH_St1GaaoYm3lBSYBvji3d7OXkeTHu_2QtSxOe5mQp2Y12FuCPJ5w7-ooWCzhmpw7eNJ5EM6_MXCHyMh4PwhTopH1E5q0wkFK4wLZ7nNCKQ16eqWFz9YlLqLF1OYSJVLxUGsI4ZACmFhAlttXC656N2_SaklgI9FpHk84qkeD6LNl6-y4n0sICZxIXDifZrw0Eys6FO23Fm7KB1peseMdsuHKTO1M8qeduSKh8ypL8CxlJdVl0RE87Hz9f_GPTXnD0VBMmJCf-Ce40kE2fHaEF2wdzn7mwD0n2CgMtr6PHrFL56P75e4zVsmXRDeuO3fD4HBT-_zkVypzpZLi76o1mkWJvH2edrCC-kuoPWsx5QeqUluNUjUZgMKi9pP6amQANBjYX5J3qdXjj9RX3gsckicVCSGetbIYn9tKINcYyCt3KtPrabtnUIbu15dFYPwz9ZQfQRTmYIuTcSAi8pcAZ_94w-XBmlGrhIY2wen0ezzyYdLqY9sWUu2l-UJPvZwVHLT7H8h8-zH1lAP4uFfwIyt1RVbpNNObtNMzOdIDbT1TXyrbuSlM3cfj-SokpX3qRQ44beZQFnm3r3Sg4a9ShRM_fTi-t4Y39EHhXi_FX_dck9P9-sF2xMfrl5H1fzIl0nLnSxYcbjhPSGh9Pu0yySyArgjXtPqdslqbQ_9FywxtqKn-QSpY1n940KZ_KyngBwM6jH8glXGBeFv0AvV5xZB-pqDcewiaGsW09oD2_o4jkTM--yPvNt-no9LuFqfulXvQC7qq847aCrMr7znpgwFJZ_Qnm8vO1VpOPtEVyEBclj2kDYchlBKrisGNqLMx1o4meQuzkXrjevOizxbC2zRArBOpUzIfMeayHaRfevzSN2g7ZN3h6rC87v7nHhoqauwHERdKuYY9YzCFy3jkaamQVBwQsF1mMb9DTi1SjWj5eKB4JyfqH3nCCiuoV0MYB1IOitvo5voxNp2z9BWejryvWdF-6TwSxSjUk5dVUjz2O3jK3VbC1_srz_f90pOreOtIaYRfkTo-KNtaqEI0sgldQA9ZmpuRpedFH_NytLmx2PeVCaz3KfSkP3Z-_kVpODUqOqmWehudkwvOvRMH3RVk_L3cMUkcH6AnzwfVyDu8SkBZImy-rW2b8wD_IdXRavadmhTD1Of5bF7iY71Y5h07lo3Z5O64vZSU2_GDZ_xixCENruhF8nwi0f_Ar4qXRDdb9PjKg4P8muH_Z-kuze1eqPJGkVddVexdk0WV9tQP9JG7vhSdTcqts8W5egid_ORR0oRHBrwaVNyJdUK2rVbkZ5d116nrQ5x4vxkHy_bBOT7N6WlA5wEL6y7ahG_PD8SaaVqUIv1OIhL6NGtORr9rpctIvAqj2MQpeIqr9cEXCLlejWXMnJ4b0mJpn5qrDEoKF3Sbs_HjOyUYRwuXc_eFj1AfZVN4LDI5THg7_RlEc3OqqrLrWXSoouAxwDw1yj2WhL1XtWzVLxcGe_Th5evu2-1lk-tK2U-gxG5iclTIN_I9NWF43W6xTMdsARGE1z5Zg72uOJDHcp7a1vhSK8ZTirHlDAA1WqFMVDY17ISr4s7GtQTj1oJaP1g7t649l9qEhvpVh3r4ccpjXAWms_5IzTs86rN39g2IUTjWAA_HnP_TYNmK_WkNDRn1h9KbhuwTO_tlD5BGM00YCTE6lQJupQ5axIXMeqp97OPSDkqZI3wPFDVY4RkXqfD4vlYxk97vOoW31Tz4AQTCrPMQtQ6qZ40FNyHSrtxXs6MCXG3yhj4E5IqkhSqFOfTR2VPv6xU96meIRQ2OE-c_Ry5_q3JQZ_2Um7qudgVbKi876rTu44nSFpJOYpYJVcZQmivUrDaVslhsSKgjU4OYr7q9KYBnTG_jaEKk0UiW98tM--SENP03xTg4VmAISYDMm_uJ2tG7tLhdZmoVDnX555iIglI0DPJ3WLxFz4x-fRwTvBUdqZnR1EIsxkV6zeDe8Qxo_gQ3ghL2q7pyyjJy7MQuNt5e01FU64fAMdm6CIM8D4WmUgGp2IAWz_3Y3Uqt7lMSdfuvtINznce6BGeI87BDZse857wLMCKdkLtNVOvUlLyOZlbY5G9JHTcND8m1u1VlWCMY40EmmlABTU1ukF_6GFX6FgCHioDcuTYVVtmvrEFRdChLA3mGspBt5RhNAtgBzc9564Ao3qgK5gcof1JAhWPMYOTGhEeKgEtPyy9QMcn0-jk8EEWUQ0-QrjULxLnowZ7zoGSXt9IvdP7ybTRc66pAnyQg-F7CJDmpXVCqebalBy4PJWYM7RHON2brkqTry9tSmsj63c2ZyCQbW8FrlxVINPR7U53RJy-TYl4NK6aLToCzslppoKHwR5LMsuP0wg2ZeObiQm4qxFYZSHgvfQiL6HdeRnG3uvzswFVrbzsuMgPDzzvmw0pLmCXAVlfyzILv_hr3xglCk_cLyolGP6Nep9tol5zLC2MjDd6Ha2-bHtQUQzVUGNb4Cn86bv377gCB4mWQl0LT_T8Emmw9gIowlfXrGM84OdsGKsZm-NdlFQdSasemLCOGuPZc17nIurkMv5HGotz6-gA40RUvNRJ176K2RoUiz_HNYfuob-8UWf3_TQsHKnLKIbnWG8w6unz91FTPNtB7NRNygROeh9LQOT_vtm6NqI0bTza3hmCAdExlG8QFg9GNNzsgqSM2nUT5QaLst6fG3yvL_r6RsdWIaYgqJxXrfUt8_GNcigz7kMXUhDrc9cTEBuf616-B1ECtSbtL9gE4x1HJOEg1iLK6LXDk-BMj_pHn8BthAElOWzLIHyHN5cc2hsWTdtg-1Rx17h_Op6d16VDkCm0dBBP1KRfkyB1GL1CuMDCBdDH7STcAltkvOqqoeNRtqA7Z1EWd8cXQib9-3pClI1zaIOSjY8xA3IMRRvYl7brfoAUCQv74skfH9yP4h1SDCjjN7Nm53Eiaz2we-gHFeEztg8Vkb2UdOZJuGYvF5k0h53TvoTaIro1hMM-LW2j4CV9fIosPYrDSmuYLlzKS570xUF2sT9-4dHCWKLRqdnq9A20lyXY7iMmBnvywJR-4btD7c01QzCnSb7BsZ2aCVetZFRT7LZURqKT2TKc8FFJ92G81ujuRdFUENR4jZIex73XSC7Z7v9DlzkFWpZAtf3xERpG_-guaBwr2YAr7vtZk8cpgLjmWmjwIRXcYvdBetpsdOuPDw6r2lpTwfe-DPKiSVOyXNG9hrX7TZWsgG-Zhw9h8Y7jFoagNqYrTypq6tZWjRTl1oLuw83osBXQt4yM_DflvIPebsPbAjSxO2hKWtXrGAviq1mawzzHSQoD2Ge9rJaS9HCzkqebeSr39VopUaD_TUrPUm3AJVDW7vaDbA6PjeApn1O8CkO6g6lV4zWFylmi5ZGWEDY4U_2Jk3IDPd60SIwFQoKqQUsEaGPCberwfZcUEw19OgNxm_Cx22QwllaXB67RxGK6Ta2OxOFwnk6L76vkYrEfihQpBLVx0Yy8oQyJYV5WbOYNB79b7bp9CUPM8qQJE4xx9JW7pNyLeFAE3vmZkXiVLE4B0Zq7oGaFy9JixC5tQsTHWIWNo2LeCPXpwhDAxMnu-EzktqJrY4_Lt7LVtK7HqlUtyO3VGZ8qQHOgqGaEI-2eobDZY3wnSXhE-MUh6LFrO6lo47WdVN9t4_3gLECJTg7tcdo6wf5nWRVQf3frDTCvbb_fi1QfgRwAtfaJIsTcEhAizGEIC80HrTY9lKq1CCRHgvxlh_OQMjwLItxZ1E1tFHYOV33rjeCgspDNbHpCGF0kMvCBgfL7yX4SSDD7K1Ya1xPMbdv5tpX2d0JpanLkTZzfGaCAcbshr_Usq1IUKotaSrWp6IF4N58nXyPHyIRUOvDOh4EbbJPBSK90iuk-z6eAEbh_c5cEvtwVtEbvpcdHzxuaKzUzeXJX42mi-V94fzu-C7LNrVoAdLzBzQtykIwcuI29Z-CRXd1NGPvlkUnzO9_XttO_OWb7dxcKdPQKjJWd9QNUVwuXB_YTSBMs1JBThHQ8t9hZpvY6fGeHjxGn678Zdwc_qESVsMwmi8SzANjTXI1OSYGupn0USvUlIJeb4iiktHtJ2BWnmVCoY-1oUfSw395CGqR2f8IdnY6-Nlzxk4ZP2GLZLFV20pKQpLg93kVqoj_xpgsLZESlnhBXi1cfhuWsF4DHzxypHk1ClOtCPDTqSFKbm7iq6ij1nkM7R719Vmj92Hg1fzRnL5q2MUGgbYTJX0pGvh7vOYHlUUf8dZllLUSS-Qip9bhEgGmGgut1Pa9jzs1YNFhh39EyaUQgtkmd8MdBFvJMHwcfEAmRo3X0CP9_FBErHeNxGWmHARWjl9Jaj6obcakcgaFnxztz4i5gDD-raqb5GeFlv5fe3tbk6PgTaBza4nC4qRF97v7EooqyP8HexrPYvh6jesUIlo2ccBeYZ_dJnPqU4fnYe0QEybEgOXPMV4GADvs3S8tfrtPcXFvse5aMaV-SGTOyPrWGzYRA7uXxA_O6cQQj3s2IaO7ic-r-xKNqgGzicCclEInPdzZIzhw-EKyUE8sPyv36PtHdVEnD2Taqj5IddhaCMk3fgFuynZR8OzMjMj1MPqq44btNJbb8uSGwskiCfPSeb-yxP9GG1uGXndc9Dlip6cLNfHi6mUb97KFeRNST-Y1yk0J8FFmh8NwbxIUCsvZd6x0zUr6p34HHpsShHt3enjhUwee3FIC__b1i04FvgIE_nyZGSQ77jygadq0QHTpCvOZxQHRD8vAch-25vM5OonolcHZd9h23-XHQwwehU8zyR5n3ZHupKdxyGrdb1bPIhO_8ClBnYYh4XC3pkOxCefON_WTpSdjYoYounLjuol5NLz0w1GwTzY3yCbxjK18DuK8H1KvKlNmpPeot7Ti5TPFoBzZXqP661VW0UZzVdZcAsu83SznVikzPAh-fpiWyjq1QAj64ZuBUNxlG4ZA6HM_pkSvdq0Py-sqzd_Sw36m6PH4vQtHjVaNUkwSI7Ddu_mcH5nronhqljOxLtzZm7B-Qo3jomwxgOt8G_kuRjpOxr2e7YLG-_GRo4H9tt9NwjZLPPFt6y_MxKHWDzBmlKrAtd23y9zqaYZrscHwaQXskMH1GaRll4BSRjRbmSHufEO6iypHfUHffkweoOmHGRl8Ju-nRzyifvz27T8oCPFG9PPl63AuTu8W3_EBt0rUqSwrBnj6sytDzJTq_8TGHso1R9Rft6XeWdQRaWevQ43_A5Iw-CEcOzg8I2zkZA5akaZFApAos8DPV7NPGUB38zQMr-DFcZN3WxKFYXbarTCqWpOVuFEaagriQJ9YPPj34EyKn-URR7Mr4Ojbg2sF1FBAPbzl0bxEI51DjYscgIdzjJ9SKKLDBTbgrmOk6Gm_wUQe4XstU5_yA_iQGNxvY6OfPOr_LCERKsm7jjciypc4370DPszUOnmLZJwvKYtwcx9JZ7NUtZYdzqgcI1kVg3p_LoM4tmZla--Oju7zK03iIw_apED_DeZ-Ppdamx5UPfmyEJ9CyxYow2IDXvgh08U9hKBa0d3ZH1fo8CJJsJV8oP9cNuEVILpXVJF9yxsF_gA7buqXqi6uRcwns2i8j1pSfUTlSrOQS6XWoF3AkEObjpWz9uzvcsEaOyntMhMASzDEpZbC9JlSDel3tBQv8-UT1nXILHx6-akfKpRqRiw3e152lr363UzsPiKdayuuaogY-N6V5UkDbDA10iI4bJSyCNSSyF2WQDOtxv1fg2fNAG60flheWWI6Wm5ks6311V9hNvRhW4WdxbI5cLhon6gEME6M1yI8alh6XqKTDmVeBDn-igdcffgNm8fES7je-V8oCTasfEONyKsKllMqQ54xqgss57XWX6XtNLrh0XVKYwVHQslLJU9cb-rsT80ebeNVszqzN4oUKUV_K8cV9nShNvY7G336EpNLXrwNr6MGTS2etRXfVlHCQCyjuqH5iFjSXcI6pSkzQzEZ4JWNk7qsmr_4W4BpvJ6VKkhF1cWi6iDSCQ75p5kwSiA1Cti0V4lSpOmeGImDqVo1FOl1oggR8-sx7C15h5LRH9fFsDEXEbOdc0iqvNDlsxCebOGQI6i4Gqk8dzUG7CvgXnwU1kZWkprHUG-BISai5pEPZg0CS8jGe49KkrQWDRQduRWCuatXOyNYXfLFl1fxlOarag6Hf1PV_T07uMcJXGtDb7EoiLwrL4xXD7KlBzeGgaqHpbmBj6cRTn97gAxBJe2VSFDX94t3o985KoYPId3Q3xWz2l-M-1YLLP-00JfTMAUV7I-AEDjNQrf6xRnlBrNsZBEh_Wm3n7rSKT4qBW_U0OEy31XeVteETvZTsoPeC8Arzwmc6n4wHTKEsy0suy87nqAJonoYu5qgNNUfwKLPve4JB9nhGGtzL8Jy2_q_ykhyn065tJ363ZeQDegm9qS2MpM9ZP920Gc_ppsarSABYfpx3oKu_zHhazNygsmAhr72maGBuu4AKcZshyFTEf52cC78ZxrjxgJtXI1UpE-ZnocnoAqR4RxovlQTSWlE9K5Mm_vRERi8S1nPChcnhktCHC1ch59KLYaoz0k0ds94feyw6GC93MaZ6xE4hTgqfutJS6RrTPUXID3aHDpS47S4I0v26MURHUJXQbH04eaPXrmsNngZzzoHCM3mNHkGxfUIrqDw2AvADr_cjY1pTm1MtoVz0BE6uN9_A4--3UdbUA0JkxAF_j2uW6_KCJvDlap-6dUtZCV7yqpqK2WWNLT6fizltlBAFXD_2dfA52Dy79B6yCgIzCLesxeruFMc31ORFYwMX5gFd9T7JKNZIfjSY8maUkBJ9HCKCQYww-Lr-AY32-ZXPoWGMK1HyND21_DQhE1hBVwlDR0SoTrAkC-sumRWgJ1UZ8C7nJRYtXGOcUUFcyx1Q9ngNu68_dmpfHW0ICGNdXvVPEPGCfDaM9EJBom6-dg20pADonp8onHiAu28vJb8yYILL5oDdoBkoEhDl4yqBgIkTwKT8ygR8zKeB6bl6SFdkXaVK6xWIolfDICYdU4AK11a9yjafjtlqcKmfhGMdZ7VHhBrlhaX0yfE4n2e5Kl8n27jJUqxLvH6e9VBQSM8CaVCDA-CgVFTVXRy-yemirPppDmQA7nCSSle90_195BraWuNE5hy_eas5Ad_ro4VEqA4DCka_o8uP63bw6ftajNB3nJKdmdV0mXB2baRlESGLlQSnC_wmm0lcu3lBcYMvf7wm3nwcys4D3KdvL7BeEQd1fl8Jl7rdiUHDc4A-95kcRbyyll_7ESZOoyBv-M2I8BZ075Xc5VNrvlQhWHc3qYv-yLE9Pb3QTbKqo52e0Nw-AWqRzkl9en9DzfW2J-USIxoyFcf9u4mP_Y1UOs4Q7prz-y3z2CPmMtXWZA13dX6wEUXFBae3duoRtutR4AdqmTVQLGnJDOi8MltrTq_wp5npbP7dqHk8HiawQXHMTvkTw6RYiukaZ12pGSvfoPj1d00n3-01pL4aageX2GThGFRrm6K5q-QQDejSJfTV3RJQbWUN8RbZhcY75P3N_cc5LmA6u4F2nZh-VwgElybxAz2AS0pHlAdo1QhxlepGFwRT7ZJ9KvcWht2j1boAbHiAssLo9voEndSlX3P32gbm8Ej7PapCs87wat0hEEcTQ1AFN6-hCo-zZSgET5ae6hiZ3AsOv_sbNJry_Mm8DcaCArfTJTNIQTlOhlWhy3lMcbSRUUBeDTTH8JzeL12MbZ_uykAyG2Wr_Tb0Q4GNOllAU1YXd1UVrw7Bo0aNJpw93U2xTtUrVAwjbmrIkRfEFunK-vvMPRwmdrxfAMgxqih62UbWeI_uV4ANm0N7R768Ks4R4RyvDycVLocz5-kOeT4PzSGQ5OWsJ-_4EJxXyTko1hQZtCh5wXsLgd0pPOtGTF0134QhIb9E3gW9rW_VG9f1BJ3wZBvIqffC6Ns9j6_HFZeoSHuuEsn5oQrYd2Fjx-_dwyArTAYl1n51EIAdzBCPak5JQNSeqegQi68SQBYb4sCay6V0QZQ-kHEZEjgbaeQdfMcanu58BODmcnnButZjjjdbSATYHEFKe6rcqfypcylPc_d3OPjrcnUgbbdzAFwfJ9jzKS-dP0Fz36UHtoWwpkR5bp9q6pIOH0RO5PNtK70qw3VxzZgS0Rs_eqAV8vreQYEScRqamMkpqAGBym7iqWAzsmfsg3SaZ6mig1_heu_uG5IWOF0gBQGqSK7C-cbSUzorkpKWMf2YjKguv_snjyn74kewb56cZjU1DNUdsqi1qxy1mxcO6kg-JiP6crvSB-0jgtmkOsQIo6RZ0Z0mulViNhQvbPowH6BvZWqlEc7fJlb633TT3P26ivoyYewSrJ_eTsP1p2UyJLL07-Mnud9FuLcZCClgvktsEqWmd5zdZITeog1QbV5WOuUnnrm-N1-Xh1IDl-c1zWCYDK_Seq6BmTUaSvabIvABdtuJwsRaxw1AWfKGAnU9Ho09X896si3xKIJdG-Z5NFUKeVFk2mrGEP445YyoJRXtiSL8ronTKRd-rPHW4JvrJsPqx1JrMXgoVUN9LMdYryUW4msGFoeqYhcd1O8R0aaimVk-6o0TFnS7Cwxwxte7P4kFxEl3sUbktusqNNjMbqkrIVApisk4jvUXy4tnopN0bggQGc3THTrpe4rdoZ8pTmNSUOnpab-lgmnWQhSaXP6dSvkKrshBWzY0VNsqfBIWZqZUjMaNoNCmlkPfTENtwEQTWPMWCjhudiQTqfE7b9gfoyHia62W0onTlgF0kgSyRjRRWKiuNo5W_6gZ6y6V4HTXGB9DJXnh-GG6L2sTeS3p9rok4Adsh1d0h12pwqWywnYBAH9lAesM9v1pPK1LlzCrUtdPMSL04te8kxGJPVGKtgl9oCIRU54ROlCfN8OEOa-TazJM3bwKX-Rrti3Mmv0WjIMkX71kPLLsxSYpb2JS6MADh_QYMglti3U275pbUIutKU7XER0uWox7RR27PVCSDy0b8L5re4UFiTrSEBPNEkEtsL34D8slsnC1DQ-Iuon2tNDu93QHPBioXNsseSnRjk3z9Ls_oAlqvThqw9qjwQmzJiIkEeVcuUeXhF12PD4lA3zc2NWWsyAT3GMr0o06Sp7eyQOLBkFv1CoSUbQUNDX8LT2eNq8FxYlypHHsHNtqVAhYFgEwJMeHX3W0vh7wuG3mc6h0WYmlAsOhAHx8kdZCRmbIA_tS-_QIqnMZxVZriuX6qk55pR7jNGvqcNum8lSYRwBac-DGmk77uXT7tFsCL-2HX0TZNq3juVrbbGFh5GaYW0XI9GaK684ugqP4przUnRIPb_OWAp81_4eVXqtdTc5ov5gk08cLS-MVlyTq0s7ViKITq7CvGJFwhsWv3NiWdR3XxYxwJSC_Tl5N3p2OKi9pZhgI66MUJ-4KTa1vk84vChC7PXnzkYcsX7lfX2WKRFKs1OQAotpcmw5sCnfTKqynhU-nMJGbQ3bh7VUOiGQ4AFJiQCR0RDehcG6aSHdK17bpjT2qNeSN7So0efSOMcO31T94eYaqvJFVmo6xAD9D0TZh03IxP3PYaCr9bsVYFRTDKm1XVxF3PaMQs1ACXUX2lKlM5cJ749JB3Z-qatkpCvL07-SrHJduUxRQPjuKYFHcQue61Mn2mmzpPWjGTXVNhw4o3oi3DDqjh_w7LAbA-J0QDFmAAgnBqJKLrw4gsHbRHD2cImpKF1GYDiCe8my7OS1FFvVlgHRmd1xHp2XhHjlAOV3X7XCYB5W1eQXX8zFTGhxVUTvpneszfOj1fUM4pRpYpaJx4nuzhjV8o8hV8W68VingmIrM8wBxEM2JLHshrrraMtuF1ilAIP8_Lujt8oYQCL2Q84093cKss2gnbAl7WA1oO50aXzMPxSZ9rkn2-JIyXvUn14aALre2g8gYgpZaKzEJAVR9fZeKnddKLgVCt-qLdEICZ9qxQ66rcea0CnyygjB2oaG6pPz9-CUHZ4TQL9iPLhOjG7jBxqguB6-gtYwAcrjlyWsVF-3uOXG8d8vqBenB7ijL0KjjzyktEjzu8KIeya4NzrECI6ChqSqN1Bb8LMeOQH7nKp2iteOoMtVrjW1mQtPIBbhQc-c4tuiJitipKeldptxj0JNS3g3UASAfuEKZO9h5epaTFD2DAR-HbgDqU7WMc0efp2t9gfJ8y4ebYPXaJkzE5euTQjrxp0VeUt1yPkgmVHSjw5xf62JU0HqsJnVafw-vG_m7x-VurmAj8nXAQcMTih09qhMFWZFTorv7qDozn7iy9r2N41mvmRTmv6OenVzg5u3H1UEik4gUzUdHCSXqmvuxso_fBs7vWuDSWmyrTA5f3S74xNhhBB5PVyKCHNMIUZMKxXTNrVFwXfq7k6KI1g8BXfUDzIYgj8IeadC0LBSAhR47whcJHqJdUPvKUteAVF6Rpdhtp3cGZUJ32o7z9E52PouBk9KzmuKmjSeAB8jGprb0VdDSBZisHDNDlwLjqqjIS0Rxr2t7gBCEFR_1PVRaKUmc6qg0udz0qNYavtZ2tJyLDt1675WR4-EozuqfgUbw9Ah--JTCzU5YF23TEiCFyABFZdaUgLMMHa9KQZrLvNOdULpapQuEoge6nN-Q72qGqmPJxmXB0TsK6nDA6fcXzYcmjuYRIEFU5ALfapGvyL4oklQsTwUNd_pA4e_UGg5So95Ei_Z165ZOkahr5oTjfscMogLAMZ8iVd-L0SnPcj0GvLKCQnRVoFWObV2WanjM02hxozQZ7c3nD1D1PeuZFJk-CmR1BJbL1HVKPLp7mL4xMzk6kD0ezytng8_QM_uPqoUoth-VhjcjdNPTnEkzbfZYFV2RdOYFItW3m4aU-ufFfOmn4enhgF8ZRbFsi5CJGPezpDXc_T37el0HUHrByrgk3U80j4HfTJAQh_UDAFPXtqrqk6sGSr0MDcx3Kd9ii4NeRs6GU6ujN4oeXacf53_Q4cd5kR77-Ori9NSxUPlxF5GB4ajwuW-XBq68c1gWDegApF2NMztW0n1a8yAHKiEaAOsuXHtBN6k-e68tom6ydcznK9ONgtAxjk0L6vGsc-hYnhfZMUZWoMt7rL1R6U5Ne1B2TFhDeRpcZLur0WiLAJjq1MnLkC19fD-7glajr9XuzPDI1ndOPY6IFSG7XfwKNq3SkHqZgM9fLacdA_bFnky9uy1NQBoWzTb8AEFBK6z0e4mXGS_NY72CVEG3qVFl9te1eOGflFTJ9UN-iRdFrWFtA1LpOqwXevLo6W8Y7YUCbU_uTpLkzCw5eip70lX02xrAIAd7Zqb7vqkuQ2DgOV4sGl8bP4qIbE5A6ZTn6d-gyYW3Oj3UFSpsccr3oHfTllfpSXxxj-_skaO3vEh3uCkb6tKMNjRoeHQyVzZyeXsowWsVcciG95EktG57giLqr50tnY3ISZ5E6p9vbDisIn5f-_B4BfkXHibmx7Qfesg5dmSorPv4y0s9FXwu1JXI2_rMOCipZP-YYw-1ypN0ojFqUyq8CvRt4z-wUVaInpJkjw8kDaKPL8FIRSSR2jessaM37QhDBLTdC5TH7Eo4A-At1NaQE_AZynGtuyTl-megUVyRn7qEd6RY0ZwjXom5tstVAJOkkJI56KW6mmUFmZ-WwvgLu2dPCSc7q6gVPgykBkvQHdwGQLL6ygOqAyeTtlTTHEKxmM_m3dM61iA9xKYEjP2YMw38tMsUTRb-Dr3SXcdawXA-o6ZxHH2UVwMxZhmEBap6UZ3K9K442-n14HofKDS17FLCKTuPjPaztwSd-Bldr4xuY-rD5ho4425LnxqnPw1qqOO5r6-7HWtsLrFqcr0u488D29hnAJOIHu0ixemWhcn5cMc8S3KFyyRiN0WxRNcz4Y5Gm2RvPq0QzXah22Zae7dKEHk4dN204QHuPbyG6tkKaqpxyjA5k1mzw9yAm4nq0DSHVR-VeVPqOr3nqPRjQgG7KCtmPnKccHdiirf7-eO2g7u0YughNVUL09coONKtBk4aQUeygd_eXtV6W-qjdbrCAJI3cPM4qQopiHtHgwDvc7wmxDxsaw0itr37ZYoh6y4loq2hcvdm9iU5HZtnyvSI0kLg2zIjKgpey04q0mRc5Y7-snOoIjmE2NxPh7vXQBx-1q9QxH2qDK0451SrHs-eKsAe-nSqiOhWvn3_rETEsvdhKlipr1HVInU_aPwyokcQR16uS8CnlWzCKPBA7nyPqmhijBMgrdrOcLdo-2TwdA0OkR_C7SH-qSLqejWmtMDxXVA8-9JwrhiXhcBYbgxG7Ygzjd1ELtHfS6rxaiGxTiVIh1UGhSCHd3dKM9NlCeuphmMwTJoCeA17f76JEEMQZVLduwm3VXfnm6g6SWvOGaOd2nqVbYTZ_o-pM8mS3AZi6IVqwXm4_8WsD4DZ9ipfOVspUWSMCGAOAVd5IQsK7qstiFfVR6Y-RObBU4hsyuv95S1W01ZVYiWyhY91iNbo-_PVejZ9AzYf3fIAZ95yoJkm8y0j-OwVDCuUAL2fWc936ceNmLYxk4xOJnidRoCHvF7MCfTPeRLSA8t2EnRP03AP7fgXiU3gh059p7QQbSiokqzs2CEDpL0J4tCHDRCszRKTqTd22xAb_zIoOqfBWsKaDQmqJCVtiBx6eqdqJPeSN7mCNyDo_DxIyVcWqdv7jBajfXRDB-oFrPdht0Sff-_7pW52nj5VFRx-9gsBiX4TFFewRlMccX5ioKAuiE-ctDqN30ckcK59810PMzTRwE6LeIooND8-oe3IM1BE_UXwfZk2kFODr8zH2R2sQpvvUcOuoM3lQG5kxTATAL1cdpN-5QlgHEip9duxx9sJRl6xdbMhNcER50Zdyc9IecQ5PJIpfXsf4wcMJ6KJfpyVIRzQEuJNNDDdlp_ihfR5OqjV2SCzlWeazNBElZcnk_29qBnd-izZpROlzAtBASNsSdGH2cPYaDA5ecHQQ_aCfZc2o6q2bT8-W0ClqwOEyzR52JEEiUidk7t-YEqYSt8rFXrKx1LjY7bytDZ3osKVqeoOu4z143Bss2XNNSl1_ZFQUbVpZBkh7m46ocrjuzf4iLHeQ6wlXPdIgULLzwD7fZHpiUCe2o735HxRlHqH5EJAp60u2uXtf8kc8Y2pYdThHYtRp4seYJf7b7_Oly3M5h523jiqB2_fwyXpJzigtb0c_Nv2EttkhlPYfa8Xt5o3NCEx9Emg4HT9juF_L25uKu3K_iGWSHuFa3y-TsqIeEbn4quaPN-XgxLWOxMJ6pp9ri7l9JkwF3Qnst5uvS6sTt77d1CO4b7AgaZV7OEuPxaC6xbaOi8rFypV364AK7qXn_7usHWrMLH7gAzlWf6HTCr2_EMuuO3tvyyuxxHTkPUDDIvPc73vRgABGr4A253LtHC3FKO6aFgtZ6ywWAQZAtN-UAakHY9BEzKulHF0HoXB7oiWbEdqIP7v9aNcAET-DQroCqFpEN6axuCSz83O_D4Ww-hQFz_TlWXGod1nRX_4OtEfQgTMZyNaqFGJAi4ZtwJYRiKdp1wmjX3GwOukwCuhMmmPsV3B3uuFUzd9GS1hf8urZZRYbn7RgjWQZEXb-NjjzJ7dURGp89pqZklgaYKO-L7PwI0k0yJdTJUVwevx9hI3teNPDPbXD4q-UC-Wxv3w-VAO4zRCxVXfHFXrZMgXtEHcyjKt6QulV-yupgjOi1iYmVJ8AwM8LZOr06uaYxoBkvasZp-ZkQ5gRL2MvHIGzKAUZ5mqmVRwA2BubYKJvR0BXDMGDj0hCYC-y47IkR2a7_EVmKGRHQWl8TaYTHs2x3rAN6y8Z7G6SPfSGluosxgVM8V26LcSXny9wRGiL26YMCv_TkKn73N7iTvVjGDNFyNPNnYL6n8h_mHWKS3nEVCnjQ_dEY89E_PCEeqntpOSnxg1XVeI76OmrKD1eqidJSBIiTeCdz26NbBor3fKmmphvm1N8vx5svCckjWl6-Dm3QX46lVYtF4V8MLtYb5j-erT8rFJKEg_TjInVQEFMdvFd6aCussJME6VsmPj-nGHYHLIpq2uABj2dGSMiVupDhkOJu1j3-UU4Xn3i91Sv7XbYXs5gvlecUoLU4MVtuHomLvoMcHSuyak8SifaORILPdN5Uss_zp2TAvL6i2Z4uqjMb6fUGQB8XseA1ZbU9LTbygeWMCqwk__GpaWeECgYoQDm8JtzZDu95kC0U4sDhWdu5dLEtMqaXd46V0iQfdzvTMsaIHyc1j9cuQh-3bowWkuqbZRenY-txhjTXUSObMy8mcyNO9gZmx8TmA7D94LVEcZ3vgMo7uHAHHafUdtQ75oewSVVJrFFFrdFlHkF6PC6FHJKagoLGmLwCIUzBjEtMfVA6hzAxFgNXuQo1NsZfltsrzr03-kohZjCaLRPm4L2-a9Dt--vivBPS0-dfZ6fW6Z2DfKfUEV7pgAfrkep0VzrT4DMyUm5XfJMua0ChTuH0Q5xiUGdsF070Ra6y7RfOv2ZGWBKrTfO76oEno5rlWRdKdXlCi6_yMAsl4gI-Dvt5c1NfVWxq_ZQEj2rAnksF0iGNCkCmXoa0_TlBDwUj3wuQO2ZJdOAd2gpl0NV9hcAflnLZPIkF0YgT_meOBFbPvp_rLpXlsJMSgTNXX68im5aD5dFKXQqaHhpjNIfpVNzP-254YQGJBl0xohFs8rZLRvpyUkiZpMpjGeEIcIuWA3EEa0j6LxhrVhT6OqN5CYYc_I7F4OLz94XQxcTKYb7IQiV6tZl7PmSyvF9_Qetedkfs-3tgvIm03sStQSld_JzUEoZtMj2Jsux9_WSOzIXEByPAL68xb0Tk9ec-k3-UCV43o2aTERU2VkvheBoIU5xMp4wf3WtJ9NZAVKmvVqkPCf9ytfvqQcAtaMVDBZxulaCizqxU4cPuvZnUk2i2k76NEN5tIg6n20LiQjJ-u_TdzT4cGvebOcDmN5ABpYVJm46YvTphfpX8AoztF0fulOIeP8fRvi6CXGWBzzSrq9GHAzegAWhRRIKJ-8HHVrL-pIAIbHwrFlhDXZeZsA9XQ1SExdtV_ittk-akx_xOIuyiL6-K0-5SGfLwb-sspfqu-4ECLJec9bLZhltLZ0XXteJtai-NzRldNybrXPY6n06r1bYY_XFCgjGjM-TBx2hiAMnfj8HuiyRN1gx7pDBrAdy051A16o7-72nC97ORQPeh5GcHAfkCUasJNnkXATN6Ju6zZA5rpHtVHuai9UpVDtSwxmowwXpL-yhfUsVWM77iUeyiRKzKtGWm24D_TPMdxDRHbaJ7CIVu9cMsqjTbVpJtjTgx2e7lKirFGe9uVUb9UPKH2-hHgQ5fhdQAqR1zI_CyHOQ83N3zRvYCQ0aOd7QWVHvJS6fn3ojKsZER17gviUNhmMNJwKyC1YMD7O71TbEW5414rCtA2AMz8C2Hh4Uwm-Q5ABQ_s4mrfGeN3rjTurGPNsb8QlF_biNt8pprDtWiyv5HX9qaK8XXJwp0lm0VtYDrVBChfvRYQwFAkwiGQieHU6ritGqLQOlya20p84sAVewqfqi4qmM1xC4aTXlKea7x6FmyxbN0VGmg7DeCICUgY54g0xYw7Kv-VZ3kT09CQwKuBBF4su9n1GVmkjSZRmuaalnQKDyxspQ4qqKiOBm6b01kWYhbbEBc15qYbpmQazDz6ihblKezAohmKRi6ir8AzAEGno-FXeR4MA0ni5r_1tbzCgeiUKrV0LqsJO6Pc2-rveOEflM9_nZowj98ygtRviyCxvXw7xk-4dvsY81pvFmY30xiHLH4agMwK0jg-2hDH8qBsoY3l250U_6E8-gJKUCg0eovpfDD-S1bfd3CLzUWVvCfBmMyHq0Nz99_kkedATekzhc1_LFIlc-eYoMkQfIk3p3LhIpgnKlqIPTcVnp6GYi8-F2ltX-2LT9QqayCUEJgFE-RaHnDiIxMNbhcdgWIYIk7RvmYS8aV_0zFsiM3xs5hZspTVRnEeYqzLT4sWHi4F-kdttXzodtAq72T4Z7Z82XmRW6yuxE5JHHZJtAv1mlhH5VJvK3Ym9XGcj-c4qsqJCMTB5d8UwQFoFmZKDt-8VJ8T6wpLoaor-7JW9vld_349DFDOu9wyiq4a68ea302xIuaneHt3cPAILYmFqX_bKDLF7GhDG6uXqFTyuZjXuKiUZ87nUqLZhqiLpdUrE7eQ-P9v36vbMWaR4QE50w0cDD5YrwSxGmzH_u9545S_YfyBktuV26Cwe0USkdJqMR6YQfwIAohJv_SrsI-0_Ww1Y5rUJaK4UBxVbG3C8skDrvxMtNb13rE4KwHBdXvf9yWG6cTincth-pgBVcr_5Nk0LjM-D5s-GsUDf5LIXMpw-B18etF71BMJvT8NTV772s3SUT-AwsNwVvzZGd0oLmhEmt3yjmctrazkg5ucaILJPMzICjaWUKr60_2yPrh0xCejNHsnT3zwgk1c6d7CFZl6BAt9w2xh6PbNZdGlxebBRoMvrCPEzPHu8nBRR65PfZtJh-e7OOEba0-hrHlRCAvCIuISw97UJAD5lCGNT1bu-i4pe-M6hUV1fd78fpcAGA-0ZNbKxfmvSkmmJaD30chvnkIrbSR5QwP61M9QF0BcIrW2kgCovF_SgUl8i0QK1u19isEih38v5gqrj6irc_9W1j02zwBURdtyOySas8bwOQ4rHE_vkFd2lWnxPc8bEbs-5ZQR2u2B0pDLp9hxadH58uOcexBEMW89pRgt25Oa_I_qKFgyse1NrLNfLRun0_IKk4j3GZLdlwzpONhJ1IErhMdILU4vU56mRdfmYwfZSMhUuJeTm50Ng4jp1OiLl0z9cSyRP1eaxp_L6Xa--zjKlxD1sur4gfNktIHYb9DNAkuF5awpfVmj5boPUNTnqlDqKPCcIpOaj2sXapqovUXpqHN85LJai5xoYSC052-X2d_JXDD3IHZdkt7hbbSWA1NqegdIYLTEACJuSj_1mqx-Ya53fQJTmuhzuNhE4NLmIyyp2gi_Z9vzbp6PHe1Pl797qm_vQO2Oe1rvlSFTZR5NOjyVamog1o7oBsETpHbFVt81iqgWe4SqQP0OPBth_KUFapRdmej1QEd_XTKerLJu6qeKUSy0VSMaRvyZ-FWqTOhyNUG9C8ArV92vaGh8xm3h9XkTd8UU0zmLvjwhIXM3j3NekwNShQhIJRcZ0uauYAHv6xFlnnj3GvIHXmx_1alXz6A3EnhhnEf1VUf9hJZ3p6S5-Jh2dGx4QmewvgyImpY_E_F-D-LkIlAeVuMC2_jVBkJ7Rq-af4q93iNkHwM8JTBieh-nD9O3h-c7HEfY5S7H7r235_UtUdhw0FERRs03XvTk30nLXksPvHWgJbB9SUe4AS4obedAoDgMCvpBjBq0qgXFnGnB3VImMycssww_Vq3XjEG289HKOZEgcRO8mdR3d8EXz0eKyRdPSfxKXguHDepXkH8s2GwsnYQ0mEGY3EAxmsuV5W3Gt9-2HqyXdY5FrGzdzy1nWSAa-KDXFxmN06e90fXemUXiOqnu6ywsEWNLZONtjlsR-NH_T_MsTL8mLxoVkfnqJ0iKc20csHtPZSc17hD7O5AnC-FqnrMMOv0NJyiyCRGMQyAKTkyMyafu-99-6O86MSr_C43f7MMDl601v3T_5OW07fjHLi6iXwQOaD1zk0qa3pYb-ortIf0E7uCx6NaehkgYCA9e0LnoPmhmgZwTGIJ7hMFHtnwaSYMDhhagjVUH0x_N-hqIHfYE5gbB_ValHCoICpnlbNwStJPH7cTFYXP1aj9jIuJhQz96-ZFpSfeTM3p0YQNZ5ZTeMN7sDj3Erz_wQrOVdgV90eEUQvoRHhbNggB66fCSlvfbbd4dQj5-R1ok30oEUx9-eCqr02uDFrN5IAHISojAFbYtxmZJN9DAib8_iQ_yl-wczlyson_NtQqLobI7lbIm8J2wvCY_h5nxo4yr_6fukxphNgrTnFpIDqr7hPXAIwfys8O1NZ6N3zb5fELOUdW5t5zUcmFy5Np0euLtC2MT4zrXbPjD0Z_PxMo0Fh3qwGrhGe6j5oWlXlGwzuNfMBCsRorIT-54a7iPtsoCUYEXtHlRF4en2_N7smXTBNVS3Zan1m4GZmHUE3K12qOeQAGflYouSjeG7aFhnsn9PwdddTh8SnFhJ-rJ9hwGjqkdVsuDsPVr287lBmAgdJ-Hu2rMf7FZVwO5nPNKsAApTp43fltKoPsJaRyrKxwZpul-kyMz8EWbm2FdUqZ0gze_-t7fWhYeFABs5rtbd_BG_p8OHK3KP6l-TGo29KwwhMtZXkEiP6oEn8CwkIX4XUQB9k1KVs34f-5v5RK4-dBfQdR2Tl8DWaokLtXK6slCmxWH3sjWHksy7GMSCGi4M0JSlrfJ95FCwUAOmgqZ1QIANihm5NSb97Ox4-dMLxaZ5MAcY67YhKQPhpDtWEmAGFWICQUX1Z5VL-23Dik3Ji-xopvkyZUzH9Cxzt2Il_KQEwH5RgvJ8a1C3kngjtcS9baAPDBpSHxylm8CyqzPcy68fJFZRGeuLeILNb5mmI-_Axbur_9_GYxpCC4roXfwLhSTPvbB2EEDZ5IyHZ8b3TxUJBgov0bRkpIk2ek7tflg68as-YNEXQDysL3Kaw-0SPl55B6ay0J3RjoE_pfgdDmgD_FEDnd3fnZJ-UMG4IlIrd8VkmJGcfGM1MQhQKeJ8aeUYYlcip3pV6f4reBhNBEB2V5pRn5t2t4f7Eei73XdZyF7sHY9qIj5QFVysn44BXJfQBrXMPV6RAC3z9foKtQuaNCD_SV_6YNlNToMazzgxZ4vCvHfxt4883U-GV1OthYDazcsrweLq90Nu6eyGYFkDgYMJ59LenoSicHuJkPYtvh9Qfgp7IPEsw7aBpmJPORRo6vUlKMnt_DTdrmbfTdU47or2gH0KXt5gEYY8DDbwaWg-GJ3ubtEGAAzsQizJyZCxZqlmMzYbFqTw6UG8AJYzzhg0VrYZCvFuARHpN8tXiMt-nhSsgGCdlI8wKM6YW6jGCZb2dpeMkVdr1Xc1uHP7mjy0F_ssV_r2n43TAigTZ1jOG4oQxGt4VP33PlzUxHyuVz_nvekr_mw7CoYN67MpEGDlmTQhpfVCIdKAQNLvmgiIOYyZsBDozYpNaYEJkr8Po3Kp5heXIogrhrEIqKKUnirBt3GMpqs0D08i3NvMUksMHGAOw4ptehCe1u44xr2jlu4cSey52ws0q5QcNN0CMbdBKlCUO6qHtt_c6KrwmNt0UKa-moMfkil3bv6ZkmFqX4k4E3XpqGtsWikys5TlpOpYdsqgh-Jit4UgkF96BwCvrg31lwD35aOyuvJt1QeAWeZEQ-P2nAOOWM8BBR08g_qHm9TCDk8yLs8FfF4lluOL7XbJy4VMY8aKoODguiR8X31564GPVBsHqyUti8N3qQfU_DtKDe6JDw_3yjnAS-SrabhE9YALoUGNm0f9t9k5GN4wbS3MxmF6dQG_OVdUEnQTi1jTB_WzslbOE7Bn3CdTA_DtwQrB6KTJ8RnP4bUb0g5RFqjBoKLxdKjUWoS4oCtLVlNREeumpaTm60MtnFzAsTIBblGDBTkPAvIr1bQp3FRXCD0ysv2FeeSXfpK9I_UjTnNThDNeYh1XmMBoEXrpgb053NgEgN6HkKoaFQqpWX-9IZHFxWU2pgK3vg2Q1TgNIJjvPUHglXlz7PDJ-MyQdBhnWjAG43bUo_Ohr18oVbIXQBgF1LFUaIqfRIYi2cvZSpRZ7_LZ2jg8lOe2zxwVnemzcYEo2GWKBrW-1QrCdwHqu4Eu7Z2tBV338NgFhtGyZcTNanvLIs0ZgjwIkLrr3dB1jGrDyDyn2v0QQ8HQ9WfNolYCEbaue9753Bk02WTX3qcFAWS7Dia1akukx7Pt93frMfg0fx6x95-_y_wQWiLdmFC5Gcuq0Uo4ZhjDKIt0Qq0YarC_31nM-zf__EhEc_C19oO3q0y9-QJCY_LAlbaBVxGmv1ttVBgD2OonD8nj3FxhZkaHrnjxkDgCvdDaqIgLKjs5SWHGPTMMFIhvunhsghQpqWOk6UQhvCbbAf_tBBY64POSdStCEYUOU8xyFwA8bKv68egAKzirccMIy5yEil-00iIBrZj7AdWQU3sp5WdKAokYNJQMpOSs96K4l63IztbHBRkcoSxfAP2YUdSa-XMmldMWAYdt3qJhirmjKzDnVO1p6UaOnAw0k_L2vtc-bBEB5AjVzBwpVfs_q0gwFsO9V4LlHXo3lAlMNKSWjWlk0K7XPxuMsb13gzBEjcVAFOg6aEc8bb0pbthNr68C9X2ZCYC_uF2NY3bR9WgZtgP6Wn3sEo2QUVH9wFuZDC84JIQZxm8sgx5QAK2wMaX9hFZk-hZDTO_u-87x4GZCghrMxBxhc92PSbQqIUKGP3bodI-4VVOqg1usvYjzUpbXSkMJLS5pokil8f42qq2xemiPvM_YyLtjk3HdvFrCkCL6QM_FppSFaspxfL6BrE0tHgwuro1eqeHYD1eN43nHf7_uiiC5T01hE4x9evgHLo_54m-ws_694CbYa1h6Z8CfM4FIJ5Ga5OJVzYUTLWNunAhXEiFFVygH58cZK5XfhiVLfflb_YD3j2hd9wuTZ_XkAKae3r4qVWWHKxBBuNLcQVtqDrvuCkf60AqQSRkjhGLm49-pIgxLL7eqcGq0AROZ7u5B_HRTRgFHO91VWWgFu0UjvL632nBy6n5UKy96gHNqOdvg3s52stjkCP0r30N5VF_QLpNAHAG7W3osI9wXKALe7k4AIbp7IQjx3Jb6w6kJjgWXURpOXKvhcHYRY5jyv-yt6gYTMHkGLbyLnHTwrinlXB9BfAK9Sb77PY7oF8D_BG4Hvcs1CJcxaYAx-gLKiMW2jalToU1k5jyVzdnDs-gKAP6ESBZLwY2L6UylDN_vEimkzABsnjaq1AHUz1DKmQIQEalLPQMK4wBMvryiWzeHCuFYLxBF7cXBB8qStz_ThVKDnwnDTFcNc8UVU-IZ0sSLT54n3ORAqGATzUWMu9VncHAScxrsPnfteXBVKPUETCAo2xKTxvQqwp6UqRuU5dp89RCgoQk2pECWHFr1BDllGEiGfg-gbElpASScnQhDBc2nBLDT9F18-_pqcBNc_36xGOw_7SkzTFIZG4oKhNuYMBLH4xFWlX-vOlWw_52ddwg5nzcaY2cnFY4v47j-7W_tdo4fNV7tOcmEbs-AY9oIlTTAaW0nHT_A6C3hEVpS1TuJm5CKYhVPsAp2VTmbO260dczBg3CTXqyW6IqKX2s4EVDSU8M9ada4Ji1R1ROGqKlPTgGpuU8BA3zFr_II9-OPAGeKvnDuNbiFNV6E5gJvYh-dWU4NTTlMkjaAy0xXeB05cBpZCS3oamtTfpkLrIesFfyQgZBrpC3TX9ekaMtG68w31Uwp3Sn4ebNYzClXR8QL73MzmwQxqBLEClWa_Q-hhUmuBl2zfuzM2BQ7JTRoRR05qAvgjjbNj-MlucrNU7LS9I9yXkan7PM3QYaqP-ANlsEpjEVeTx6Jcsu5Ci9gu_4quU4FMsxKvjlTIF-z_05pi_cF-x78DsSHePnj6oCzPZrfGs01NkFx4ZSNBRvpqzFkVvVInrCWkW3E09s-kNEu8318W-BYc4b2kUbzt_J8idkoWsYwelteo111kC4TzqJJhyTmF73Tvkw2wPS056_hml7qJtJymVdRgrictqIEJkjn_rU7qPC62FYlpHv1z5CJ86sBPBsKK5roLi4zst6MPvqe7yJKEVQjlak_mUMo6VPjJ_2xHjWe-lgNnqirWAYFibxVooNLidBSY0w5jQgqbuZv_wS9m6BmTZ8CF48hUIVwxn6fgoAhAgx59ZqB7QP7rEsewE6NgEf5JmWAQRbn10B64-re905vQA9DuqojuTjnw7kxubP1YArAXoUkTejG-8RGwSeQrH82vDyqlS-jtJCSgsfyPAiyDy5fYzSoBeozqfiKyJtEDTwLzj902s0wwrRwxBR-0LM8EtR-ReZhpkkOwHb2ohyxLe9TZFplCYCNFoO6mDggEdPHKpZG_xUIjauV4PBDTlBAmL_ZvlOKoVkQ8xkNp-a1MChSdXfDnsm4-Uwb7MhhNaItmPLppgsYR03P-Kh0R1VmBD2MXvnIsBrQzPWmSxmJdcXvQOCgoObbheVkVHCIc3O4AEUNDm043KcZsvEtHgUYqjSt5HwAE8prRnsGkxYE9T_n28jy-PDWqtRTzv_kYig4EDa8LnALxJn4NSS3311wrlIFQtwiBAGDcangpKXuHEc47GJlcaibKZFojfuLnd4aQ9WbPGIn1xcQd8J1Q7OnmjavhIMldAfAYjCWviPCG5UAyIpfbZIx9BljmOa-XtqEQMxbGeIxS8ATrXFBXe97J6-jzZiyS140I7tjTmYR3-zVt013SH8InXczsOeSSXjNEUkeI2mHOLn5OKgpKZ2RFrlr5dA00EpT_IkIrN7wt-0xYZ6TayI0Pdrrw-0NVrj0k-EaVIZd5t5yVW6drWqcMgdHM_lNL0GgAF2BTRAWjC_uT9edF2aA7ZHUnEtt1L0U2tAlxz57LxBTv8r8NlZGfIBXBSheY_ih6c_TAdNfK0C3m6dg-NtvoPTgu9mde5e8L4TJ1EyBik9DDv7zRFxZf6a73u324NCwp4IQOO9_EJrcHvO1pQis5eQaywVucMGGqgwx3g4vOD2d6UWcKPMpoIDhfC-bpy_xdqeR419G9XcpJ3grIAvtyV0FYSF328hItKONBbOx9AZpMJBisMqEW6Y-41jsEN8zDNDj6hr8f2YyJw9zggIMPwNa8ACezHV9azE1YscSdmu6DLV2RnoQYMFsTnGQw-kbt2jvhQaoMfSApB-_P_CC2FCmTgI8K4BbPaMD2hSFgOay9Rxv-hJqsvTvmfRT0PhZ_JIoTRwvbsBcYU0e9mU6yBX364irYNXOg9iu7uMDo4_3FUrqZ8QUghTyot5yjVcjXiEBcgx20e9wcrs1bjDEMlfDGjWZUo2zgU1HRTMSVsHaj9qjUODJNHxxQnV9kOpZ08QqNqyFflCpg80kw7SoDVxtSomc85gwT_t8ubjug0KErjevxxzHuUVlc5BrC1w39WKRABL2HAG4pg4MYYAGnSxDRwDvsB40xjUKHTlwXrLeJvhJCxkuEJMOkwtranilOZIDQB5ULpIkQp2WTo3E2HNumQ7sskWoRkzN7LDwDHx6MYeUY_goOgynajjdYBJAA92YalPRC4VDXnMMNh-ipFC5j-6_6RhAAfZwlEjeWLnVorEtyBWBxImVIAJbT6lOOyyl7i9Igw4sn1EHcTIqcWU3MZqYcBRP0ygTFHbztN9GTEIOeQGZgz9LLJaH7KA1lLA2Jmm7SfY_a1lL5HyLmFH-hNO8gHgUslK_3gnlj-QRhcNeyxyV2Kua1JztWw0fQZHlvAEEMw4pnoCQwbq0aAMLCjngfEhcVIhfgj0_8FC5XwmPqPc9c9htuWGy0d3xnVFVDdSDO6s3-caAf_6KoECD_dwvlU93mkRybcvE-OrMX7_goPqBoAYtTuMaVYfjM6Jh0elb_xxW8N91SJ8ZqyvJRdcGIBpKTRzGzaEWAq3DaRcu0WqnStCnHZePUUmaw35JlQZ9t0ve-ubNVsWVV8ldf90VJFTiMhiO7D4B8EpZg4JrzJQCqcbDDXHtmGdADJ9lJzxWXRSNQ7-Yc0jMnbDNZsUHgXmPqy6VCYXtchxg_CSQ4PXdHJYiiPVuK7VgIfOJqM4JUVSD-NY8Jt-rjWICxEM98ccUrs2FdNXI_qQk850HcTm2LvEWKx0CodqJNAqs3S4jfcuXGAhBRmdBjUHG6cSewZPuFwNSLABlRYYulDHhZNxKVzMsoxgtOu30pcbtfvcUgq87uQiHVsv6fKH4iNh46zMz8geleG-ky2F34F8TUJEDVtAcbuMxO-sqKeNIL14aggT5KZTuuRgGQbArbsw9WEGeAb4byt_K9L5J4hCyOCGSWccjAkQ9d5_XIdf4Yz6T2qfEDoVdOucQ40-xHA10ETKMNwEo31xwLwGt9V2Gj7RbkWSLxAewqu7wGxbrbovSYUNf6SWUDMBD7AfzPw9Ief2aChVoiREOGPGbSBQNw5t0gA3o6rGIFHxrpNY3k_WXaQ7LcwxG1MeIeUVqycAFSaKI4pfttKJDJjMY9sMhktP3d1ECr_zLm_JPHyaTYb0GPMEruw_WTO4PJs4zvEjUhZ2uQDLe3ZukhWK5FoZ28S1_gocLVZPrNusKqRh7m6_LHLJKKd9dAgZcKpoUhjPlX5jluMZwFDRCHO-Byu4O3GAsn9NPwdiwi4vA5kznQyTW3CHpkqWR7KaclTXr5SauqtsMY1Gx9xUQ30is9RlNl3hZhmU6H4qwphsZDPnUwMC2sCZv9IXZx5qYm5bN9a-0kxqUVHyMxZIciBi-YVm-HBjqxBSihySZIW9qO3XdDdGlq-ZgQe121hC1gvD-pWWoQP6svQouykU7ymsKy7ODGX1fjudhknUPDHkJ48BJadA2Nr4ckHd9AXSbrv8xseltQQumv4-SQdGjqjLT7Aq-47ySVo2V-mCD_bTZV8B-NH0Fyu83RXX4QZffTpWishwdikA1nodfvnmk77gYxSZK-VhU4Ji2RTD1TfUb9PgnTwfuw8A0RtSMpyWGgT3eISmUhf7rctIwbDqSR1H6mgIuIQM7-0ilea9X2JeggGdphXI8J4cTCncPi9zKQKdtVakRGdMSpW5IW3D-86xmMOLIQj-tZ6KZdLNakG75IywM3kQM5HVjI6R46WQBBG9VIwEOlPE2C72BnoCu0rxK7wK4isk3uQhUAboIiHi_VXV5T-5oFc9QDNED59RSIpwn72mVdxst1GFQfSBf5b3Z1JvjYjT6ve_pv_bidJYiW0mgon5MHloOk2WjN-DmPfCW4eRYM6J-p2DvrQlIjfLbLYo3BLg4rtHwR4ceghjEBwwk59VqoC12PQ6AYR1up0rsYlhofIN7ns7QmbFyyNLVPrn-blETn5uo0jzzxSiP5FzB2mBjQR93-59Nz5q5OQegwOkUfV_vcwDyaeQpJT75601DXUQZGn2DsQXgne-hFc95mejcSwnyC8-K7QIKtBWvUeQ44p2Vr5mamh_CuVS7OlIhRXxAJOvZbyMBJPQvfzt_u8ND7urBLrQHy0rU89lV99uxbDQf8zYEwdHWBk-0dcoZJzDTMlMtY3j3UHJOsIDHdZgmrEDxDMWgUuEkfgjLuXNv33vR_LaKmecF_L2nvdiojeWGuOeTj5UOtN8tmbtR0GAKXe-Fi9IzlXw81XAr3cT1pvq-3FzXGIA5hGAkfW7xcwNmyyz8hNtWbnfQivJOapC_GgOiCpV9DXpq49oZbwdFCmOwHT4FoF1O3sbRruPhGWpPwQlYeX1Te5D-F2-r72B7RIj1a6azGbjroj7RAJjhHhhriQ3WFyiS-HDwAo5qj9zmttA55BbbbNbKNJarjFLRdKuTdvARyn0MzRv4h6f8gK5A1U-jnFDfrpNeFdWeeHSBQIuvBh7IB4XdPw33oapVlXfDkYW4sCu191olcqgJW3xuKafZ3VFoP04fBgzyw8EIuHDHGgNTbqPUMNUBnY3qKXd9JtARZIKfPqVkVYZYWrmhLakvv37qKrnG99KLuwADgT_bOwj7izF21IyOS5yMAd0ZQ0uZWbHGgMfuOvQ7mCg_Hs1Fl7IZ9CrGt38vrMaW0zkpx7u4IqIcb8c4juoqggSe2LtJXn_9HEPKibxHqp3zZ4E8nwdnRztOE0U0tt7IpRjRVMptQm1p5kMT2voJyAbtoNW-7C94qOJj0BsTE64uTJnMMEcC4zlTztFwoI5KERW7vgtawzWVL_KQOK9ukx6zC15YlM_K6GpAhlxQIU5QdcwdCjrDASI06jEBTcBtcmObmII0woRHMFsibN7XPTxWsZT8Ok2pEacqqufEo5Ra7f0WPAA-4d96vJoyiKud84t8uKsCdsc-JBusurccLBI1nhC4-7L1obTdsimXCuFHH3fJdj_SMONBmL_t0j5k56vbr_APBL99U5lRucRig2v1q0I-3g4dKcAEm1REbp5DoyIJmppmTaZu_rMGvzcxep6_VQuTYp1QB3a0sjVB44C2tG6Di5xjkk96-KW9N08fT8eIKNXiS4KBt_vsHtMD_B5sdx7H5z2d5v4Bvb3hwQ0BZaSLLQGHattBaaaYYP4oY5bZh2atJgDbgM1jGOsv4t1CIPjYgycfjhrUmEzjp4mkwmFeKS0bFHThy2TgZxyuTXHYh9sAqAlY8hKPsqNADGPHdJkajGnx7AOjcc0YBKiVvovr9mm1_DxcOcM2B2jjtBNlIC0JC0d0BrU7wQSYFQa51hY27sFkftDY39K0kasw0mPQLmHxtfIo9aS6vVNhorMrnqovTG_PW2OBUUqiC_WO6whnLi-rma6XVgEzzvpIlJTQGcd0nQVrTt57Edy5CVcBi7IRXIqjyKIdCvRiupgHcKJqtpyLISbjKAd1lPqKH2BDbaXpw3iTh4lkyBuvxOFTkobaU9SfR02WO8K1xW9geHxhyiDpgLL7rxgoWds3yQKZwZiJmSaC19lrdfskYbGHw-8JSN1DHcN0xHo82hN5O6Bt0p5Xz8UQIuacS3I8nNQy7gtg83GpgWr9dhfExFX2JqgovK1IbU6BxwRPO_J7yJ_dVD5K6Al48ePll2Bao5VG0WWbD5eXBgGBPqrfK8PLqN283gILoa-R84q4ajrN0nDeMUFOeClApB2IIN9ZOVOfkR8OhT4bdhy9UKCd3taIo2ZKbkI6ng7-F06QE-UQM8iau4fAaeQVN8Y-vfqM_sTQ1VLTDWTkysqIRAAA3vTvVgvlNlejnOCPW_BiBV_1DTNJMNcaUXxc3WhNQCues4KzrpsJbMhHlvgVqDFfyh-eKAeqznMoLJ6SA4X0r-8H5mcj6oZOWVrjvOLwLrDkvbjkhiBJ9YyljteybQbo7YgBJpLpIOm7zyP9AD4eeXA9k7Xfh_76AAEIRWxjuEBPNJdDJ2CL17fWRFCiz8KMnr_RHv6QyYxtUDgjKyOJHv3-mv25kBHNWwWnv_KsWNvjI4Oequu2DJF0g0EpZFenMlP5r5-ayePjYGUZz6h4QprwvNWp_pxKVhD1j_l-jkELhyffGtFJk_mGw9XpUFM04_DyMv1WnC_u7lF8JmUlMnPkC6gj6vcUq4-cM6kcecftITINBFeFqnMM9wXPgWkxUxWm0QHL47ZDYYSvOBf50qTuOUBGxmYVOQMfmZjgB2kTTm-BpeZ98RfK9HQ55Yqyaq5A3quVBeZpQiZ2wx52uVDbW9hPVY5-3rTW_NWzEVU4P5NHV_vtVcxYHoUhb78rhrGXHcBkzzlEgatllxTw1KL1s07BE7h_WiOaNGcxQF15p915nPp_ieStieYggYmkjkXV4qy_LG8mdRfSf8ZMfQay-iLQOZmnDN_V6jvQ6Ct5PS6k_LbYi9DYC1ZVoNIXarWkF7dcM3eEoQwuAurnJdIzkKKwuOpJvxd5a0tMCaxNN8zlSgoXwD4E2mBuvYs3eAC1LUnyO9UX2ZfPSVXXiWluzPIOnQTjfZfSEvUTSTjBgMrjVHkZATbygrGS87wgvmw3nocKtO5ByMW6bEVXII51UvpcfjwmEpsmIMmkW_IN6fO4qwwiZRpFRIHSaEB9rO5d4CjMT48RB-vj11s1IpupTwB0-jI4wpqTQTigPs1SnHh9tR_ZH73mnX0Nl0YiZcSir3P-xaDOMhwO9YDhNSRzTv-8CKmU9YTJwpt6CFTvj9-2cbkPCnWTVOCmJZut2OtoaNkXLp61gGpjj52tKbyb1gouPO966mizOwAXacvxzoPI_XqPNW0sP90QlkAL9CXNTh_ImpP_LsBlRmFD6u_BDNAbb8CEBKQaSzCYUrw2gkt8stNXY0TFP4072zdboVoUkxetxoQPyBce5GqLvoutFtUzd5eZFO3OuuAgTF1_DaMYFKahSBcQA4MUCbIWAFbnHSiodwdOkKIekfBDSkxDW5kbiZnPHtVThkG7XDAQJ2fvhAr-CP-fiI2_NBE377eAVGDzvSFgVbxuq4inkZtnmDBrdRoGwDcv154tBmpVN7k9VarXD5-pDTHDuS5Y8Jauyy10hrbrPF8I0z13yUeE4RxWbdqqujNmG908pl88_AXd70hac9ovgLxIFyccq5S6ckAooh9lbrRATD80pA918tDflnS9EjGYYgsKsqDG0jMY45wLdecSn0TpKSuIhcwRY5RW6EumE8V_qbgTUa6T-urToyKFXaGzIJps21xFQ3QI5VcT7hLiI7ZQ_T2WZRu_JTCrXSNww9TxoAyrnktZcGv5lVGBOvHxpNfZyJSgIhv_vZ4vXHZ6gtZhd12eqnPxXPQkwU7uUeSAbQDJ2L1EReW7uB5mZH2oCX7jZCaMf4pVJuWMlePE6K0XAHDAdeq0Pp-cIRagoxZAYC0U5OidXQ3ILv8zj6xy77VnLchsQ87emb2PJZde_Eyti56InTZAERsDhkuuy16bA2Ovxuia4cdTCJaE422F-pUeNGiKPBTBiBZuwyJgj7vrQ8FhT6F49t3PV8dGou44aeNj6F3EpZzVQoixhAYDtbqSd7krNqL-VnEMWyVqo7bV6mJ56JwGSfE28oM7EqRuYFw21a9H8_2drfnGBBHJAZPRdVNEZ36soTDoKhB85c75hcoeDcf0A_7SZiZvi3lhFNHmRWKvcWaiJHbeitdtLyYki9CvoT4aO4oqzkqu1jQ041oJ_Nv-62Bmz_8OQnNf4gu_9puM_5ZlrzyH9Kv80vqc5mIAaaVDyUb8RxYDA35ztXd_bivFfML8KmFKvis4iHYq2HZ5JZg5E_FiBEt2QC3q-LGssDmGOhoCqBPQjxo8qVaz59IWBvw_4wa_g1PEjcSkbPHgMB9ZA_1204iIN5dQI7wQvnD3uzDd8vfKCJD8Ec4tf4QB-uZq92S8g9Hlvv1XJuvEAMvwnwZ-juwPtILZK9tDKIOobcZgfKfebGbKKlLIg2PTQ_kE9stj2oQqYuLQy5sQ5PvdMNXpphVkZ8u5NaKLmVvaUsn1gaLj-7Y0X3aQS5uKEYur54CtSl-GjNU2acCUIa4JHC76hnpli0EyX2Ev6Uzxy6totHQpj1vWZGcc7lR3WDc1bhcDgLlauZQnQlokt8aJzWc6P27riY0xJVnQw64CiT7ZPBFbdPyKVcFUn-3lXvgZ-1eQXLnj76WbNIjXqKq8VhCfk5W3GpYcg0SuYwn2O25c-PhJt3gnbb_gkzACBOWGLgQT0WsJYRP9yJGQE5Vl8yKQXKSt_AUMcvLLfjQAOeaWTup6SPVMRziFnrfqZUyv3ZKPDfK8RNGU3n1Qtui15SIAGXgTf0emOi3d-82cIRofJc1BGmJsyvcFpBuUSYP4PLEZ0sqxSWBKX2MrvIEa6Td28Xy1tU1eavvwXU7cCvozlcbs6G9vb1d04WkOYGMjt5MVhEt95Y5EbJ0rDwaO_RmO-_b-jtKaXyoK156834hPOLFhGK1L4VqnqWanMyFit70VBHnfXMGefBsr4rUqa5p9jOmqkTl4NOJmDhPhsOtsminOOKcEa_UaVK42yIipHM4uAZyOjb8wf910KHnK8I7ehD03URv0QYmcmPLwkZ-hE8w7tXlguN4D4kfdOyX7eDnPTnC_gOv4LmyJzVUxUs_iKhbSwJk1Zo66Ja3fSMQ8C_O5l4euExW2Q6fPA-_uhT9VWiH6iA6ogS78ipV0pUQNKKxpVVgO3dwWmXBe7pWomH6ktZtZaYSxl7MZosEYbDrnU6inbycNAS7df7tTKo1Ds_UGUR7A3LnjL8CdBrdBhkPn5fwZd9aclEHllnoOyVf2NN57B-eML7iZ1IdtarlUtMWm1bwuIsLRrzCj5V85gPkcDh3crTHRuyORrHgEtkhPaWHbpiUgxFU3HIkiprAfmkRpkN2CGOqSUzI47nCeDr8LAt4uzxr0gFe2mMxnbDoVbfsYOrF2QujOedAWno_jGOt8UX2TbC4XI0aw02_bUld4hZ4QEQdx63MQfSXibpr1zovePYn2MUeAXh5SHgk4dxHzgKwJB6T65cFNrAPATFJiXYLQYyACTMqp2iOydt1osYmxA4WZ2EUEYHy1ZY6GtF7balM1vLFu9Bj9RBfAd3b_nhl2oUpafOuM85zmM4Z4jidcCI-Wy2w4z5_zhxgztu2Qn439ttXc_pXZf1E2XeuVEAMGFfdKKRJEnHdAOx6BJk7kHq40IE5mNVS2ZrEw34BufBn5cADVO_OHZLEtH1lqJ8bTcKXqzXRCQqD9KGUk7VJ6_PbSQJB6YoUJpdL1EoFc9w9DV-0WKpDccb2v5hZxt_56qFX6wNLbm37RzJcp4jvSkFGYw06YKXwe1bX8nqi0SkaHv56kw4CQViJFXMIyUSPx3Jfk5-8b5L1f4EJ-0EvIrIEoj5m0xrPkHwL8UdWYt20RL8bhwFf4I1oN9nWTGf7j-7hhocftSawh7q2FCvHbyDy4Dern1ZdDcj4GmMAs8cqLoklZ5WZgDZfE7Z8KvymhE9w0Y_C2BIFPbtOU3fq5SpKez1eS3UOXax724WOf06_qcoB9zsh1HGTDn5Fs9qDzajcM4mAam3iuJ3H4uGHyk6v61luEzEc_UUxqz5zmDGRkit0jwRnSOSU2lmKLngO6L5_1Q-XK5g0VpfLM9DoC8PIbqEMbLXeE2vJf8YbvEDHX4y4f-jYJHKBMPO_dfLHuDtD6gAJP--zAZhQ7QWBmZT94hIcZi20m1U92DOwU_Lse7JLvGsdDLchj0mhgFUEkmrWd0udcwy4MUJmTnc8YPiMnmUwfW9A6zcUPdsB07nw0K-iLXfgNFFMgY9I14sUa7ZMUqClCexa87rx1uAZjzhh19YpTxrpvifp5neCjSRJfmQ54YgMdpGkHxuSpNzj6rC5H6zS0FLdYWhdQmev30dFoalo5n4-s117y4juM2iUdGjFxkEodmwyarS3rKg7pvITlcWls4PfQ-o3bjKTTlCIdtZS8zk_4nZTCil1FX5e0wPZtTAdxKPsuI2mOptxkw6FAyGwHJGEtKRNPktI7ZBHXCF5IoS1tpGVuw7nqHZKx4gvAmMS7q6tZJZrfuKH95GPvdtaUYF1sgs-xz_xEg_9M-wY4kovil8pkPkoT3uHeXDD_e2EJd3LvbYnKXY-EHqTqTgS7mVSnRpDfkFCyEz_KLM1lUcDC5PJNe75aDuW7wt5hsqAiNttDZBSPVglam04J2IdmJldIT-PjoaM8786aFGG308yMFGKnjjcQfM7uagq1ujxXeyQtl0N_qE_llXMf3izooHcjweaSRIReB-J5yyNXvCRXrTQn46oiFqfuvKQGBtrvoNcAUuBrHzvNn14tNjeYtE7sCRMqaa0SRkRE7LQ5-3b7cHSaMo-zS6_I1xWA7j0Seyyft2aFuQu4JFe6k0OFGiUodGm0WJ-9RtzRkDegu85VPs9Z_BJAd29vhC2aThtCWjuO8Cg5htWMobdwbqFfGuDGEbZPH6FY6zkczphZ1ivEkcOX0sRU1BVvw3qgj4ir2TBDbYN1l7upoV7jidf11BsT3ce1Iko3t8bUUupxhEAgkwkvlsf0grBxYLxUhkTaxB6ESXtLU3HQigULoflTcUIl5SPUrhP8ag5s2ijbxOdU_tvwsDbuyFRYqv9l-ugy9nO81cqVSj2cjcTetoLEqdv8TwaaOuZn6DWB0wK0kY3AXHc0lwfjNNfRC0QodzkAYl27CwaNyUAnMniKVAOI_w0d2x3ooyN50f27AtJhV6ypFp2E5Gjkpf0NFDw0NyKwNxHGVAHYNo5gavqhkdgcOYANPXQjK-Bcs3thSMqlWchjq7MJGvtzxoRB4pDtIcEyGpi0LIM3v3DGmI0CNazuAaBvOUbcvUQQDAxmaqH09IPoysltgX2IZo5mqraLn2BCbg3QjyA9qTCNgUDGjGp5wC3mPh1sgNiwLhAm93NGPq8AOCViNrhEooNLcmq06iVDdCGbkKKJpYUSKvSX_oIqLrHn6Cz7_QPctV25MK26JnWRGF2ZT9yam8CgFXJr77JFCczlFBhkbuIqyAl3ghw4tP0RAlK7XSi4zNV-ATgZoUYdYzrTp3FCw5gvgHFypo8e87EjORzdACW--zo1s05XLeMEvUDfjK6nLNB9OCvifMPNEyYt0xuCV8A4MWhnHtRRGhcWgJpU0x01XepaPZVcbIUrBJ-PGy4-4Dzf1CtYyB51TQgLHJiJFEFuDhBReRpFBnhCyHI0MK5YGe6Pxu5kjBqF8j_GNkVeIsasLYSQvaPITOUp0N_Afyhr6Mwy2LNTb05U0njtXtiWKXtFRF88Jg8t_hzX4EHtv6XcmibTC6PgqC98CywuBp2bLr0MHazCoIqXEvHnOCby7Xhr9uD0i0YSysE9IKZWYoLFpaILg2BxD_SiDetS-b3ibkoiQE-r5zdKqtu0OI_t9ZeKUZzyJRgTCBJdiBr3kSEF9YjgFybO6gWimDKSmyPQM-orMnQpv6JRWVORvZRpW04YUayxE9zm292c7G17Q6XLDp_MoNnIODiYr4QyH-PLJeyS0JMux1zk9WpZaHBI37z47X0rO6W2pkd9VdEK26i3Lum2YUKXxLMbDLzhdLTf3Zq-0TN9G5DiVrNp9nwo2ANuKDVtqTO_8Kmk1IsbL5lbAGBldQ3YENpQ5XyK2T4DGEdD9CPJIuRjvn365w6gmJVyMAEzgfVusSB4Q6ebUyMD8vTAxZ5NhsF3vvyXloP3MqZoxULs93tl7-uJmAtI0t4J7ojMPlAkq4H1QQvlOekJSbLP_xYy0LEbrWavMlpyQ7VcYERi8dcx7KKqMDBwhhSKSmd3KXzxltYxRKkpwLS7Bffnguu9gSggEwmxe9MD0RHP_l77jUbTsliOBL_bgBbnlYlrmAIv_Gbyog3aLHAp0F-pgJOmM4fkdYmhODCUjOaK4YkVOSo7SXCdM1CKKUYjtTWukJODe-Af5oAzO-XYDLpgI1SQA6qzpNoA5YC3DVIBbuQsMyjoiSAvbs_icEO86MYEWPcQ-DnWVhG-8cAFbDt0RMekOu1gTsBZtbpdXm8BXr6_Yh-kLLtU626v9cCoQ3l1jFsdAWwxU_wCYZi_m3s91dAIett9OJ2FhcN5wLf865kFuPoTjn4x9WqxXkVcWlbc0bgYxOigbxUgdR7SZqqRwlzFxMgJFbPnDL0W8dwZ2DIzsKHCp5_4CsqhuNxqB450dqgF7v1xkOJpa8oP4HBM0Sz40g68dYuQ7TUcDA2JfdPoqUsJIghOGA6s8Gaf0lveeu-iuSzZAtOU25Phi5NIltzGxSCakz3FMBoCM7U-jUw7DgUNyyZCn7pGbIECSc-k_fcjFGG2vvKFGfX64_ehqkLCQTjJBhjq7vUVdxpxT7eBG5KBmnDemWiCi-2brVyvBuqHviCbrC9IT0T_rC1h1KX7QrUpgkhMkplZjXkEC4xjZCKH-hlIKUcuzYtDs8qm15CkQWU0fapheaitNUMEGAoOphxN02nQdkXNtYcNdo4cPQiLjpkXJQKUyZkjwXMvK2wrADCZu1ZbXOOi0Nu5hLEAxEw9ZtcY1PexWtEl9LbIKMWz3K1sQhkCQ0JUgNkSiEtrKYmPuuJjpDkjPAo99XKpAb3G5cyL3l0AYWKVMhzhe1fkcYEZ3enEnfhprp9ZqaEp52DcESRShmiV09BwX4Z3fP7YCwQlzGvPQfbszPNSOV9mY1ya1cLuVjB3lvtlStl1pI7ZHFYl-WwiNTOxsqFPZv35zzdPe0oS4heCwmBYbKQm_VrXuwinEvPQErg1aKjbPBMN8DX8iYiKTqu5DBWo3UviZofo-wGswyBpuDYjgn54qg6OkyftxsCge5eyc5cdFH2_-qMr4hOqARV3ri79nFcDnF2VDUWxUFy6bfAFq6_jQRnKFKX3qkjtowpqt-kjIqZ6zRDZzq2GLjQk3aIlnwOAgOfIQ9P88L2jNVZTkzxT9CXagE4ElVPVYrwtDT6RA3CWcTmCIYLM7BZX5EAMR6DSkxyZGpngQt0SZvTWW0qKsgEVgL5vgReBeO_eMQw6EqxyKQypozlIMrtNCwRIP2MMstifmSWbPnW8OSd2-MV5c5CQf74-ltB4La8lAFEDHBdtfoNnGIFqNobMZemFTtzL8iTZ9-ddXe7_Di1UrBISncyCrtBxic3wsw0MroKidY1sXbN9MslVpntG3MyNjtciyBejDWbtaAIdHQhGCf-s5SQOhIGA42Lw0O8Ayk3bpy7iCoVRoIHCT0T38CTMhNTUi8kwokbfeU4x52LeBfUeWqnJkK4_Ilwx7KawvOrOTAg6mFtpWlWqMtpqnKieK-zveXxnCx6x2MjCUL0vBtp06Yc_p75mfq2BrtOziZHArhLZru4nlpZMfu1LN5dfkbhFfO6rRtec1VxUB920W2AGbacgD_I6AKHybPS9UiHQknz7sqRfCr7N6Qub_DuiceN4d5F2TBye6RZpDLfuIgPYyZMaNaLwBjZebriq9AYzWtNwkbZaJsZxy93Nx29z1epDh2p0YDYIzZkMAcOhXuhwld8KYpqpOEbcAFc6wewcF939avvTQTgWCErGSRqlqBjcwwgeFMD8thWo7ZoTmkMX-d_v4xc6VYcuu2Ss6fu4rgFXLNtrTy9NXqaEshMWevCy-SdwoM5X2DPzXQxJhSCkGvGgkz4Omht6TOCke_LlIqav4lDPTCWCmig6BSBTnqQ5NTRuM7CQ9kpd1AzTHuT1p_jLLNVJfWi3dLqV8w8HPP0nKoZciQJ9Z4KSzkjyJrqmF98z1O0HHDu6Q67Puc8F69J2Fr0hLk3ej0ie81coglXeJLaTsKxtMRQ-L0jbGR1GjTYD07woDp9PgDJDf1xlJR5lekJt66lG9XTAjMEzwIVWqWX_5Abs45h3nK9IwqRBabHXXzTRRrZLUatQxAxgmd7c615hh4LrA3gdlg7VxW4OGHpetuik7-IY8D4bFqmcMKyOmAtx0tqpVloP570VqFrthJjD9O8BaBpqDKJGsU3KDUxCdsZ0hfRt9SMHIQBbtYsk6pUFZDBK_Z0JRX5XlZgN0edT9CjsLoW1IFzbCPuqRW-14oUOpy9Bpc4vG2ByWv8gTm-x8_qOhgnJ8GOEAvoIKkNhGydmm7V7Eul7VuJ7_i0UpL77rWv3S2MTW2KNVujNUMzesmpSwgUEI4pGST4pau06AhZTQ8u-l8inIv83day_0K-WiL106aFbL3pDAq6yLsKNO-ISTGyNBHZrmBqposjM44ul4EwVwWCwisQHOMWVPLNpQsC8JYWx2GWShm_7KHlRia_nTAKd7Q7gj1hv5zuS4F-07Y-YObWYUgLueqZbtME7dzEeV-zWAXAXQdGfrfr34koCCDj3hi0ct7EdLxN2ZcNieo-3MfNKN391jCIHCPfqyNm8NVUryMkUh09SvqM4fIoWUdx3AngOBRjGeip9ZziwGXDLuLnIsnQFshAMi_5qNMwUBY7PGmxvt3jPKHnxP2N5rg05rRyVWL6_MjpsT8hgsOgAZxWT3vTFBFrx_UIc6mhlwWpj3k-mh0r3kNsXiT7mExj-TNtCwXZIhIrK3DVfDP2m6EiAECMmkBErKmjxkunAepBwCo4u5j_2-nR_bAKFKp4HQ6DiNOe-lJ38z76VaqasYIyjmMcL2pjttjzIgWNiPPVKZgpMW2LG_mejppfENDGgRtXEeLHuRiAAFAAsOOmd4vahZ5MtLL0tPxLT1arNamy_u-ohXb4djCwDXHZeIp_IR8arDXNhwDsAIoCQKhqJytQSuWwJjUDpkT6nWW3iPiPvXEjEBUaYA9Xp0SXFVb6dBI4kI-LqOoxnnur7tarxWbaPXrmH4eBvNBYHbcq06L4HowCcknAhqOg-v5ZdweUs44FI91ZJnnIETLclN5G-7hddFkWXFYLItnUPSFIYcUEceTKLfCX9erLu-l_ZNBo71s0fBtt8uSXtDC9VM9SS7tlNE2NNpgBC7lGYcX3T-SgZJ0pBg_ZlO2CXlL6iOSlt-ypRgNWClZTCqsPYJhpMHzHW3A74Egn4uSmOirALz_2GjTEPcilyNKCr_aIYUxBjqQziDUzmiNnSZWf49bfzLMDC12v7PXf2NBOjxq6Bxt43G4dGsONnzFwGRj9vR16vRSxQMWx_t0t3Y-qfzTneR6Hf0LaH5Fxnl6bo2dv4C4CgKRB8fr3INxDLrBSbIenAFF8dIOQs_ZEGg62yOkg2usQTrpMfpNbSsKnYCnVCwNJTxaego_lrcZZOKO66G6u64-GomjlUAOld36CfpC_DUZv6ObOaNbZrzuNpXhnm-jL1qRq9wivDtH0XxdmnPvaYpnkjlo7iRtmmjUG44hoUS0O_uHHBgJtFQMXiMkdyJlrm5cUhW46PE9sJS7Ou1yDlS4ja_eq61NXswYhix_toVT3-mZgUrzxY8Jywt-4AfpkqeqOXFGky9sdcQU3hH1FWZwSfOSzeM6RJro_oztp41FPfmpz4fRSqyjthU_y3Od-9PJSq9Id-qholnAUUTUsZb-YAjLCDBjZqs-lEKOD7lzhWxewKH6Xn0-2EBXTx5SjBWsSW1ANMhVIZUI_L-QvAYXVhTDLTi38DBJ7iDjLaevy84rH02nBYbnpdOlzeZle8-7qCehLGaXAEnEHgs9zHIIPeDiShKlsrtrOFc5c33E3URBvoeDaYOZc7Db1WrFGGE2HI6F2AqF30z04gt7vYSomMK1Fc69E0WZOZW5eGqyM7jlZ5dxb6GaAtst8ObUqIMJF6asbro_jVXMqqR6KOWlRo8aafbU2Fqnq2C5dXdvo0SJJfgwpKP7E1R-uaKfe5PL4OBsw5NYXS9Ffm1TaUVYV5xYdJWs_d723dF5d8S77cGaA3YqYoHmiY139Kos6pGoMUIaxjvqS9UvN3tcy0fntnHRNTXU_TpBYW0CKVGCRKsv1q6j4VbIFmhrgfsb7xyCQv294PHVGXVOj2ItzOblfO7Mc4YCDdluZAz8DwvpRub1t51u14hTvdmvku_DadPEK2OHj1mVujZJLfmA52USeDpFZ3eRi39ku8wKSH8W0MRL48jipZvntb9Qoj8hoHBeVPtT8_lnVyvEOZyAsBA5ndHe1WawQBljatwBTIeDs24lQ7364q88YhQFvultKUfphxLeyP8u870vrOmz5WqZ26t0xuMSa5hruSlV1kMEfv1voi9llRl2JgInBZBqEzuQUr_xhOdYoqiqnJIR-iCGsxl-RgHkyNUkiIEjSVt9NUEwiMzlI3hcS3xqS0eY0oH61soJN6EYzQCVA-M4u34SdasYfCkTJGlME35tzTckDoafs1bsdi-meQRihUaWIuUBoLPdcVMnlqNrP5dhgBygaDtK07oqCZ7AAGu8p1u_ba7rE-kCO6gtII_LciLi1Jkgg5MW-7RhMIC_h9F-hQ90cUJZefeElWRz_MWfWvabfG76PR5NCQKXKNmd0_o0rFLjqZiy2qFn5XFv20zsFnc9u1iUMJ0Hf8PdAGY-mpvX5ey8_lzWnwmZmq6WFiXxzVEeT4xaA-kT4V0pmvCY_fjR_loT2ZDx0GmY_uYqPe5XjoXEq9vjeS7eaNsJRWbxn7_uI-SnXH-52RCkX-ernpYkpsZ6qKDUtGijGgKk5--ArQrZh1vjiCmtFaIiPjQbda70KFPVnkeSUjmmBnrHgZGJJLNYM0YIPMEFVksO1lSg2xMljLex8A6GaC9KK-3S1Sd-BhdSNGmok3qdfKvDnOHflef2ay0BK6wNVWOAV4ali19dTS6ZTxgybBOm1TXF7TawzhdsiQCJjUW4dxgPav66mqzv4Z98RYkt80w2w1DSGoYR1xkg6lPQCc1IoXdMMkEoDgDzE5R5Bk8u4V7wxngPc8qnleC0YNFJ03mRq1qr5I_ssrlN506kFrnWfNH9zwMArmiXypQltpe1LGH66xN7rFTlobmgGasqVpRVauvw4NF2mp6DvFp3wWddiBsx8znsqeQV5I160i-LpawO0m9QQIYyoWPkIlp7cB0EuDBHhI0n41ayTbnjoRsEkbGB7UaFvMM91mhdb0Rb0HD-0R_xpTfxnoQShIHUAxKI5tk_oZ6Om4ipjnalJc_6yrsACFEuzkWT9WLX4WOghn75Re7OFRhzLEShfuRqhfRlunw6LvhovrI61qvCJblNpCZmUhBBHyHxLnanQ4KwUlgf4huWfzgyB-7R4XM1YxCXALjuxnhjQDjqDDEGv0GUxkdf3bYNdcE2t609UMCjAdaDcBQa7OX-ivuY3IlctyFYElmg6ghYMUoxDXvT2uEUiM8lj17Nb7ZfqeHpuisrmuwe2N1OqEAwM4anUAUuEXzt4FDaUfcZTttWD2sPgu7jTpLtdt28WH77Iaou56pMFtftuFqbGjMVwW9rad60KuY5fJ8K07cA3tp-VfY1uUdASoriTVk_OzBQYpEKtT5FN1Z5p4sDnXf1rLz_EXUyR_RnPFLZUGitcFNGqJOj8NmLTzY4rB7BtXlMF_Z5cWURX7syfa8qKnpk3luq66y18sKgVTQT5QQ6VwQCm66x3k7RI6nvRRx0sMiQx7wEB99vt2FlJXRgXTXR7OnGEO8djfd8SgxSi5OyI4e6kjzqCsxto1f6WunpN8rrWceDnTCnzAQ2A29uv9wpzlYygpEtUCAKxsVUV5eyJPWvrhWrrvR_3DmYstK7rz0nuNpQQfVvwFmCLUbMRSbEOegYQgAJV2Djh9x7uWNJSxWFa7_V4qLZUmPS6E8XbEVuHT9DwuR3obmKPtZhU8vDH3Zm81pVa0cpc9NyGpFdtH5mFs0sCgOnFiUKm6CMkEtoHAXzBYmRlzjNdo5rtM11WwVCwmdk09stRcV66wzA_JMR9vG2Mz1-upgaR-dYO7HtLqi_h6eIYwFs0q0-Ly2schH90cZ9AYm6ERWnG4NvvHb93guMre3C6GerGaH2qZ0334Y_difcGc6k18pETcFJcK6a-A5ruFljIt_-RUi8NB7R-45gHc--qbYHRfyapqLjZAt_GIMyaMeGqw6cdrWfZvX0y40sUAx6tjiCG7qUNRV6ccqrsTb6J0pWrrhsAIjWQFOcSPKHC7ezRDdwan8bSaEoJWqgUu6coBG9Nfhykdu_rcKyB8iE6qZo1YY4a8tJqUbOXTGBMQ5fqWGdpHQSMcPcOVLAgGSA_4CahNjDyCZYnOM__sIKNZLKlM_cRafIwwVeUUMGsBalWbEdWMbRw8QaXlQ9HBMGhqFGENg11itsukTkiDGaLWKLAO615fqoL6LgCPE4r4xZIYu1Q8JabWBjUPp04URwQkUJ3vwQsgoVsOv3Glvb8kaU8XGC_cdTZWkJrF-NOucNrbiMli2Ahy1YtghlY0DyjCVM4V9wBRGE1uRgO-YLdaYWgIDPkYmLQqzy6LYtmFV1jI3AihotPfqJBYJ5NGI7pZ1kNgbg3sXhk1DRnZhQ7rfTK12YtR73hTaYUcxbGAhvTvvhNnhXIWQwKavGAyN5WW9NrbCPP7pIah0ipu82bqn2qFWU0IGyxI2BqUBSpks4hNOSGlyeHoDdN8ZqZrRWQTG0NCMpII0MnpL95gXCYb_MJoybuAcGyIU1iR-9zOdL60YoVq7YuUpjmUwJVcIz67uMe3Py5x0QmyN6Poy8TGua_qjzFIXsU0dqyoSS9kyVljXYxjbh1oCFbAbLgocVTBURNl6hyrbwa58M6JBZjEX0E_W1WGOjdI3bT7Twm6Q1ND6rlPcMnNMM7Rn5tivnoXsyYdUvTtRgeTqCqzQNGFYLzfx3eWXdqFQCqsUWg7NTeUKknjlk9DHuhzPhy-xq7fCovQsPvOXrOUXttOFXhvxIJvAznmpf2uuW1hwDBzFiWFj62YNn2NsK33zz8yxecw7ru3N1VOETbHSBqc9hBDgu8v5hVlMZWW79A8zi_sReuJsholmJeRzmF6OTQOyUL1JkX3rHcm8JyaD03qEYbxsml28f1TrbcWHrPTT5WifIfsaugDpoDrNyKqkFrSyDo1KBZCiCB5kES4b84V7PlMzgG_Q81-IHUuxu-DiRjmX4Bxof8gf-e60EzUv7QTGwh3B_4EqM3VcPIQdGf8rNRznRgibt_DziW6Ou_atVI3AkHj4ZqpQqimxrkCJGleQ7iMvVMh6HGspx2nmgbauOLG02BJJ_FzyRRyQKicMU5SPdIGB9TVfLGugQT-Cg-9lBWhSOopxjVIY6tJsolpzZiKrtTKHMNnErqpP4nIbsgdKCCE1QmUZ0oFkjXzDJrIR_QFBDhDok1UcMKnM3Gu0zAlPSnTxj7A-2fxOzId6gNNyctUV5gxSoYTUiFb6nbCVkCvSUVYpleU4iD13iw0NyWM7QAWyRwPJ8JfwtiL13sIEyMnBp--0mHFC8P7m5xeRHWK366zM1SYm0RV9EqLK7Q4tEiPTqTFt5sS6F97BJTS0kvVBUOojkoxTxpTmeIldtkDwuyiKbIpmlPZi0lvfNeUIfaX7iRT_4LyW70V9k65ka4WeofdyYB9GgZ_oVPQBCFso7kGBwH_cG5KZmIsltR_U7z5drPFBojZJXqmogcC464ASIXNuQlAwOQbqnmVvM7eVfpl5J76nisEX3zayyNnY4Q6pF2IYVjiYYoeV7PFCnGPjCDNubOPfdYQDClnDO-q1RTlgUNyWtUGBlK88wQV7phjmNBnjeHuAPw_1yeaqT8F1FTrCY44eYxTn-zjnjjJoa4uvOX24EDSYy-NLOlcSATEj4Fslltv3SQxunfIm1xwkL6XExgGko5vAtEEy2nJGs2SM3-HRRm1UsYSQsrR0zHB3iVgpzc1TpYHzyh7Rp2nP1osqF28ocmQ7ZDJR81_xD0Ut32nVBA054UO9zQdIR-X-Xm5HTRCfWdfTOOcWA0yV7bB06_VnrOBJS7mYSFerr7hFbL7VQAoSWZTMG5s8y8AhJNoqWc5seMIVU-vmcze74aRLnuvhvSp6RE00u-gho0wHLOgqRNQXRP4Vjqsi4Qp80Iksmv-blo9bH3-orblzv-IyvrSX2tqgAa8jJxvJVFN5_tag5zazEl1jdLRcg8Po2laVvq8lOhsQkGVmwpkSoFrv8_Wk9f1vjUwIkJ0tC4iowlhnT1JcfXtSr7ZJVWrqxZ59vhlvZkmAi5lpOf3TuqrravLnwrmFsGB4jqGPbwfmDhOUIJuVc1SeO7PewcWUN3kkh6TDhzti-39-8VG1v1WEeA0Jy6oTM6VIHEF3E67r8aO87MlTwx2r-UeYD7QwrO_XUz_9uTKGKJf40iMAePUTfu-IR5su7bAlPntAY1JSWWXzA9MzS8KfzIhOlkCbdNiRJrTRTmIzs0TXJuSXf3evtPTN6Sn8xg3SG-0ZX9zwqtXk_7qzhrvJCICn9AIvT7sEN91bdlRHXIzBTNcfRBv33Rssm902PXCjltWotxqfmGaey6-M6DRSnwPjZ5U8PCPb2GJALxSTIinDidpennbIdhOzAVwi5XbkT7J8MPfWI49S3l__Tx77hdsg90qf-0GKcNA1KZlWDuVfie-t-70h9B6qzvIZV70iWGBldK3OaBjSUXpTmElh2t6xw0SohFAdDbT4lrSyURxo-aZqwl_oIzNUBNj5SArdagYrTIEDs5IQ2Yn2clwiAphjDnkJsDz1owBmDAEy7ZRTTsrJh30jf0OA73nJqpYJuxlLZmJ9fqgBen7PQLXZU8hH-CFoJxSEq8B6E86BfuhD7z0AESxMaUhlV3OHJt5ksjurgEMQvBktwLE9sWY110iyyOqop4chzUtDSGH1XyIoWgNfkFcwf5hWDd6yPskHXB_Wm1w3gRyRwE708RFauo5xjA4xpqgRaejQFNwJJw-TRoo-gj_Yma1aq81cSXD9XE3RzToytrqNKnxFz8eSdbySq4Fs1lDMjiX5CXRoPIVpLDdUmtT0wnOrDV8UPPdHmoB3WR6t58_r0v7hiamcmG_sgG_Y8LwL5qQPBfLfJ0eiQn6tnRLjmAr3Bt4b2tyMXm6455hgOjPxZCHuVDEuIjlshCZhZXXJuT0aG1A9AME8Z2tM1bK4V9E7-BcM0KeaiATnZqcoTgNL4m_QH7X_RH4jF7Z1ECOfSFG0fM0DSI2E0p8BhhyCV4ZGZRrp5AmzQi4fbnSfbJujAOPHJ2uYWgd6s__ud9MwHVOzktRtNy4h8_nudSp6oWcG-EjoppKWSdDXI-JFvb0cBoxvhoRFSB6Urlr8sI05vxeac9Gtnf24Nxo0QTQQ3PyuG01VUCT8cVOTQu3o3aangS6bSXZMpXAk0H7L2RhzlNOHdri9gCtIegUCGwaGCH0xk3d2uyZuZxIbnQbdEnktKmOR7ATrYhhaPhnkzNoDlXvvEYzg39vN4EvVWf3XPcMayERSy3hgWdCe8Q_s7fmfE81RWrmnQEXsFsQQPHc8Toss90WqmyaZUOqmtoWOTdNqmrULVZniT6WHT49fa0ZGhNFCoHWR_AQTWT5DlEHvH0zd0Sx8x0Zg6X9gqA_8B7fzULeU-1Uh1TC6-W9VpUg_HHBTJ3zIFxp0z8jmktsz80m0OzmCcDtA0fUK2FApxY_lEYu9WutzrQreGfNaRIJbhHh96QgUD2V12T4nNp92ROVS77A3I9_GOLm3d5RTqY-ZwA-egrmQVZ-eFjqgY8rES7orO4GPcf-xrtCxetWE4iSPnOT51p3dsoru_QnzSWXQ8WBn86G0NE-lMqT6n00v1toBX2nXeBcWfllEgcJywR-9F17WSvM66bePh-75b_EdVhd46m9CWog018h_rMDYjg8FANTg5bZLcxwps4uIlcfd5ajJbiBJvAmshPMRX8FrhTQ75d5G2UOlGb3lkbe6b3_EhOEoUd-8TzNuKnpFvMGLSuKmZlwk2b6cSGGkEcyA5b3UUegXokaJfn2IvqicZzpJy-G9DDmlEIXQ2ikTdqJV4Xj5mc13xZvlkli_ZUJJinmsrtXFhfe0C7ENS1kDH3aVEu9TlvbIXVqAxGqyP2y6Oeux9_AWSkxheKMeS-DyZPEbbdn6zECk9gaRejA2WGcWo-4rBrKpt-5iiN0PcKfIyvKFuqeEabnVwy1UvboRGyavdy9VCS43mHYRt1MsbLFanTEh653aeH46lVwYoPOa03od79jkXr7teg__TuGiB1UqN7lsFy2zgHIRBC9-iYgoLszuQP-10d245OcRDYGrbLVSMXrS0qBiXprQnqUGsRk8RK7btGIalMxGW6g3BeAlGfIrvJIObOzV6JqYfVK3k4CF7DaIFHfViti0pN3uu6f64UESyBuCA_3cI0eg6IxFZMt1HqiM6MMA5CWVnmudKyp4qyRNSSYycd9MmHB5Y5Jqsmd21uirlDS7_fsFFOqhk-2o45G0qgSRTO5te3UHlH95jY4N36qz0W4_88e6p4bAAqTHrNQCmYBQYDBWgGSPI8GFuHKfIhi6nuxnkjbzf70fr9ocZXYEKiRRxwlJjiPu-jhekkpX-u1MVkUKAO9hJJKwLnCuuyEuklE4JjMxi7M3HjTfWd4vqBRJBB-bKv6KjpQtJG2xkXXPrVFZprn44IlRgrf1bfNE-JypGXG7a_vSUZtvGfOCjNY0x_FqWl72CORNZUHOVPnZt1gnvIrzbe9mGJy_ApsXOxCc4bP3shrkHLcBM0CdI_0jFQzm9kxilj4XcGkw2L8BFuFehjRPn3LZt9dRe1lN16t-szt9AfPrJQnjZ1UmTWYGuj5_LHtmRzv-wJpi9qdoPl__Ez0q58CswYXbQuhoowtpPjU_ikS7Sw_G4Xz6eUnfou9JOqpa7_Q6ZxXeOlbMkgOCU6QSBqlHElCxgMFS1u5aYiQ-YkZf1-EF3yh647MGw3VuoZrfYCojMkS-6YxTugxFgNfmCgyrYmOe8qJtFaL641zgU5ylEO5pTnao8ly8l4mJU5vb6StfGzQCc_2_g5tKMlY_hs5LpUeZ1rcYwxxyvncdtUGvs_AhAT-YAuRBO7Yf24lrOI8jTqBMt_E-k5yxJ5jqyFRaJ-65eFQlV3YEDcVGMQ0HU5_6XMyAKQ2Mv-jkWMbU1ilPBJC146GGetBq3nccLStWlIZRCppJrq4KR2ijBQ9YZiYHsdDJylzEFo0IOIAew97TY70spzilK7sQ7mAnnk5BOoWBs4jc7sTHBWln4-XUZ3EeIu-oEqvWc-8TyTeDKziofeKu-wgxb1Pvvfj7oCeqtxYDlF8TJuLtXMxgDeRXzxKk35p4sOmNXST-is8i4buRbb0XxPElPlylC-TwvS4VLNeBZ9O545x3oCsx4tWpzsOwL3MtsxOF3Pm0ADmeUg8tI3nfkOTlZTaP76Ruve_PBC0-qSuMTMJIl1eVzI_Z4cyub67QQb7PmHvTvG1qfQsUw7FvzIlHWbIgMsjKf2e9i1VJys7Hgqu6nNoP31OemXlP9ewc08QEynWY_BmeYBBEAV3fvXuxnthlPncdGJg9XlB-dt4ml5HAClTbKpeSVhsXR7FD0btktHmHVpyBjQeOhdxlbpixqg27iDL-u0ZR1jSe9SvnFmevV_oWDmnWoSRfsKj5qRBVlASiksanrvh8TsgFmYbhZnrztwnkyu72nTBdDrdLLqw4XpLQETr8e_vDS-xBxBQljSsaIqVnR2IwluKc8wPnNSHgBwuo1rwC2DD31M1iyJiUqRnt1WpvdeTSx2erpt6oCZNffhpEh0vuQIAj70hInxiKMAJOafRzGReFQG97bRYQla247IeEoFO97Qif4XLWn-9TBXawcKKGge7MAF2Bd9bzcwZKpFD6bUmW7qLNOBtl6kyoAKMfnMRI6WnVi8qM9Mx4JR0O_fAiJe4PuHTq066hVJ3htQNrIv-w-d2RmqvrZsiRarrIUMFZpaZf4gu53Cnn5qhIm6AsP3s9DO-MHVb9odWu7VBAcUfhb0QcUNoJKzsZ6DLSimYaCDToJC4uxL-LaklVAQmincUN3zJqtNODLuGMDLxg8CwxgsQvqhRxwIdRvIWoRekkKQC1BUkOZrxUMWbZRVGZTHws9Ppvet1kE8bF1_Y9O4aoaXtgQTKfeuBhZcbCwJ6I2doc4FYfVgzYVLy4OVg7s-iCxK7NNX6pXzcQ2h0w2FOy6xGAhJIajUco4GQdqeE2dzu5i41tO0S-1wSuLFPp0xYBFOkCuNGKVMXcL7krJm2X9H9lwB4VB0QTM8EKerRx2NwrGTvdhQS9yM1ZyaGqXVzUEKc69224G7wFej5tpNCwBfAJnQnVXPEz1xWtX-udI3cijymd5TBJettyXW71f3IvFRPAUzADKBuEn4wftHQFYr7xjYwOCjmK8jNeOVutlDuT82O9mM8EZDEGgPETFjSCppcY5_02Edg0fihZRI0HTFzQk3RhDk-_3bND92PzHoUhXtPxgZ7bTcGjxzx2Lh3KULYZUFnXozfJKcSpJ_OoTrXXbrOy7gU5kDuCCwaGL7ZZ8Q3oOntzpCSKcd4X2rGTfNPzvSH0Jm1-0AaYApu3TXInSLkZ_mKUMwQCdAq8P75IvRiJLSC5J1giWPpbahtc7bDZ-2Aniz0WMIbmwnpQWrcpTuPZUhyjvjpJZqYLqMqevxt-zqy2y5UM1fwKaTDH_phgl7gnyGtehyawec_TPZGkgBChN0Ebq6pIwBYf5gIFRzRSiOhiPyo2GnCvGXyDhJiz6SSMFwVenlJO0TwpM9XCRVy1BAd6CPcJx7qBGp6rIiDLk3alTTXe_iEAH-a8aOKxcMEixSTxafXgRW7vIZc0DVap0O5cvOcW_wBCaLgI7F-AJ9VwNdnsEhxiWqRXrsY5g3WE9WAvyQJlOadXchbss9xdO0FkMBFvSfRNt2u5y9O8opTlIqxKwH71IRcUJnMp9kJXMS3B7l2i9kHN1NOWhdL_AJyGEx6OuZGdz5jcZ83bPasfQiR4YUECZbMtYpmNSli7z_44We3RgJtof4y77EA8HjsFYC9uVamSuhiJeX2bmYKv5fPEJJPGBXpm_U4IfARM9BLlcBl6lGWpmZmgrljivJp3VYTcDR0rON0oDXd_uHvpXkjlNK3J5E-dwLXiJCkDMYV1cwqtG81bouR8DiS3kQiNKUCsCytBNJBc9VKDrvQULxKWFYNGwFPf000U4nYLB2nlr1BgoAYTjUxtIRZZryoKPnkW27pykoxdZDLa_MZMcsmZyCr6OEF7msKkeFdbobcUv-pxepN4FGnNc6-Nb-e-hvS7rjhsDwPNkbo5vj2e_VFWobK3aEJqYmF-hDmoUO-mv5wRS23P77eL79SENZhtI4qJPiZprihSt9LTwHbPudeGsjMFukALTiWRBfK7_aMxn8fNafUvZ-vaSIPVUR7PfR0-qOwQpPbWw6Ej3lKQDndVziFI_V_3ZcUnEcJOwF175Iw_nvXLTVN6g_TwBbA8U4T2Wf1pZpXeGpnzmBxdjrsxNfl5WpdNI22G108Q44nKCpkLzG1prhHmI2U99oKXIni2Z65varhTOxshTin2TFekSVeCbuXmT52hyJK8RRif90EwVDW9xOkI4fOzUqKAMeMtFzCFNsXPtkeemtNyWm9CT1MDsDLhWz98nFKCKba_19jbb6AH1ZluSFSapH_NazFUB-OGYSacUOI-26oNhERnM5hKjlMsPLk-UfAciQ_nW4CRtNsYhV3fQEukE7IEfW6Pmu2NKkqIXWI_PDfJ8rX1C2foAQH-ambA2BYaXQu2DgMQCAFD9k15CHjWS-Vo72HGeOtJr-hmuRy4obJwiwE0-6RR4OJ0cNfQ4g6WaTFpLXLVZTmjjeuNI7FOsTHkdshkN4nH5mBP150KXf6CuNB61B_H0ZDkJQHqYFYSHUP8nPrZ6TzDk2tRxG4nZOHhwDzZic36X_RtOLF-STSygv-D0DtMKcOfdndziv1bXddF3ATkwx8a6bSkBAZ8_TQIdO5fPVVzGq8ER39nXIAkV1NuVCj2s_doBxw8rEsF32X-gsttw9ntg3foiqWj72lsaruhyuVg7ZXmtFQtDtP_25jOnFeoKtcZPo-tjCpIPuzgxJaR6yW8omQeMplL1hQXAmCI9-hJleYp_f89WTABQr9FJBQO7bmJh-X49bvu9CyuHhL-GJozQK17CHZ77owOnq5L0zRfvsQf79dDhzBNg6gf04I74wXlDLb94OvGQP_xHsPQkMkXS3stJPIAVLe-e7bYoRUlcRPor9i2VYCXeJ0G2FANY6EtyS2HXWtay59dUSGp2YUuQVQcqn9lFjaetyVKVARqcerGb76phjZXOf1yP_VbkilH6soY9MrACJmJDPHCtDEFISkKcPFmAUQfjrpu-SnWckK2DCroSlzn8u-a5hJWeuSa1DFT0ONTms6TMz0-7TKpugoQT6yRnLdrXFAvaLvnyO5vJwDAwtc78e_rwQMtuiDJstBWaN67oVW0RseHkJqCQeByIkdLYT9J8w9NBgdT4BKuw9zdx_IUsGnEzSgWq4m0WApLYPo6I-HWUSTTc5gPx9vJMWXsZf55ESo24fPBWKzSKucZkQiuJZr9Drc88OxHQHaGlE9olDmjxLcDe8KHNZRTfss07Tu2iK1NHJwCbjl1rdU9WyZ2J8lB16BZ2oh-JWcBzCLokCkEnIsVCOJkEB7NiwTJ9LajTnHyH7R5Y0vkwDA_nXVpUmq14f2gikIF52w4WNNPcdmdnUziG3pGrVzXMIoSJj42MFcZe5RzpKlUcWibEFRqijoqOYw1pvvVhGy2xTaIk-bwjeKRr00G0A8I8aWgr3w_dJIHMHQGdrhWA3dyAyzdamaqTSArG1QIv3MMMUSQi1TTW0Eg734JPzP6MFG23wYQFmUdwCQJ3kHoK9oSOJmdubAmf7ErPvfwuzt2Z6uulPEv5ZmZTMLe0Wb4JfyGQVPkADATwVxwXnx4INA3Jbrf6cUzYeprV6d4gDGDsHOgnfMGDdZyKRZyJ-pb47ib0llrkATnNC-S1jO_wxUiZ0HM1Plh-yWMfcYC9psRpgU3ly-zPjQs1d3z58pvA9kw5jbzGzGJrVWY5d73nY2zOkSxvYXe2c4fzU3s5i8qyXuCtKukRq7Ft3ZL9WbZ4aH-e54LNIMN1Xgn-oeilm0Ej2RsyhEercqVIuNEjWrHSwYwBhhMfPYclE7CoYY3irDeICcrEcyQJ-5ZDOKPP-60F80IEsghfqJvrCALAXBBYbKGApoXjxiuzAh2SsSYfbGZSoom3KZ9jKfrybr85nx3OT54xc9fpa9JO4CYWC4-ffSrUCL8b8WZ-M-RzjzF_h_D3dNCe849Vq1_Bwgb6rbvMDvEnbWxaTrfTvqM1fpyNHULZHyRe27tKerekmzvXkoVDyjphkQN876UNYvf7fFFClAEih2v0K1nx05TQ8Pac0ottsUTg3Yx5Cd8uJQcOupJkgDWOtB9mDID5IO9fgV0kTwKCNRTIlKPCfQ3WHut6edz4xcibn_ogSYKRI6bQ0V643fE6CepkslRKoN2F98RDG9-drgmLo4FdmXI2U8qnAWnMWwRq1vEvZVl9LHUuV2iguNyVmXfK8GMaQBioSThQaYb39Lj49bmhlHzmRE8fBQbfNBwf6fWG3UQw3HW6AHEnMqE85Roz4x_XLAuOQsIoLwapsnpOxU_2qmK9CqWsEjfpb0_MJPQZLiSifQe3-nmJnfa35XxJ_p40cpGbjCJns6-sgwZ-5POFS9-i8QMi3Ot_pox3gfwV6sBJmqybdXnNfte59NhyJYRQYyl-oNIbWG5DPLTL1UTXC0O82EQjZngPUtI_w_UI_ErjCjdp1HfF_oO1aB3eLlooDQ_cOUQ91fW_AAuQEH_mKmb01GGuAiM4NJE4ZIixRpdSkTaTNQ5o3hoVz5QmGNwtZn82nN-dhOD_H42HpsTK-no06azvXnLNpr1F723Xudjc2ZV8XUbUwHmOGXfUBFNrKSDQ1TrS_u0SE_H9fDDzADZlLksvoPZwJ7lh-THvwIqayY7_TXtVT5SKn9hH2Uc-5aICNSwyM1C3dhRjXcJ21EIQVBHBhMtMANuE-rHX8TC6_tqAhZ0OHs7UvAtyftOZj6wpEQWbpPAPZFz6ZNC2dCzz3c7TbS0v60fhHloqcXweS-w-Ivwj3p29_RQ8IuwVmDzEhqXnMB0i-T6zJYGH4aJFO6F5V9cQyoWyITHPmIBoGsEq-oNLsFiOKdnEAztTIXA4OLzcgkN_lRhN33jpcIjhVCIye4Xun3Z6GXSgneDzI5okVp4vmjSSCRDdV7v6ChVQNkmzBgCV-o3wWTTuHwYYkWxtqA9-8umN7zagNbsEtQK3Nm4UmivBfh65_pmg8rHZIuljszsFthi9FDY6lbWI_gq1N-CiGkxyY44N2Fa8iJYUTIrd2dlr9Eba1NAofENcTzu8kTgZjvC-VE4LGLU8jpMBmfnAB0Otf81jjEUH0hXzczCbyEJ1TeRV3jPkZaWkr6K5jNx2BaV0YYmNveYXiFQ8v0ogq8UtLtpMsOWamdbJQQu_sBLcvePWlJg7x1Fd3h8KDvJ6f1eQvz1ZFxluPi-Oe_Tj9WX_qWdIiLoAstrzFIJpib26bMShTraFPDVUjLX5s1T2kSbOrKpg1CTaqadwf4vqUjoGl1PEsqMz2tLKHApYkZpiEBpPoddqmqsA4kWR4NQjmC0lJ_L8vB6iID3c_ajgBdVRt_f-eo9PJisH1LmP4_I7hLYAFAPmcwQ6iXBfLJvSyCEdYZakpPDi6Uo1P0QQky902ua3K97wgWk1po_7fx2_8jNXAGmuKePn-78l3Jje88OYqU_gKitDv_DB3V3Bqoam_kPUlMZUIoTRgVyKZ_oBimu45HADELb3Z6dZVjc0RKSA_QAbHyPB930BdL5LDOQdm9b0yLJEiiGOBE5zE3FLI37bpCIsKJxznzXY7BXi_Svmar0T3VNO7VA7TDot8ZepEFR5uZ0plq7qZ25xvb7Rf4g6s7ENBsWA-djzOM8IvecMky0tl17lfgi32_gYpINh6DpPYccBi23ObSy_aXqRvb8H_v6mGE6cKF-mTRt96vHgioKmUugS5jVZtq3XpuOBy8Tw6oEJs2RmgSWBysX75QJNJHw-xCCed7Zs7Y4z8LRfTz9okHAHT_VFBa_MDYLlXhQ4OHAo1YhdHg467FUSiEls5AJcnzvDrbPR51R2BAviiEufzEeIX8GxEsDkHZmXwXiKPHIdYbgQRorHYCrwMZ3UqALEVYB3T7ZUnE2_VFrowWvNCISrorxgyiDbfV6LDtRNnUxJPmVYryOUnM9XibrW_pw9CyBQSWM_oMZ3glv3R33_0q-FK5dr-gdNXfuwgkDYnC8gfmcZZQCO7DlhWUsKQE0qggQS9vJ0AlrHU77Sn5LI2R_9fUmSRIjkIx9EK1YDLD_S_WPElEdq4ol8PGDJ8_Sm_O-_Oe4cpZ0fSII2b1QuJwnmCrX89El_GyH8jPT62IONeeR5qCXXsViIOtV5tGuabTMa4GHQBnsA-7xYsSyiNSxlTES79j-yapm2T8tp7lh4vYWXwV6IPkFkwQO97eFwSEFVfqxKJu3Dmb0TW38hJ8FZ-TpTT0nz5wyd54p8sW4rkHugqdQs01UkVCKdR4C1QMoicjTTqvFwkFV17jmxxQKxaHIbVVSma284-hvzAJO03qk_2wazy-vtdwjFKgNocLoAlXf7G_N9XJXpI63zMQpBbHQbRWQApITlrjROGlQt77ZeH0iHMHw9xyiAWTXLC5ArF0LV2ohJyt-72qPvJQht0j-GNGCvPlhrOnEaXStQ4TXdJpygoxxFN0TDHqryBzNsNHKXU-UzRvuiou5X-moxGunhxMdxfNhLkBwE1IiiLg0OrwgDfJn_URnssSk06JvVtjCGLxvZFU2d92f9sryCyUKmYZtCOvpj8JtSGr47Tx3MgbQNWXgQrNpSLIFFyclLzcRfjNnKXXnksKCiGX6mo_vhs7iU7BTu8CDZozQ3vKL-YBC0O3y2EPwU6p2Umi9cOubePUvInW7VpWcvpbXDxXmvTdXjOxOjCe-1OQ5E2M7CC1Mn6zVgWzYFMWHKxQUhOte94oVdkljkdYtWVLDukeUkPgdX_qy0xdGuXDtfwegYPkN9ThOZiq9_BKwlIz9OUdaJFH2LswUhDpLRfV78At508XFYBy8YWWIpoc1oSSJ2heS1xGNj8jPyrDv0QNpW-aSpBZWRGv2QA8zYfUVPGIrEESkSpDjiivNSH4-F5AkE9-tlZoNoAQKb97R8QXEdgEk--aDFeAUF1dBei6Ze0ucGyK0bkImT8rjHSuOLMpKRVUlrjXv2SGXuV2v7ovFQ9k2CkKsbNb4KivGmyVt5dXUaAcxBCIyHXaATFmMkQoE86XcC0klPM5lkskKKpDbOl7Y6iaJoyVLeMAPJIm-Uq82mb8ml84Owip9m-8IA98fkYhm3h1k1mCWWIuH4YqWdHK800FGUlZz3NM3oJz0yBY389FCYTSypQ7g28ozqfyCw3WkgTL0j0Pkh2YyVE8e2BczFgWgFy7kEbEFBYM4Ld258BS9P5AkLZo2HQV7SIoGp-Wrir_SIwNQBApQNmnUP4aoHACa5-SDtSV5xoDzS2-kiH2ZcGMtGVwcY6Abl6vQY1SSfhuUtnpiBwJ_M40vF1XDdvkk-uDTbtf_8QMA5H49ILo0lKP5udlcX-2mkME1w7FRe1184khTmdWaWErJJgSoOLFRzr74TZxrjHWNE1XTlOmrBQfmHrW72eWxxPtpEeQEK1P1xGxsZ--bTYLro6YRHquU2_BM3tASkt1tMrpwee_nm-fzWhVQLSN3vyL_IEZhQVnb1Z6B__N94KLLwAJ-Oi_WIKyYOLVxaJtkdmEDJo3Eevrp3JRCvkkD-ZW_JZbYgH4B85kEy-ChbcN9oVL8DjX3QFXuc0oMpnFGXIOdvwzicu1Bg2bgeLupHUyLOp-4_lA6694FFuSN5P4p91L8FDna49f_OoQOa6e1vsW-ytXgWE7I_OJOl1vkaHtSfmeSnabFLD7Z4KwzOB_8YM6Ezly4VvR_BeoKN9vWueUykUNufwzS3bgWilG64bG9vlNYaUfsXFI-s1Q0qc_ZIs4hj5Uc89yWUWBrSyoOd46lDtbAwuFqjPS5BY0oTNejOG4BeWK3dCKePpOzanRhN4nPmJAsDOUlOxvN4coC_TitXb4FmUQLI8J1RfhV70KUA5HIoU9yVGU4M4I5IcEqmX3_A6gydnntwCANnDcogzSJs5tykPmTlBjlhuUHeUpIAHQexrcTVP0YJ4W4U0K__TF17sQSTVHZCvFii6owNXyavcsUkj2XLlzT8a5emgRBZXjObrboxvdkvzDMtr7jGOP-p259bNUiRdFVvRXCYOTPPCiq4pEQbU0haESuexRyphcb-BuLydqk6DcjMYIucOyVcETzpNii8whj5QM1SjLYHJYtjEh7i--3S-rkmBMvmLJWsmq9PEjGqbm4_w0qjvpL2KGwoOsSlK05TYQNXDMDsBjAlB6Z9B9wJNv5EvI9lTITNwd7NDpEPwBGUgvJoP_dBexoBKYiaaTZiMSWeGedbncHblzMAADu0u9oxq8mBGMZJd1SkpxKKB_gqObJ4wxe_TlE_LHk5EEnsLMXSrnfLpxEyezvTh3tadCdIFlkTAM7S9JrNTE7vYSBked66kdJRbEuvYL6Tw6unbbMTDXPa1cxcfknsBxYD1023ycz9u6H26DOJ7ZS_N_6orJ44dYOPY7KmCT2Xk5CTVZUEXIwHoIdb8WEHAuGMGFtwSCD8in3zlGVp2FlB5W_bAhdmutOJxgedgYJgfmYfu4fPW-Ypo_Wrtpb2uNFBlmSVJQ7ywYuoP1cDRu3atTtt4x4i2UCeQcal6nYPPVB_BGj982lbmkTk6x0TrjARUkN_S1M5gkoTx3GvGpZPDhHY5zZcmR3_zCLcSJESll-5WrIVzkS3u0H_Z_z0rr3cgKemFwKBblTc4_hcN-vA-htD2DuUXeMPx58SWQqjQcjlji1Noeq61kCneYJfTSqk37Irz4A8JW3J2w0r7VibbzFt-e-6d7CMiFNQ6q1tlv7VNjnWhvF6Csu9LOzhgQtBKEsirW3-I7xKfea_Ahv_b3cH8Rc6HVVLvNqIkYDzsDBUC4ho8kq5bVfHtRpF5RrNJdYA2NYH3bZxMITj_GeKfLHV-bWSotLol_wteZdduF5cu9rak0Vq-gndnEGI-khKn9yzm7QN1pUhFQpNwdkrYsjIncWmRQRVBmXkGhrtVBYY95P-CfNAQiFHTFnnIApFfO1rsanu6lLLfqJ4AplRsqOJ6-CljAIwEdxQzHqLInylQDEMTKbgPLOWtBykeOZwg2nMxObNJcAzRXqvJ4SezfhTuzZbXvFQbMxTHgTH4QeWICkgAaLOwlBGSLRnDNzcsLwt9vJIpQQTUSuInsGNH69Se1_jC2xb578ja-OVfxZWZviec794IZ5lmmKu19m8Sph3XZKOp4_6rr8HCn1qjjlfKq6WHFdeH8fuyR847y00wy3Uh1iksEJoufyKbIa2QAN0UN7iYO6KxLir1zAy7hHBcQziQqRHw0ef9SopJ-twjO9gwcmVoj34o9KZMbK7smeeIqDzuf3UTxa6nVuoEolSnp5G6FFNZ7RxEJttucfO_6QXH19Bz4JPxLMMVHVsAMsyKzGwBzYhjLxMYamDj_KLVIfOgARpzddATc02Jx1Gyssx-GKqvh2VxYhlF8YVHqMSPJO3wivjfDKJu2LObplQ7fc2BM4NO-OIjcH2Axv26dHQh8YVXK9DopPkY3d5nXUm2JzPtrl5R3BvYqcJKhZn1RLEZi_gb5fEFuW8BJZoGT5ZGtBx9OHd6mSkDU13HDZya6o2xQO4NYfcW2LKkv0a2JG7run-QFB4dIqMYEOPYiBvVQPr8RmBAgtFf6ALGPv-Oq74YFWwphl6yrs7KuCC9HYPU-A1pKbYiRQ4UDMRxSuX2o5e84aiWOR8IgNQrFhgLXAp8S1gTa4CVeP0NlOPa6AAKIc_j2c_UZqdfrwwfBIKh_ixrzwOoMSO7231wbCDZDX73GRby5FThb71qoMk0-ueD-tisHaof6pp104tePT8Vuvqy8hlwWCJHnWpmGasppnHE-JYpdR2Xa6SbIB9Gkdq9ZIpCJ2lIgZOAQx-rGSlECpoiG3yEyDZo1Bb-UEPgUzkSXTQD0hq4SSZRMwqWzUwvDoT-c2IA1PCx8KWhM5S8o2ttgXOSv7m0D91tfKhvmEdWnXAHdWNhaQ6BkRFTUX6bUIiRsCshrIX8vN475XSUjT75nRoKCZmsvd_0T2chI7HfDeHT2i-QIY8_fB5_gid8mO_UXMViq0BKJT7yPRE8ehAG1SzUkrQtix3-GcgBN0okdICnaw_kdAeVgHm3H0vXmK2IMC74oZIy_gTrEAI0QEH5Z8WRSfs1sLyRxmGOHELu9hUMOy263LrEF49LNTVWidXVUMBuKSwlisjkoJd0qpMfiI7Gfb646Gs1GC9e7TcIFp7gjUZABzuMlj6yYr1kTtSIAf2xn00yqGNreZ37PhdXlUB_mZ9CQoVnqBn3nnK_RpYjx22HDNqgza0Jw6m9_uWfEd-bjwCIlYLwgPxQv_Q1sB7lTDyFgFqDIRVF5_V3-XNgOgUEyGsjTTogeEOkTPQ2h4AgeW5dCyuNjIcReQmNNyW-BkjPQhNBAptSyo7THuYZP5skygZyPh0dTz3Nyg9DcWhyT96QZTsOV6FDcaVH9YeBAmiiPblYyb1hzQ3kI0fZ0aOfkry_vBhyElwxB6WlSzAQfX8KVABN6YA-WIIiseAqSI13l-DRB2x3Ksp7q0kpSVpnSAG0uFXRbv4A0Zb-JoSbste9JEMUbdtYoG5Q3xt9B3lfkSnVxn1eeoOf0kjsEOWPIKz_K-ibZ_IkSoh4l65Hq2yzucnbsADH6WuqSpfhlRZ8Vn5budZYUme7r9UHJ1l5UuIDyFeS16m0V4ec4bG12kPuDJKI0f229GCjJOv0Zerqe04OUsZ6VK2QMmkJi8elPZm0Odxw4mQQE2vOLX3G0f2HG_eXQBEmrtQc3TGrPSKoFpfLZkp3c9cxIgV1TwyVKCMdNrhJlyUNSaXimIGia20kg-LBcMFsBuXWZ9V1xUtg4TM5QwGGZn7ApfZ4Mtf2ZnAMzzxXtFE1sA3kPVcS7dp1C3JW6cbTicLynDtLS5Evu0wJsr8sOxKO_o_N8EEZmw3wqpck0KHPMn0-q0btlKKbuNuqGpZqSUvsbobkiDHcL4O9Cf4g3neLCLz9jKq31UIaWI034yeUnWWueQBplS3M8pYFigiiew9xL-tQpOsj8LBXO6nscGICVPOPt7DCuspJDrbbEGDI9WOsHyL7IDvA2FfMbDgndQWAnd2CmZ8Dnerl--PACP7WE753LwModC1kOofLjCEmeKaZlfenEpDm-MlWgAXhanyIa0SIT8pjDmggtqxuMyspeRxUyxhsVuyenFz36jbFEN01qcKOZDOHRuZOorNl7ACBEFrZjJBKawGF48R7FNyMsZwQHifjZvZAC2V5ZqkrNEpukuKZovR5zJRF0YWNEZM1nsH9CeneHQdWznAJR1kk2ZAcHIkkAlfZ4CGjGQXNKvIeF6dUkyCDXLgYB7OhwKWSim6UaKE6DC0JTSUaiNjS6ikEBNmwLywkZ4r81nM7nFFmP6k5IgwqVQGOSZvDcdKq9zyK-8npE5IGMkvUM8oih6EAQmu9q6a71gNKvxn1CQU2Qs2Habp_BoI_gVjWHpR0DZgIJdFJGIzdQ1sYHNpWn_pNie7x4vsw3ZJsOE39i0vDKV5DMPp4pnht_EJwHFq6kqK5fM4ntizz6WMNgbozccDdMTqjM0PtZxPPU8Zm37RRlqpctwXhYMlbcw3e6XvyN2NqXu1GvXKNFbG09vRXNomS5nu8hzyzywxIMofguCStM5HPAkcH27iBJt-TRtb-9D7RnNsUicuK34EDrlqQ4qWJKkFFXrewINzcj01v9KdiYEs9785dDzyZdsQ7vyjtvd39nx12Me9pR-iUMZ4tu1ZDFN0VA1qsIH8PLjkHS2TyCOS8Jy703F1igf-6RvG9qq7_6XJQBlF_FhAVyn3SKOOy5AKr2YUTtE_r3u7qb1fVlh51DMfcoVQ4yBHLdcYUhvF4Re4pY3JiVQFDKy0F4GDJEowLyubEIrugnyU4WAZASyfGh4rElea1B2WwPBaW4D3a8xaf4yfHue-GwDCEcSXXeWA2y02XCr0UnHlUyuXK6QcAcukGUz4HbJ8fyQU7eJWG_DjXjyxuryiHikgCU29SHAmdm8AlwGavkB5XHxZUEpFL2R-4IyI1UlQ4edTU6D9j_1QgHiwPMpGOU2NSTLTECW3Ob-IPiyQInRHEAPFo96oLA0evLpgUi-7k-wI15KQPUSkYpBGHpt7HuLKxtVGuSfddTWDCFstAFOekelvnc0RM0yfHr4cmpcDiafYJAOb0iRL8dH7_gTmsUpP6cg1cj6pEiVx97eRN3V-044HHt_iQHXo72ug-tafpJidDIriYtz9ueGvIM9RLMscUZ1RoWBvuBtt0njBl9euFk8D6mrDq61NqCE7Q2Rt2Mp2VIjjqGVnpCGKRomHzz_uwX4aUaMIKsKflN925R2esI2MSvfC9N-yXhR1vd_d1FpoA-mVLyVJJyd3EMjJcU59hS-viww8HvnDkNlenfX4_6jhBFpc1aHEoncDdYojEW9s6JsYnNRzUTHnPNcJ9qvzKDZeeGpunkxMUGiTP5PuN8kdOzfM-QU_aSNVpS9J4kB0Fq5uY2H20YpXgtmk5_PkaSHkpOPlA_TYKxYYixfNucCzP31uqSjnuM1t0jpQt9i2X1JeF24do1zBUZIT9zuHyqobTNTTThzci1yPYbLTJl0_xChiBBnpAsBFfrjZuwFaVnVKxMzxjAWG8isVjS3oKOfu1vxd-ApWPRRjeWq1qW-CL321zvkGxHeJByojb5CT77QmZ79Tx9Oi2Gapmk1ytDP840oCafnr5Fq-vLhINLmgJtOvpUxtSmKtHXLHY8iO8RRWQyXnwv22NJKHpVb0MQ-WYqpTMTRN1f7hRZ1ycjgCtXOhp3n3iXr5w8b4lvIYP46vgSpyDf6bkZKfN_hSMbvTzDX3G4_iCJNXF-u2M-fiIVB-4IxJN5-9ynvGFaocFbAsPOhIM7ZUG4937mhUixsofJOnVPt9R4P-GMZ7nDfJc8gHspdPQCJUlMjTz6hOIO6PSOZ923rfq72sylTgL62JFWpz8E4nvz-Oqv3db7ug0WZprzcQYsqOJ6cunvgZpc6kWubkv1bl_zvUV1RvqQJg4JroKGGKXqnm0l6xrqp8RRecH3YPbOCzWAf-jKTm6obwUccjNyde6ELSGCtieCtOCVvF7QYJJIS26Do4ewou8U-kj7tf2wVREq1YVs7L6fpXCCp7TB8HZ96kfCtPX8a84c19MCxlteBi-nr7VE7JIVnrj-CHDwBSznDECwecIjqoI8q91UdiS8fXpphtviIEqxEZReQKE031DMy8E4xDnZl8pU0oQu0s1PhNF6LpDjI83ymY6AF7ewUnTszYcdBP6lOklk4uSGLyXnVI7W7YODhP9jryg8xKniapSFusATCmbnGlFaiybk5tXZXUVB1HxaGW7k0ro4j04-cXSoDsjScHmvm9RP8rMrwl92FcUHM0XVs5CA5S_eK5B2CwIBF7oAFhIUKzOHeHuCweZaECYA27PrZ6Ij8w3QtHkCnEKtaVmcTnlFX_tNLHaP713EVfKE8hKb1lF9hu-gTDUfh3NbN9-N8_K60LCOIdNo4j_ruqElXZkN9xPLXRg2QzeQheht6LovPxcAvNcJECRzM2pwmif82cKNiV18tgCYtqcg9JkbR4Bj2Fsmkfpzm8CT-gbyHT6_uVPpwNQCChNgXZpR6jlRDGbBz1aKezfE4zNP4FOXmyLhzhPewsdp-Rn9gD40J4Eju8POwbn-MpfJAy45k9tcyetg2bv-lIeVmp5RmDnf8QXYhdp69QtWgrkWECt4Qme0XyV0f697Utj0VZSY5yrYR8uvpLybJNUqPDDHBiF_LjNzQ3go9-6WI-s2odv2VRJdM6WETyMF5oPAYOvvtye6ytoifobjiEjk4pySTUTAcSJenDp38REnyQ7HvYNDvHifX_Mk5wBGcHAeM1p9xryhFnK4-h86-ifvFt6ZX11nLeetJ2JveQiJiU-tIpr0kppZBe_hVza50mqDUXjyHsrBNB73lThB9Qn1amGWwdTFJ46PoNv2tZB3sZ7_l5y8rPEq-Jd_0F7XIZlVdRVACQ__aSkzvoOHl0vzDqi_822UWDKyl4_4vRw0Euzy-0C0Of-whMuQldan2ZooqCh2I-Bl_2zhqShJ6ZYwJjx-Y-6Nvxk4qpUYEJ0bI5OMty6LH97WLL8Fxr_6WYW95f7MHu6aK_QUTXMqxD1E3hij396fUd8H5NDWRqI00zuA5fP2DkKg5fSDUOBpAf4Mjfa38p2H6EqEnWBCdFUpIu7vlVTxWvHmBxLEDwM1DU1BdW3nbZaA8e0qNFZ_aNHcNM9jsOCwPGBwkoucLBZ_aCvCbNCrhZbrOcB-ibRbrBFPHSthRALAcBq51gOnukHKdCLiFkWfH3bF4VyZuvqnRoLxndML8nV_PiAqyRk45L2Op3K6SpYbRvIkkE0rz21VpcpDzRYUpqXqDK865IqV4Q04sKEA4d1MFv9WhWvJWfAgOmUqR-oDLBGJVcW-5XEANbBlLtqxWXIHldq1NMmk0VxU8n1qNraMFTUnw67uUCqyjrfieuD1TPeYWSekqe4IUG1FpvuuqDB4cl6HCgZ5NXa6A0SlxbU4ynOgb73Ye1JFeTMaa1wNAyiObBJyCpWTgLt5BWECEfyqKeuXEBawSqQAKBFD9pl0Xogs92MumYHXUs6wta-Nw9AHVtGfhUFTjMfjsFrymZHLJpJoVNr8gjaQQ4wHPwDD4_fCwOuLpCXgXAJRwIzt2Hv42wOjtMVg6rVNNYrpBBQas70FImKcKc729nJsE5veM4J4UfEgDwOkRE2gczKnR4e67gXj0TKumdnbixSe-ixSSkGjQHHyZvbG-HL8re-ZdIcMxKxiCOleNJ24wIsCGmLRH01G-RuA--44pLcSwTwC98nJJb3NGtj-HV5L9__g_9AHQvqRhQhsto9k3C5Jbb1raNrwuw8T7ZEdtQ9eYX2GA_FwzkQU6E7tUQuww6IAkAebGrUGVYWXPdIxqpXwWhMz3j3RG5GZ5lAF-9CIwUpRsZJxbStBROUGY4KDHFZ7REEH0j7q-BiJ11PCU6L1gfZusI179bz-dErMLCvwntkkoHBx-2jEqFvvam0JEEFtPlx4RjHKevOC8rbSrMsVvfcJ--3_TkqdRQwQNU4SI50qxhUTviJ4wMaPs-7ArhzF8hDPE_SnaklMCY2BkRRPIpUn8CJLgH9T9Wyyu9V1Vem4O4rXeU8LSkgrBry7eFa2WLZSjEnhfiI95AC-3HTKL1bPsGyghzJCM7YCV4cLaYFY_Lm2cFpHGwa9PCrYFg_T9x69T9u_BeAQF8s0fARgCSWWeptk0tndytnl-W3AUkx_l8BS8uCTfK4PbyvBna6rJCznKvEJP3iJhTtv3uG-uu-THZL2_d4oCIOsOM_m1ZOCrShYEp-hlNauiDKiUHZc3Q0ym9GJNjCFFiNiLXMaMpBXJWhJULFlF4GfjV__3R2M7vvkHVw60vTKeHM1VKb9Kf4NwICaiFkfS5DdL2cY4lb4fHzgR0saVgMlvQfBra2nzMNDFOg4LG_L5NbF7qt7r5Hx-YCHzeAkVoNwinWP6-MRE1KLZCsc2Rm-nGPPJFeXkCUc4hFBgsI2-PBULAd8U2k2kQkDbNc_w1XWdlYTqJWWyRR6OO_u3vBi-3zQEEgIACcl2L6gWHQnpmwo9xQUGDIiPdYUSmOR6m3wS3tbDIJ3lmNDnh_P1popsab5dDTCU10h4s0p9VYS9WaWN5zfOXLHmfutmX3CiAFnY_LgWdApDwJFNiojGJPD2UFbtRU-H8Gyti8eMtxuJ5xytZxcsZWhZpuQULd04y4gMRsiZDgoWwleny3aTtDvSB9Sk5xE581SjbGcA7Y1890o1lCHLzu6YCOYP0eXh58QW1K52GLVeRrM8HIp0oaLLEVfG-z02F5g_0eK4Ux-Bb9E_EiSnm4W56rg5WxOF6WeaRl5YsMZ8b0tDoqtdyVBQ7n4NzwxE8E7GIYIcbT7He181_l0lX3irLgC_FVRHinBjKmcJAlvkJFtrVb5rz6_hHPE6Cid10fvFhhsPubbLxq4Pzt-3UZeel4J1NR8wpWEOz8kKTr6BupJsv1oGyp-mvHTJW7gPMi57w7Z-vkcmtOKwtvhJQdJhGOB6MD1C4QcsDU6wUTRHth5iAS8UtneLci6pauGx73cw-5ig3DYPaNswS_L4Ek5ltd2fAwYiRa5-8EDlxk60lXpGfXaNZJ0y4nuDSl6AW2c9crYWV2jRkZ_wEXmuSLvcBNQtpxMyomcPkumjWdheoZQGa3fEPXUpWfuC9jfVkJYOs1CV7j_lg9lSCvnCaUrgqhXiD1jpAlljnYsxQ7eWxuRaW8cNzRrVYCEnGg00DS8g54zIIERtlFf9bd1HqD8HhLIuU6t63q_Ff-0lsLVOdpvRUKXkFvg0LMxAvFMTbeaNpC1DvDSsndVA-xFCzmr4UXJHSLkWjVtOLx8Ax6bL195Ak-oEYlnTABTNsipD4txOUUG6CeQnpyB_AT1q0UCcqCthKkEVl-dPfUYMLW_qk1CEUncuXoKKF7uzxbIl8eOeNAbDNgUrF2D2-okUJTWYiRicPtbtB1i8rP9Vlp9M1cS-xCFl01CEmCSp0azxSanxG6-JvZs7sUQ9IsBkZjpQzOhpAiI3jajXsH-zq3i_plNIh6W7Q_dOX7meSCTuho3nJDiyhvi8uagf9EgNGaGCQK6I1Q1wAF98ovDhyX3UNXPRphEoIFuz1tKkdV5ap9426J-lvKQBnlKTXurIsGDTK73l2-tIi7T0OMyztCPGTDPvcQ8np81Mlntlu4RVQu2wyhn2Fw7oryCGBA5DAa7n4vOkhkTAI5_BnrS95rF-X2AYXUHuUBlGi6cI0MuPCZXf-nH0V1-Vt_0b7GQ-AYKfPwwFCcLRuWSnnfvtDONQGpieZuQtcUPW_C5-ciID3IGjwiZFBbpSg4BO7q2-UZ7NmMDU9D1vVgrhS4pGwXpgiWpKW5gdESM9BnE_TuIs7vMBNICrLyuZv95Fagmj2uQr_lsmoO_ut42A-M2n8u73cSflfA7IF77OZ5RnFwEA1T05zgbXBrjs1DagGZYP7mvq8nZB0qhxzeMAXE8WfcwP7F-8cbHHsVwzELkvHPQGi7OufJgUlV_q1bMX_nA9dLjiZiv_V7eCEqnT8Rc3dNGIMuvEcOlu4Rb2W_cgCh4eYhiyAJYapenHIumR1Ac0aR4bBUK8kfN8JdQXDqdS7S3QOLTbCmeun0ogS3mZw8QCh_DYxNhEH9WGHLjTSwCOeo7OnL2B4IjAY5NAYwlBG9cVnzJYtCSvmdR7QEq4qtPfPt7KFlEJwWVi9EAhjgY9Ip76MdPxel8bAsckEx7mjUYARuEkZjIB9yV9zOxqqnrClv73nGHJT8jLNbzhHHe0jrUpPpnWwVIfsJKneQWgN56q6W8RVbHM6sVJf7yXOo23YRr1jL8EG7JvVejjo8bqCWHp8HBK06aoShvJP6_yZX4smQVyffNMLpnGk95Wd638yPQVVtA7K9JPW1uhrLWs0hqYs4EACKjbF4l9wtiCY8bNYviQHqVgdsnzOv3h-BO0ASHv3up4MjrjjjdpoCDdMMvx5QUSrgEFaaFb3j5Z20GHHuDVvFkBNz1OXgO9QFOYDxFoXC-hLj_ciX0Iezp9EO8vstJWLcXK2lTtxfrIe0fsQlniRy5yI6aZRgM-na3dVvmvPkVGxDYycmFnkCSoF5NQVaWIbnr6SY1Pt8bi8Bz3r5p16tJ8rYlLfnCTzHcVAi-flYdvfqShYbnxxG-jnjtIiFfvpr0bKbymAaB514V30sH_2tN5wlqZdxWguGMd-qGgLHY8WleNerEp3Pupv_e_O9gNlDYDLZE83LqCRGQW-NNBrnleIN5zw6TZW9YSI0OBst8sKlIWP-SWIm6XBK-5Rnxypsr8yUgldRVwMLbnWgCQUiYam-zJ6tia1l8PxfxeG_ZkIh4nirxP-3P-8Orha_s5YMQObqBwvg8bqbumkQdXDOayE19vxc1gBIWyWQBWTqTDovu7iaAvv6kOkwGrc1ZHDUDn7Ha3cL-md0Pg7vvi0wTH527jp7s_VhJMi65SthH3QHdMm_D-297i6Tf48NQR7bVAWqc68muwd2SGxbOAWuV_cF0Q6H6CupO0bZxu0KO5ulVZpDnDi33y9QsVaB3XZ19p2_r1WBQMH4eCiPM9mVkagid56vx6xw_Y7RUxRFXK9a2jni7_KlqZmDBK7TqRpl4Akp3tJpd3bcXK-x5qD3F4E1s48_IwFro5UnTGnTLrSy5HL2g2Wb9YLH666gf6a9ZjXnA-Fxh6zGmiLtm54I0YEnRlFqhb2tVUUU1wRR4sq33noAomcRfvjgUpwzUSrrmIUVlviHZxRlZYHt2V4_SnemaS7Jde3YdmQ3WM28T3HTPC4hN0e_c7vlhqfvd0kYY16ZA5nsuX9EUDzgAvvsNEU6ndO-eoQ63kKUSxqfT9BiPy1U5fZDdZm8nkuCICdeL880u-kFxKdl9xQ-Rl_bDJNrnB6WA62HEwgTt-DvvLVSH6LCgsiieHpy7-1k_lYyS_Rb7TOrE3cvCdnenZnBrsDvL2-HK60WKCvd0LgvUu-QSL-IgyilTgCGNvOQvT8DR-DuooAeRW4wKv7j1RW7z4AJeIGppmCW0QUVoPjnRblsieBQmrpipkOql7_1L_Oo7qrhKWJIkn1f3iFnrHlUATByubcDNeuFCmoHQoVpa2OUmfiCfwBWHgHaGHxM0n_XlQKUG9FdEN11FdUiBdwIPoEW4_bxVyQbAR0ZCadRbYK6sfB8hYez3q6SsQd8czsHbXIFkA0AEj4ZWOJD2mVy2Q9Q2CH2TvIDEWTZsDj708h4HRL_6DtSn_dM8eVuXoxc53w6-hPXkG4Xaftg1mQ37SLaiAUfVXEleOJ98az4LZ6qkNkUm8XCT7rhzWFK8kC2CiztmCCVTWq4gpfQE-JWvvvO241pS1MCXHAn4Z0AXdIOxpQ6akuH2DiQoX75Y1QsZNF4zfRmPQIu_se0vfpkOEYoFHDwo75C5D44TB420ZyPAPEznh7gpYpABPOMFeF9HSeMT2cVg2uRNnaRP7yVwrqkoLD5sNe_ULpex4mpJwhX-ujBGVXL7vX2AZw7kY4GPXXDhQFYotAMc4aEWLqixDTw9XR1U_4qBe8MrVswccQ8KImJbw7OSbH_wApesS2JpIemAAce6JoBJsQ8pG3u5bZ84iEQDXaURXgUazNDPfnBmrj_B3JVA-lvZDwsMDNIXh78vZD_7jVOjqadMgPBzFeezO6rKy9dMUch9y0vt4t618wRy9LMotrAvfZUCpryZLMJUbI0SmDYoN9ZbpffszhEJL0CkLgwiI09Tzk9265cM6PsspcPTNaL8NuTu1RZeTToRlgHERNCKuTd8YETH48cWMlgOWRBFmvcER-iUpwNdJOgSOFwDMo8ZNDI8UFA7PnNYdXbB87AQGRwKy-ztgpPje10neOXjuJgyw18hpjw1wSlwJ6mNVcSFiiOAa3x1lmAy3L3M1vbPCHdaKFEoZg8nmdn7eD0DPuP9hxNkPC_rFGFOTl0ohd0eyudKG4989AMAEefT0amAzHEMQILtHDy43i2foh8j44nqZIk25KXzBxBmsmgaSg3IF3LU5wPQTT29Zwb5DM0-pPSoGClo2th5rpk_eBprngs8qnUQNCgLeUAfWxSCD1C2_IyoSjbZB4hp5oG09Uz1oWDu3dwD9Awu5DBizm3uJHwf0rinrNgDDs98V1t9TVIrLHjp8dTZCBBHT7hFTi9WSxMnnd0UmmDXXcGQNw3rw2k3A1cMyKID22zLba0EyW8sMoU8jT8AP0Jb0iPUDFj3dwR1eO-FoyMeL3jzigm1gMVpXbytHS_RaK4bxITRthL4bBLTjrBhOPkahkHK7w9qpytbWSXdCIyHkgXxm-qGoE8T7-05tIE_nL8npCD0qEI4UwvSUfFBDJ6Utgd3LNEoHsWPbTkKMONz83zJHzuSmJljRLtXIrS439sF-zFuHUURs4HoRIqI7qD8sLqvuTRegQ84p-XvnjJTMcRWeVrZUY1afW2Ky98mE81k2uWbb_PBZvzup3bpdbPCjZf3XoUhYkVEZdaXQEF_qj8n_JPhHHgxy_BC2-ZW5r-BDOlKzYEGFULJ4whceqPApuJAzQFoMk4HCGvsBrv3QhzjJrDuFrDsYGfpaaQchaCb0zkbBwKvGIETrrH6hlVb2-3vhVQoh5jZ5hR-PQ0F0pf9vA5I_8dOQr2Gw-UqicycUanyIodtnvC5HVKm6-_9M9UWRmDyoiKb6GlTA1-YKyQbOWuZuw770flFyn6GGDlOUSkoZGtp7pmkjdsULDpNwFtcGAFrcgqNYcw-26Y8kFY5q-5QBm3qcE7H4wAsV6z3CQJvpghDLWO9-tMsJVusQ4BTaHv6GhvfN7Orupg9_TCcU34b_si3wc7KBqN-PLo0zvSTD2qqcNZAUGL3JmIpCkcbWPEz3mCt9plEWo6YOp-jDhyS6EOElobMN8rT47ah183x-UM9cLXrECwCZ3_gUF1OJzp4juLWJPU1pwDgLN97xxonGhNDmlOA2kUHZggWLmemHeHd5GsrVJDuGui-2QnE2FZ2wjiu3GZTpLj4zh1euQh5KMz9sF_24SEQWnz6YpBFBQQV7W22MgILjrndy9sdB4TPfFIBOfQtyR8XBz7z5aprVlzKu9mFx5hDjHfIhFh9sJuqeHH0FFXgRu5ZUWD4JtpxqKqL4Sc-IgUdbnMmt4dU7Z3XcSq05uwrSszTN1WeaIk3DM2Sk7LKyNHThK2mR0BR4qQ7mBjK9pSRousaHozXcTynW_mj_hmZH14hRLmcaEf2S-x7oC4fasmRw_QRsDidxp8K-poXH7m7OX3IwYh6h5gwFAbQKmtmCXRhFGgMofr63owIdjwSsLxMmft2atci1kt7-P9oViWJflCpnfasPlLvn3FBnVrsGsJMOR2IXdozfIT6lq-HEaflG0royDVz6a34z70ksLvNR6-quriLrkE5jJxOqde3Mvegb2Wsq7LptH8ozs4GGzXIfCzd3_EBIXmNc5aN50DqohZHRuYdrh0nI57FVDSr2jpxNXLPVoJprThORdnKU0MhCY6DjOSm2B6UGziNSoyDby73w1fDiAglgMgGRh52FISZOmNKGIdguX1WGoxO9v3c5oN3J9uWunpfBenaV52AVWluUS3oKh5fbx1qaHemhRzi_Ax4izfJ7ZSwmh1hCsUfBzyzuUI1dca6PkTcnvqi-lxvr0P-cgyAIXASmqeOoMLeZjPqPfeSfJivltLq4e5JMmSTHJNwsPC_mGQYEIYC2ZjmXXh-1I98M-Yv7CDPYf2JEkftI9B5vw8HW2QRcJwjvr1ZsvoT9HSP11Nh-M5EJogA5gmUL78NdLrizXkf4Zu0vz_TCS10qEhSSRrxREsd9awc1wL55kmStlYqrv_oaDxu93d2zUg8iCy-9_I5AoB9pAD7VNdxkvcBEZHR_0QR4YFRpr5Pwn7iK8UtKmk1qDxz7tKVnST7ytaEvGKYh6d8opFXE1xVpbWTxwp0JnbCUsxRCYIwaW6leynh3KDGAqaxTx5qW6xmrT34OHJyz-8EeO2IYePEPIPUQlugUZr02YWG1e7wXidhwyHrtnEzeGUBQGiYT6Y_e-9-W0tOAoWl668tvH63h4qsInSPKJ3sD3lhREAXxs8_jdayspznq1VCxKVOiEqb_RbJ_eXTgsgC-L6XQbFVViKPaklc_v4QTzk3A-Ttmqur_pSf2gNPxJvL86gZKZ6t8TYhzA38i6s-EA_S2t4agcwpy_PavtkbHSaIk0UkoJXNi6tKbVmrsGcfLU_1Z8sko1lbNFEieIl9HeEZeV7J3Z_2KbVendgBwHpY3I7g8ZOfefpDeCakGmhMfBIlBPZ0Q7Bky73rDkaRJvkSaqkDqw8ugVISq5pTPdU76TV0uHfwqVZtaCJd5ZijtPb28FZEi5e_mmdUSiPucHW9SA4Y_dE-0BgFWMQN0-xYn6pgTBZAzX9yLDlAdtJtgDVVCEY_6_H4Kjs2DCxlJwigN2_TmEBfd-3o7k-6UtWeWRKVp9mEOPJcP8e9rdXMH5yMWVrOhFETpO_iG7qKujTBHTyWjPdOygdiuZyskSHIVN0rz6mvLqVpar_jRXAhzFgioZCfHLT37DlSl_b7JCWNuRdUZKH402yfcgN5SdsrnQcenBm9c9-SD1ygcMwyBO91SSPApErCIGvP9DvaIWN6_OClELLn_b8DKok_w2QrzV9v6kfGhPQZifJCYWj38GDAFDdxFFd_EKQy7xDbwP3xjGvDkoqdVIQ6fnoeURkhOVDjD9GmegT_kvJxedwypxJNLLMM1SlG0rz6TpDV9DPDiNO5kIUOGHxGe527i7v8TjlcUtZHlijjEHxy9ljg3_Ecoe7W2JcUMhI-6_M9cBgKjOa0_k11uMKwGQ_RJHp-quGwYEEjN8_f3Ymc5-p21beaS_hAbta3DeHD9Tvgx_Qmuq8zgj5Xi90IPHfmMOZybZ83w118LaW7C8Q-TzFOASUQ8rRWv_U-T97CaAjLNER6XFfJM3X8o75XAxjgvu1mJQQIYyWp6yppo18evGdwd7llm6iTZrz-NCtBFpaD4KE9AFc0OC5-m-af3G7u4XEj61IlCvqZAThoChFfrwOvaVkEsCy791Y9n6MQXAV8yF0GIHqnSYFBekyFbLq55RvUwBHh-Z9dE7Fd4X-I7gl4084zkLkjg68If3f3p6KYU4Ti8X-QyN-9sEnqyoZpRRCqenBX1rB-Bnrj8Vc3aaT6mYCYnhrXDAapAWjbE9UEj-CbRXeTbuJ0zmog8tq9Q5s5VzLZ30iPBijBEVtn7vYT1bEtDokY-ZCrYuZ4ul0e2bcw0XyZkaqDSH2bAsBykwpVN2GITI-nwImpiqhwS2RcBVsY700PNgxn0v7yswYyk8dK4Dl5xXxuEKrp5i-hThA3z9wMebdynQPjQtyrPE9PXT24hpYHs1ie9c-HOPM3UoiBUbJ2FAjAp57lW8mn_SfSrdl3-TJTRHP9ATvIqgLg-Vq2AwaKJQqh5W15Dd3o9rIGladYjDTZmb4B3BdPA4hVCUVRK9DfE8qIliCt2FKXb_4sAroqv31vOyfnGlx-n0e5M6vHfcBN0DUmXbRDuSrqTf2sS_vQDR0GHL-N5H6v9A6RiU0rSKKXBQDsWS37tFF-UTIQKoPWgykt6boZTPPsENUeeo11ol3Lb97ioNBVlqZPIthTW3ppDL2uq2S5-OtBvfDp2h6ewxR1Y-8ZNFBtdO9AqbT4I4Vyn_QM21NjQjAwfQBAps80leCssgQDkMUrNV7EdU9n4mT1AoSotmgQynxDJCAka5IbgRHV_X_Jk5-xznT_ELCqezB_YnlQT-51eM9AK-9cVbovo0A6YGQxFUwZR1xJWb6gLL3nLsAaNPxjzbeSq1yqan4Sxn4F4jdzeQdsZ2zQ6fOBICNESlw71fnX6mWfv6dx3qWX8x0TQ2BPeonKqBwhKIp6Sw0YnGBV74MA5viYANx91XzSMSk4FZuQ4vl1wMNnfX5d-qEu43O2NvUJa9yvU65p3gE_Wx4xvzd_nFpVegkR47LzZFg113lfIth4rUCcyGwkaNComvTzrnCJBsKxNVtGiwozDS3xrWahBbfhemP41eOrYyj5xnnbwQzQ9NbpLaWiR48QtSWZBeUHZm42yrJuuP_ovxuCmE4MSWh4ehhx20werkebZiB5lvwMGDNrtCoN9XyAHB2tRFPW43Rpr5PXFq6RLbONkdGh6Gp5q2LwDctN_BHly9VqVrxZTCX3HjbeDCh-Gn2QsahZY9ScPH0A8eD8aX5bTzQQMpurJ0Qo4dY6BJaQTwGJ6z1jXF0rGxUmO0Fm160AkAXTlGvKTdx_ln3Th8Dwh-9ty2OKKccjLv4Mv28CxGf3IPVSFm2gED_3Anxnu71ejHgDWDqUuL39sKZ3MHp15gpm5m3Tap6dQBa_A2bKLz_x0fhLvpMmngQ_Yp_5C5PAXZUbgjKVe9NcxdE3ut_sZ0fcwGyddaZF6zkuK3onCEUZC7I4v5CR7mF4NYYQxdnrQcgB2QHd7LGskm0vD982jcEsuegYh84BU8ZyDuDT9dlIEpMnTEi2FnuoTjVzvkzylTvOU7CpP-v5IYDdPsKhnojuR228VCJFrc4TIveAntMiUBQdM08Gm8LNlhQbHtwMZ89g1vk6N3Ef-gkEpdL82nz2ryjTXj-pjFwRo-XZVfDTT58i0D3hLtJKEPawN0NJnRE9mr_PxkQ-b2cA653dCLjaqjJyl_km8rVnPXtpwhWU2hKWvuUuSEZPBItKUmcU5GzVC-YOAgwodBWwU9X80SxGIeALiXq52YbJTBmPOmIMgqA_Th4G7aKF2J3h7WUE8Ff18Up-U_XB9bEQvaKwK7-Mc6Ww0BNxxsxRLPIfPwx8pJw_4PeU9Pdc3cSCHwSF6PbXTv-a5aRgqGeaZy6Li63qRo_fxouepxG0zvokWeDk8wk8eRVtYkBf3re_ODtUs2TJvpVw514sgZZxgPVr1S-vlq06d4f779mwM-kb4JNsB5Cetp-2i7FIwbFvPRJpU_-dUVnCFl1qzpbDjPr82GCiE7WZc42g9lmeQjy9e67OHtsGgNmY6VQIRaXTskvfSe8auQrMZsaEKkxdFYhmDozTdYTJwCIj2UMMF95n-b5VC-gmeXGeRhyfefNBB8_cIWTfzOz1kyLUIHw-fYCxeAcUWBJPcaGIIAc3MfYVOPv26MRYFOf4eMQzP7zISMbo7jNh5eVNQVR1uEv3LHdIkaui1dDPJoqSbyBzPjec_lQ4XudErgq1hSNHszrAjFeHp5aZzX2USaR2JW1WcGw0x-dNRrMZDH7LkyGP_BKr8cm9IgrLI3p5RwN-y5nLaP7Lj-Ow8lq_1u-nqiWuAsHk-cEZFbICCg585lDNNXw2IL3eVeekezAAHnVlCblPr_oCaPh08ziTDtWnGLb9NkfNJqNKpSjqRbOZusNqUjRbUOyWdHGegFjXE7oi4v7O9jlHHkJzaF3yunfwVQIrK03A_rdvuDqpBbyeYDQFWZK27gHHc84mPyspAr1r-x5ESkyYIvQrrxecHLoFHrjmNHUuV9dhMdxK_tEQ7ll-zepiYw74ZaBYUJq-7F9ADQ25L-ymYex3blbmyPBHnbHeVv2iKnK2tgznkUeCMWTkVpbQCkwNDybU5eZ5ZaTqhblJ1Ys1s2zAD_iyTLtxZ4Vk_MXrYTejZw_uTEtnvFTHcgjOyNjuALZ-EUmVgdOJhqcjPyMBId5uToVWIk_6yuE7hP2v7lQRK6o7eGJ9EOLK7aJgowk_sQfi2sdWLfClrZFNUyGY9QqqrPSR5t5ZNgyKkihpEkv0gwWh49cBKR2TAmxKTx0e0uXB5gZbOPfNFFalE_uZFFjYlkHCYI3xQJpYnORXvx0r23lACuSvh67WY6LM6ezAIbwJ9ZeUqEztVZDmzFCJ4dn9WRzCbs49s3PrBJLW7wAzvvk7p37WdJU8PLnnKf-ynVlFUpGJIZugZI6K6bE4H8Sk4T48VgPuaP03HcQq2mvXQIByD4Gf1y59v99WFSS4rVJrfzn-snzX3VnxyJCqYF8Gyg1hItVFkCf7RgmWzJ3P3ebYYwJJQMh-Gy78VDeaMli5ASwD_-z8hBqRBnuWYBqfPvaha512ahMtsw-R5mfcAZok8-fqMs8Gzb4dJ7kPa-_YaHKQ-t57VHxR5s0LrnEeCaSIFChrughaoQqFBciwkKCTEPglleG24VzT1-EAtrUJTUspL9uPILomlgCfC9xYUZBP-d3i8stQtD28yyCRU1mWmt2Wpj7keASbCNV8A8m-MwIAR6w_ulPhlCa8Pm4u0of84nsA5LhBO7YVwbhW8TEB4qziel0FrGj4bYLC90KA0XLmdxQn5jMgBs85jjCYmUeg1FY6RHlKrgZdWPbkjIhQBlbMTLLtSrpBoMySA3tCdYhcXYZyptlPjrcm3fGNNthSzVImDvkKXenxuqr9Fb3fO78Wc78CsRrB2fYXTxJisdvyw1yp0zIUeSzGX5qgGOsdpIlVr7yO7zJrl9DZSFRgju5OwBETGxrdzWHE2qky8agpE8RT01WGmeemyoSrKvT1vuS88RNwW1lkNUNh62EVHpnXnPPNxp1x74mrxE_bFRUkRMPZUplvcj_8LCCzvdW67A7E-9Icu7pNcu_1sHvSrGbJe4V_i8ZXZT98PznWold0YD99CAng7xdZXhbkQMb2F7Gimup7_hx2_fNq4IJX9kbHcJhRogl1DIu_-7jWd7q6nf_KVZGM6btBmbJY705D9dW27CKuiu54wJtyo3wvriERDy-we4yPQ0zeoIM8IXj3mjJT4moaSvWKrqSyfhEawnTw244r63FNQOAUkQcQY0RJa19O3ms22FNVi_UOK3et5duugt330-iVxmKjp1eLEoUghcsDF7ey79VJfIszCwRXjr-CArL8bDdD7dHss46EGEeLBLp7_NipeZ9bXY41Sb4wDoCae1oLxL6IglnBVt3eSqhrkTX45hwbARp6ReDBiVYVoiCk3ewmuE3syuj4peREh9o1ImMYgUBN8ojSNbhDIs7w80eED0rwY0fMFYAWfkjEzncoUqglQZLBylX4muMgASnD5yjhCtv2FWyLXt-rS4T8aL9TmVKgLCrq2QiqcBWfQEaQHHOv8Wteuibc39ytog3B77hnI-zxXD31CQoKHHwSgevx5bmdtLu8AuQsrYMPF7h_RtNqK46TkVNrwFRgOTAEn-CJmcol0VXgAVhfFAOKOnz43vrMwbHlQDuerzkyPLv2KOJUidvrWAXgGAl-dSoD49G3sFBNRcmLjy1C1Xb5EaVreQS5ub3kkwLEpabhom5zKJliqjlq9tSHYZ-j8aPMr-fmUuzmu93tT2MmP6JnMO-XviEeirFYNIyVXXcF44yx0xQy9x4nQGflA8fOznOL2G-duQOdmc3la9REpAJYfiwlyAw4XlQoNStNceHl3uIiWs2iYcHU9ZdoAZG7GLtpkl0abzopqglVcz6vP_H7OS8TV-wOqZwOseKfDhyk4-nV9yT44ikgffvHOm1wpNtuWB41b-XTvqjOwsvOBwMz-wIQ-Dg9TrM6_Ub3ZktW8thXrDhwKgLkI6KmvGOH_Yfmjr-tcgbGXyF2X285pP5bUgQxvB-UtJJFedqI-sxRGDF9P858AzSLOUP0ihpxglX4Po4y6Z4pLM-zcXeIWSzV_AwwJgRMw4NO4mL78aVBmqpkMHm9DSRJiKA4jon3qtnlg3VjbnSao8eaQtpGnYfB-aTvxM-9t6Cgjx40t_jONZZXbqLad6-__XZtm9h1_qEo1rN2vjKi07A0BLHIHeu0kRfWJK3zmvo5EsMzjOSgF67pM_aqITjnPN6jRjWbRNFmOkoaiq5eEdhy6k3V0Xro72kxM_04U7-8LWmlGvopkG2PUE4vypB3RM6SuuUjn5CYmgsr5_lPgJvIO0jF9LKB3DjzD3PVZx3lXq3OzLn7ht7rZ7hSZ3-LtGQAqSaMICJTKEcva9Txpuq07yxdWEp9b48eBwUnaGT2bOGl9MKjluOZiZOIRMn8jzYd6dgSchlhNCT_DnDP_aaOJFsPBbrrm1Hs_2z02-eRQDLFZcb24_ZvOAkHSIHkDVVIsJTAKARIF-ISyzkvD-Qe8G3HBhUOv9ceIL79y--gjszWAoQgN6DIuf_oJ1bI4aypeR15iFlCuNW9TBeKqedUuuZrlpLdsqgpSR4m2VnPXwfEgl_xnbeMlzg4_rmabJmOlpCl2LJ8BgLllrYw951JQApwsNW5jjM4fT6vJg3mzOPUFJKNXDcmCtj1vU-ZJ7JO-KUrj7h778u9IlLPV31O74AzeBybUJM0yTxMyWdZtKov9kS2GmkJ2clzb7K2onVgcny_D9Rp4Ltx9-Ry7wa1oHkSMSTJqiemxvy050ZB1S_RPARN7kVA8r0zsUCFfNsSOofovrv36NR4r0v0etUH5oUgie00CWVHmpe23DmYEhy4q6oKsm2BqrW3pB05uk_F2P2sqCbQ4ET5u50YLTnY5Ih5A4Id_24AhsRDJLhojzgp4SMfB-ynT0a4zSI6F_lU-TjFqrIAmquZtRL7U8Vx7OczKDp--5ATLM02Yw-QvxxZdvWSGrcA5TLNK_xuhloiUvZVwMZ-7c_FFdSJzp8dOYCN91ug9zolA1vbU9G5O69hz6T7AgGx7nCsQrkfyCjf61oddRRa7-TIscJzboGIFDFyinICbGsA5-szp_cVlwvAVu-solZ-ePcBGfsKd4Fan14iB-Xf-2FjavujKKeNuCM6HAN7b4VRnCkwTo4lnL3TW1kFL34d5U6RSsR13riJXDwjGxB1rtaTFbthTMjCgtLNPwPOOx5l_K8nw3YFY4yqM6STeblRue61sMiEyMPGs3fIBcuyYDOuEWv4q9GqSe7P4XFU-ZlOrvVefFAkfO-qxiSD-hEYIV9V1gL3QjLcZctSaAeXbDRwgMZ8QxXFxVBzFAM0LOFG7shHgiGRE6TmeaA-VX1YKCpbON0xEzojKQfi-mcEz6eQGglmcvWugi99AD5opztw7KS5THFAUyBSRx9_EkEFS2WXvILc45mvuMZRPp6K37wNe8AWqQqFZn62vxf8OEbNoX17TOTltedT63jNiuXGaAWSjTXfv_3W_Brte9cBEcrJflRLF1UbitFs2nt07OR8V6D0zdvEtpeLXJ3_BO-hrxN9oK-S-NZywwolEifU2hlWIiX5UJoyM_E_G9OBZo2zpSk1PQ8bqi_SE9Yy1Z7qNJed1Voytbh5tUqVzqu_16bBjEIXO0qD5WFlNi98ArA7S7wD3-LZaKW-STQGlz7ziDdNDzvFPOU0watzzwDxy-IA_mO9TroAdoI1NpxIDZTYM1MpBYr5T56F6rewvgD00ujg1m2SJmraVobqezq0cZ-7DTOlF5eZ5SXazIzZTnEDV6vjAg3XeavRHDjUoie2NSI3qo5NvY3cPa-aRmjUhzDA_csyTx0e70wv3YVqvHq-4DlZpl8czeStm2qcpuB-rcSPcPNc0_yMXxRQ_LNWZzakWeet0xqhUhWD8nbsA7zZF2N6maAyqmtOgVbFfokXpCil_Q16qEj4spfrdDXUHtnWAL9aOc3mKZaDd0wx6yJBSswUxmav3-vq8ynAKeDVdwfy6pB5891N0YIaCtR5w9QFg6DDD8VlpRtXSke8iQou4yhY2OmlOK0eNBncb7bWjuzBo5bgFbRYxzVAYulKJAh4AwdTK-ikUVeWDke_DmM3X70ElqBxA_QCnGR2H-XYDrN1hdgxBcBNScKkYs3F_bmLPSY9bvz-VgxMCdOrVfRvbpKa7r2M39wWBNAY02cJ-FDN8VjdYHcPXg8SAzUSdy87Ot7kRdveaBC5ZL1T5q9TGR9QzpKmoogvL16wNHfvuWYlobkmx45eIcvkM84XfEe1ewL9OH0jFysYipka1W761bXFCAeExtzkNHf8elwtnjoAAqsDACRobCc_UCmy5luEVXh8fkT_HNvhEW2nnxAFvlqfA85MWM9xL47sZznx4kiCfPxhOuDaC9kJgfP5gg7ED_L08UpRoB1zkShXwZTMlwt1hzHlJXY8tiZTOd9YVkRaFQZXpFuJ5kmI3Dkwyjlqwz53sPfd5LlmRAJDi3I-dwcEi-_tPqEFqj9QzmbMr2SKSts4iJ9TBYyIPl4_y3r9HOX8Vo-ROLSYp_WTJl1le8OAIe6Tq6FjZoAwqb3ommBEcsOMi6vhtskCrTqWs66VDqQYCUy1Xqu9HDM38QQVA2xdXV_i3a0rHpjPoz50vesT_wpnHpUTJcWPfaSsoVHs4ahdE0dVngGM8Oqv3RIdu008w2mupM2CdYhlyLT3u0C_TPs9CqzrNmAGrMrCbTwcBLw_-0bUgz6m2YC54UtQlo_aWbX6vs8Dt4_1EprU7Pln4K6vNMUi4IedIPjgc5rJrG8E3O0g5_vjdmH2ioMfFI-Wmi8Cvt_a8EB0pj9U_y5PAwWSVisoBH6DqYo-3ztl3bhZzNBDc56oQRxJhpGegst0XhRuageDAY1cO0uBbFav7ibA5N8r6heB0VMgg5g1QkRuiKrw9Z3krS1gEcvAU9fwIiX1DA-0Pmi_Gt4GGEkcW-jpTh28g15LzIKuzGf35-7daJT87M09wDmvw7hl3hd92WuDgpuSZjVxJPtcWGeSKi1QobyYxCG9eAAkkiZRan_yoP7AB-vgwPEGI_Sxc5xACeCDg3C8Y4xN4HI1K-0zdQeQUOOb2VMDJNcsKiCfnli5D3mFljBOikJIHR1VcId8KvS57lLR7tbLSbXJ4TImmce-42rBsVpw4Dvv9b78M1P4JHE8lU6EbXaxZAI9buWzR4kxqa_auVcFRb63K5Dgwfj6swGoZo6YW_g0XvR2RBtSYWvOEJLJ5jvtFYz4_tfubzTwRDgi0xVLpHIb4vadrwLw8OEskf-vMMwEkMWgm_csCSAZkNkvU_keTs2s08Khzak8cGyHwvqskRVHvk2Jli4qblJVJqkY2_MEEok9Fo2cVGuJAjoaGS1A2t6K2z7iAVkE3U_NAyqE8WYXOFPy905lUeuLBeyWU_s-LIX5NLd1Y95mYAKubqehThcsWjv7Wqk5oz6cczPN1lNpTIlK8i22Mr215wkH21Ig_fk4oHXfJtJpTREoef3XprpKI_FRTGRpgmcv5vDYxTTi6kXt6fw9dnNKwTzfsxXgiCOdSiu2mcxM09GfZMbmfCnuMJnkuCUS6Fw-UjKC5UHW69jOrmEHGHdPftEehRAS3N9nkUbpEb8if0fThMkwiiQvaa__Bz8c61F4u9454KEM70hgQzNn1PF8O1fXMxU_lQHnap9RPeQN94ElTHlvOOGdfLnhGNdE3ekRcKql-vKzmiKXJmCCHbk41skZVD-f7dKrjpOlRIkW84Ssvh6xhw_yxPkOXkEiGJA3Sf0jRNmS9QTuXpD1wUJehlkDNTl2CutfWXuA2e1tRw4xnc-xVv3wYQFhHEqhI1lkJdmFQ9cLdpG94cIkUDlXTTZ9UG-moiV9x2UjyBMtebzXybEmHyDptkvMRfZZzBGTWREwrweU_WM7TlAFT4-XV3XqXiEQ6t4qi9RLI-TGNMOXyw1x7IndwfC2SiZ6lOWgpobShEUekSIPitcO0uyp8Spjr3nwNKySnD0_u4p_fT6BRUnpTQAudzSfqYIvP3nsJ9oVJHbnJ-Ua2TDkL2QpCrXEbx7kWVg8Q7WQeuZ9DAWqkUiNEXV4PdsMXKw8bFLH5BUMhEtu6AYE17iv-kXFnZTyZTaWKBrdSSH2Ht-bEjJo2UZcTHdwTDvLi9ntaa7E67yLfLLfFVdddILiWWOigWvj3MEGW3iqvRfZd3nbUdTJQzLKfkZej6cevbNFBQIALuYclQhxMeEectC1kePkuE-jWljRNSqKZipICRQmBCRYdNcZNO54Q3KUaOz-khLHoViIbMaxRzp49Hwy4LPagXtIqKePB2FFMPMziQaJjKUIPPVDObZMp0L-KNcPZARqIr3kyG82CBY1s-HkLgKPso8AVMAViPiaqC35DiPlV3cuSnYc-ADtLYLejaaIEvYC_I1w6CsqpbwKaUIWTXz4LPDU6Sj1emcHGErdW713dRLiFWHERMXs8-ld95D_ns2EtbKfg1XUa2478J02fB85Y3B4DlsTYCOptAtXFIh0Q9n-Z_ZoFteoe3nGMspzBLI9HHUlebQ71cVD41ldEBdlzSsXSgtyDdXwYHsAubZyL-5cDw20mg66iqjefpsGTmP8gpCVL0s2HKy9uwmnd_pA3q63KxSHDtPhYcSEVh8Q_D7ermX-xS4AUsdwGI1t1_IK6JwsHG5ne1Qd3EXRTtaSe1k9AwMzG4jkuoRuDkDh9oT2_uzSJSyZNOdMogzm4Rf7cB1yP9Q1WOutjTbAIgyZw73tuc_leN1v95sOjkf0JIqJAXZ5hOESrd6aYLX54GzKtPOHwuec-AUIJxlAQKHz3PEYjHnCMXmSyLJ6ab8pyA0k_KTa6d6hPCf1h4TaKN5XM28xFsc5iTFhyuwYRoRs8mR4yVtejed-pm8ndfBcbU4vIvM4SEw0qezVPJMAn-dO-USamvBSe3wAvfa9FKFEbm9s8Wlv04dwcggJ4vCMJd6yfJzIc_2E_WJXDWKsqGjkfL7-3r-SD1rluQzA6Eh3qOtfRZ0Eg7VaLYCdKfoTBCdOdG7i9HpL6XwBvKUNJmTaJM5GiBDMGG8WCLvQ_g9ZqEMby1Hncp4vh7HtiRhzS7gQ ``` - \ No newline at end of file + diff --git a/docs/endpoints/misc/getTop1000.md b/docs/endpoints/misc/getTop1000.md new file mode 100644 index 000000000..0d23524a3 --- /dev/null +++ b/docs/endpoints/misc/getTop1000.md @@ -0,0 +1,23 @@ +# getTop1000.php + +[getTop1000.php](http://boomlings.com/database/accounts/getTop1000.php) is one of the very few endpoints found on the servers that can be accessed via a [GET request](https://www.w3schools.com/tags/ref_httpmethods.asp). The purpose of this endpoint is to gather the top 1000 star grinders to be used on the star leaderboards. + +## Parameters + +This endpoint does not require any parameters + +## Example + +```py +import requests + +req = requests.get("http://boomlings.com/database/accounts/getTop1000.php") +print(req.text) + +``` + +## Response Structure + +This endpoint returns an HTML response with a table in the following format: + +`{Rank},{AccountID},{Username},{Stars},{Diamonds},{Demons},{UserCoins},{Coins}` diff --git a/docs/endpoints/likeGJItem211.md b/docs/endpoints/misc/likeGJItem211.md similarity index 88% rename from docs/endpoints/likeGJItem211.md rename to docs/endpoints/misc/likeGJItem211.md index 296b730a6..bf91bed5e 100644 --- a/docs/endpoints/likeGJItem211.md +++ b/docs/endpoints/misc/likeGJItem211.md @@ -10,13 +10,13 @@ Likes a level, comment, etc. **itemID** - The ID of the level, comment, or account comment -**type** - 1 for level, 2 for level comment, 3 for account comment. +**type** - 1 for level, 2 for level comment, 3 for account comment, 4 for list ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -30,6 +30,8 @@ Likes a level, comment, etc. **like** - 0 is dislike, 1 is like. Not sure what it defaults to if left out, but it can be left out. +**chk** - [See here](/topics/encryption/chk.md?id=like) + ## Response Always returns 1, regardless of whether the item was liked/disliked or not. @@ -58,4 +60,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/requestUserAccess.md b/docs/endpoints/misc/requestUserAccess.md similarity index 74% rename from docs/endpoints/requestUserAccess.md rename to docs/endpoints/misc/requestUserAccess.md index f61a23830..11fd6b0b3 100644 --- a/docs/endpoints/requestUserAccess.md +++ b/docs/endpoints/misc/requestUserAccess.md @@ -8,21 +8,21 @@ Requests moderator access **accountID** - The accountID of the user requesting mod access -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user requesting mod access +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user requesting mod access **secret** - Wmfd2893gb7 ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 ## Response -1 if granted, -1 if not +1 if granted Moderator, 2 if granted Elder Moderator, -1 if not granted ## Example @@ -49,4 +49,4 @@ print(req.text) -1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/restoreGJItems.md b/docs/endpoints/misc/restoreGJItems.md similarity index 100% rename from docs/endpoints/restoreGJItems.md rename to docs/endpoints/misc/restoreGJItems.md diff --git a/docs/endpoints/multiplayer/exitMPLobby.md b/docs/endpoints/multiplayer/exitMPLobby.md new file mode 100644 index 000000000..e5bae29b9 --- /dev/null +++ b/docs/endpoints/multiplayer/exitMPLobby.md @@ -0,0 +1,29 @@ +# exitMPLobby.php + +> Note: everything here is based on leaks, unused code and educated guesses. As such, everything you see here is subject to change, and most likely **will** be changed! + +Exits from a multiplayer lobby. This endpoint is hosted on `geometrydash.com`, not `boomlings.com`. + +## Parameters + +### Required Parameters + +**accountID** - The player's account ID + +**gjp2** - The player's [GJP2](/topics/encryption/gjp.md) + +**secret** - Wmfv3899gc9 + +**gameID** - The ID of the multiplayer lobby + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +## Response + +Currently unknown, but most likely 1 if successful and -1 if the request was rejected. diff --git a/docs/endpoints/multiplayer/joinMPLobby.md b/docs/endpoints/multiplayer/joinMPLobby.md new file mode 100644 index 000000000..e8cc6c22f --- /dev/null +++ b/docs/endpoints/multiplayer/joinMPLobby.md @@ -0,0 +1,31 @@ +# joinMPLobby.php + +> Note: everything here is based on leaks, unused code and educated guesses. As such, everything you see here is subject to change, and most likely **will** be changed! + +Joins a multiplayer lobby. This endpoint is hosted on `geometrydash.com`, not `boomlings.com`. + +## Parameters + +### Required Parameters + +**accountID** - The player's account ID + +**gjp2** - The player's [GJP2](/topics/encryption/gjp.md) + +**secret** - Wmfv3899gc9 + +**gameID** - The ID of the multiplayer lobby + +**lastCommentID** - Your last multiplayer comment ID. It's unknown what the initial value is + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +## Response + +Currently unknown, but most likely some info about the lobby if successful and -1 if the request was rejected. diff --git a/docs/endpoints/multiplayer/uploadMPComment.md b/docs/endpoints/multiplayer/uploadMPComment.md new file mode 100644 index 000000000..c5656ed4c --- /dev/null +++ b/docs/endpoints/multiplayer/uploadMPComment.md @@ -0,0 +1,35 @@ +# uploadMPComment.php + +> Note: everything here is based on leaks, unused code and educated guesses. As such, everything you see here is subject to change, and most likely **will** be changed! + +Uploads a comment (chat message?) to a multiplayer lobby. This endpoint is hosted on `geometrydash.com`, not `boomlings.com`. + +## Parameters + +### Required Parameters + +**accountID** - The commenter's account ID + +**gjp2** - The commenter's [GJP2](/topics/encryption/gjp.md) + +**extra** - 10 random characters from `[A-Za-z0-9]` + +**comment** - The comment, with some delimiter characters stripped out + +**secret** - Wmfv3899gc9 + +**gameID** - The ID of the multiplayer lobby + +[**chk**](/topics/encryption/chk) - `accountID` + `comment` + `gameID` + `extra` + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +## Response + +Currently unknown, but most likely the comment ID if it was posted and -1 if the request was rejected. diff --git a/docs/endpoints/rateGJStars211.md b/docs/endpoints/rateGJStars211.md deleted file mode 100644 index c18992641..000000000 --- a/docs/endpoints/rateGJStars211.md +++ /dev/null @@ -1,45 +0,0 @@ -# rateGJStars211.php - -Sends a star suggestion for a level - -## Parameters - -### Required Parameters - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -## Response - - - -## Example - - - -### **Python** - -```py -import requests - -data = { - "secret": "Wmfd2893gb7" -} - -req = requests.post('http://boomlings.com/database/getSaveData.php', data=data) -print(req.text) -``` - -**Response** -```py -1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/readGJFriendRequest20.md b/docs/endpoints/readGJFriendRequest20.md deleted file mode 100644 index 867d4903f..000000000 --- a/docs/endpoints/readGJFriendRequest20.md +++ /dev/null @@ -1,54 +0,0 @@ -# readGJFriendRequest20.php - -Marks a friend request as "read" - -## Parameters - -### Required Parameters - -**accountID** - The accountID of the user reading the friend request - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user reading the friend request - -**requestID** - The ID of the friend request - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -## Response - -1 if the required parameters are met, otherwise -1 - -## Example - - - -### **Python** - -```py -import requests - -data = { - 'accountID': 173831, # DevExit's account ID - 'gjp': "********", # This would be DevExit's password encoded with GJP encryption - 'requestID': 43199784, - 'secret': 'Wmfd2893gb7' -} - -r = requests.post('http://boomlings.com/database/readGJFriendRequest20.php', data=data) -print(req.text) -``` - -**Response** -```py -1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/request.md b/docs/endpoints/request.md deleted file mode 100644 index 317cc963a..000000000 --- a/docs/endpoints/request.md +++ /dev/null @@ -1,200 +0,0 @@ -# Request - -The **Geometry Dash** servers are based around one main HTTP server, rather than primarily through secondary sources or establishing a direct REST api for usage. All endpoints are stored over the `/database` folder, rather than the base. - -## Server/Database URL - -```plain -http://www.boomlings.com/database/ -``` - -## Requests - -Geometry Dash uses *POST* requests. The data is in the body field of the request. The body of a request is always structured in this way: - -```plain -param1=value1¶m2=value2¶m3... -``` - -Though many modern languages implement constructing data from mappings. - -## Server Responses - -Most server response are formatted in weird ways, not in more usual ways like **JSON**, or **XML**. Usually, they're formatted in a way that consists of `key:value`, not really in a way that gives each key a name; but is more similar to an ID system. Most responses will use the splitters `:`, `|`, or `#`, the last two being mostly to split between multiple response strings, usually seen in filtering endpoint such at `getGJLevels` or `getGJUsers`. - -### Example of a String - -```plain -1:61154556:2:Chillin:5:1:6:12901137:8:10:9:20:10:3348:12:0:13:21:14:373:17::43:4:25::18:3:19:24962:42:0:45:29651:3:UmVtYWtlIG9mIG15IG9sZCAyLjAgbGV2ZWwgY2FsbGVkIENoaWxsIHhkIEp1c3QgYSBzaW1wbGUgYW5kIGNoaWxsIGxldmVsLCBlbmpveSA6KQ==:15:3:30:0:31:0:37:0:38:1:39:3:46:1:47:2:35:669275 -``` - -## Examples - - - -### **PHP** - -```php - -``` - -### **Python** - -```py -import requests - -# sets the target url -url = "http://www.boomlings.com/database/[insert target file]" - -# creates data to send -data = { - "something": "value", "somethingElse": "otherValue" -} - -# sends request and reads the result -result = requests.post(url, data).text - -# outputs the site response -print(result) -``` - -### **Java** - -```java -/* - * Using Reactor Netty HTTP client - * https://github.com/reactor/reactor-netty - */ - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static java.nio.charset.StandardCharsets.UTF_8; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpResponseStatus; -import reactor.core.publisher.Mono; -import reactor.netty.http.client.HttpClient; - -public class Main { - - public static void main(String[] args) { - // Creates data to send - ByteBuf data = wrappedBuffer(UTF_8.encode("something=value&somethingElse=otherValue")); - - // Creates HTTP client - HttpClient client = HttpClient.create() - .baseUrl("www.boomlings.com/database") - .headers(h -> { - h.add("Content-Type", "application/x-www-form-urlencoded"); - h.add("Content-Length", data.readableBytes()); - }); - - String response = client.post() // Creates the POST request - .uri("/[insert target file]") // Sets the target URL - .send(Mono.just(data)) // Sends the data - .responseSingle((responseHeader, responseBody) -> { - if (responseHeader.status().equals(HttpResponseStatus.OK)) { - // If 200 OK returns response body as string - return responseBody.asString().defaultIfEmpty(""); - } else { - // If not 200 OK throws an exception - return Mono.error(new RuntimeException(responseHeader.status().toString())); - } - }) - .block(); // awaits response - - System.out.println(response); // prints response body - } - -} - -``` - -### **NodeJS** - -```js -const http = require("http"); -const queryString = require("query-string"); - -// sets the target url -const target = "/database/[insert target file]"; - -// creates data to send -const data = queryString.stringify({ - something: "value", - somethingElse: "otherValue" -}); - -// sends a request -http.request({ - host: "boomlings.com", - path: target, - port: 80, - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - "Content-Length": Buffer.byteLength(data) - } -}, (res) => { - let output = ""; - - res.on("data", (chunk) => output += chunk); - - res.on("end", () => console.log(output)); -}).write(data); -``` - -### **Rust** - -```rust -use reqwest; - -// use the tokio runtime -#[tokio::main] -async fn main() -> reqwest::Result<()> { - // set the url - let uri = "http://www.boomlings.com/database/[insert target file]"; - - // create post values - let data = [ - ("something", "value"), - ("somethingElse", "otherValue") - ]; - - // send the request - let req = reqwest::Client::new() - .post(uri) - .form(&data) - .send() - .await?; - - // read the response - println!("{}", req.text().await?); - - Ok(()) -} -``` - - diff --git a/docs/endpoints/getGJChallenges.md b/docs/endpoints/rewards/getGJChallenges.md similarity index 86% rename from docs/endpoints/getGJChallenges.md rename to docs/endpoints/rewards/getGJChallenges.md index 726998013..1327a52ab 100644 --- a/docs/endpoints/getGJChallenges.md +++ b/docs/endpoints/rewards/getGJChallenges.md @@ -14,15 +14,15 @@ Gets a user's quests. ### Optional Parameters -**gameVersion** - 20 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 **accountID** - Account ID of the user -**gjp** - The user's [GJP](/topics/encryption/gjp.md) +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) **uuid** - Seemingly a random number also used for identifying someone @@ -46,7 +46,7 @@ A list of attributes of the quests, separated by colons `:` as follows: - Second quest's attributes in a comma separated list - Third quest's attributes in a comma separated list -This list is then [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded. Then it is separated with its hash by a pipe `|`. It also has a random string of 5 characters appended to the front. +This list is then [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded. Then it is separated with its [hash](/resources/server/hashes.md?id-getgjchallenges) by a pipe `|`. It also has a random string of 5 characters appended to the front. ## Example @@ -72,4 +72,4 @@ print(req.text) uFpntVWhadWMLCAsADwgLCwwCCw8BAAUBA3EUVFBXGERCRRl6ZxdZXEpRDQEDAAYHAgMJGAUdCxQBG3JWUVoXd1BWUFJDAwkYBB0ICBgGARVrQFZDGXtbW11cW0BYQwMJGAYdCAgEBx0IDRh4Q1sYeVZCTV1G|00bb89e2d55fc22fb9b60b9f41f1d6e5663b3036 ``` - \ No newline at end of file + diff --git a/docs/endpoints/getGJRewards.md b/docs/endpoints/rewards/getGJRewards.md similarity index 67% rename from docs/endpoints/getGJRewards.md rename to docs/endpoints/rewards/getGJRewards.md index 528c20f5a..4792115be 100644 --- a/docs/endpoints/getGJRewards.md +++ b/docs/endpoints/rewards/getGJRewards.md @@ -6,7 +6,7 @@ Gets the rewards from the chests. ### Required Parameters -**udid** - A unique identifier for the user's device, can be replaced with anything +**udid** - A unique identifier for the user's device. You can put anything here **secret** - Wmfd2893gb7 @@ -14,19 +14,19 @@ Gets the rewards from the chests. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 **accountID** - Account ID of the user -**gjp** - The user's [GJP](/topics/encryption/gjp.md) +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) **uuid** - Seemingly a random number also used for identifying someone -**rewardType** - 0 for small chest, 1 for large chest. Defaults to 0 if left out +**rewardType** - 0 for getting info about the chests, 1 for small chest, 2 for large chest. Defaults to 0 if left out **r1** - A random 3-5 digit number @@ -44,14 +44,15 @@ A list of attributes of the Rewards, separated by colons `:` as follows: - Small chest's rewards in a comma separated list - Orbs - Diamonds - - Shards (0 is Fire, 1 is Ice, 2 is Poison, 3 is Shadow, and 4 is Lava) - - Key (If it's not 0, give a key) + - Item 1 (1 is Fire, 2 is Ice, 3 is Poison, 4 is Shadow, 5 is Lava, 6 is Demon Key, 10 is Earth, 11 is Blood, 12 is Metal, 13 is Light and 14 is Soul) + - Item 2 +- Amount of small chests claimed - Large chest time remaining - Large chest's rewards in a comma separated list -- Maybe the amount of rewards claimed? +- Amount of large chests claimed - `rewardType` -This list is then [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded. Then it is separated with its hash by a pipe `|`. It also has a random string of 5 characters appended to the front. +This list is then [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded. Then it is separated with its [hash](/resources/server/hashes.md?id=getgjrewards) by a pipe `|`. It also has a random string of 5 characters appended to the front. ## Example @@ -73,11 +74,15 @@ data = { req = requests.post("http://boomlings.com/database/getGJRewards.php", data=data) print(req.text) + +decoded_text = xor_cipher(base64.urlsafe_b64decode(response_text.split("|")[0][5:].encode()).decode(), '59182') +print(decoded_text) ``` **Response** ```py DA96FcVVmQnEPCggLBwMOAwIGDAAGDgsPcBFbU1sZQU1GFXtiGFpQS1QCAwIKCQsDDwsHDAQPDQEUAxkJHQgIBA4DDAgCDQcMAg8LAQgeAxUFFAIPDAILCAQ=|3f5f0ad92a601380e7eea113c223be94ff75304d +DlWzC:3935672:499769:I can put BS here:173831:2646:40,1,0,0:1724:74640:200,6,4,0:533:1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/rewards/getGJSecretReward.md b/docs/endpoints/rewards/getGJSecretReward.md new file mode 100644 index 000000000..f5cedc286 --- /dev/null +++ b/docs/endpoints/rewards/getGJSecretReward.md @@ -0,0 +1,155 @@ +# getGJRewards.php + +Gets the rewards from the wraith vault depending on the entered code, which can be found in the tower select level screen at the bottom right. + +## Parameters + +### Required Parameters + +**udid** - A unique identifier for the user's device. You can put anything here + +**secret** - Wmfd2893gb7 + +**chk** - 5 random chars appended to the beginning of a random number [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 42 + +**gdw** - 0 + +**accountID** - Account ID of the user + +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) + +**uuid** - Seemingly a random number also used for identifying someone + +## Response + +A list of attributes of the Rewards, separated by colons `:` as follows: +- A random string of 5 characters +- The number used to verify the `chk` +- The rewardID +- The chest type (1 small, 2 large) +- comma separated chest reward in the format `{itemid},{total1}{itemid2}{total2}...` + + +Where each itemID is as follows: +| itemID | Type | +|--------|-------------| +| 1 | Fire Shard | +| 2 | Ice Shard | +| 3 | Poison Shard | +| 4 | Shadow Shard | +| 5 | Lava Shard | +| 6 | Demon Key | +| 7 | Orbs | +| 8 | Diamonds | +| 10 | Earth Shard | +| 11 | Blood Shard | +| 12 | Metal Shard | +| 13 | Light Shard | +| 14 | Soul Shard | +| 15 | Gold Key | + +If the itemID is greater than 1000, it seems to be treated as a special reward (unlock type), where + +| itemID - 1000 | Unlock Type | +|---------------|-------------| +| 1 | Cube | +| 2 | Col1 | +| 3 | Col2 | +| 4 | Ship | +| 5 | Ball | +| 6 | Bird | +| 7 | Dart | +| 8 | Robot | +| 9 | Spider | +| 10 | Streak | +| 11 | Death | +| 12 | GJItem | +| 13 | Swing | +| 14 | Jetpack | +| 15 | ShipFire | + + + +This list is then [XOR](/topics/encryption/xor.md)'d and [URL-Safe Base64](/topics/encryption/base64.md) encoded. Then it is separated with its [hash](/resources/server/hashes.md?id=getgjrewards) by a pipe `|`. It also has a random string of 5 characters appended to the front. + +## Example + + + +### **Python** + +```py +import random +import requests +import base64 + + +def xor_cipher(text: str, key: str) -> str: + """ + XOR cipher function: Encrypts or decrypts a text using a key. + """ + return ''.join( + chr(ord(char) ^ ord(key[i % len(key)])) for i, char in enumerate(text) + ) + + +def generate_chk() -> str: + """ + Generates the 'chk' parameter by combining a random string and + an XOR-ciphered, base64-encoded random integer. + """ + random_string = ''.join(random.choice("1234567890qwertyuiopaqsdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") for _ in range(5)) + random_number = str(random.randint(10000, 1000000)) + xor_ciphered = xor_cipher(random_number, "59182") + encoded = base64.b64encode(xor_ciphered.encode()).decode() + return random_string + encoded + + +def decode_response(response_text: str) -> str: + """ + Decodes the response text from the server. + """ + try: + encoded_part = response_text.split("|")[0][5:] + decoded_part = base64.urlsafe_b64decode(encoded_part.encode()).decode() + return xor_cipher(decoded_part, "59182") + except Exception: + return response_text + + +url = "https://www.boomlings.com/database/getGJSecretReward.php" +headers = {"User-Agent": ""} +data = { + "accountID": "1688850", #iAndyHD accountID + "binaryVersion": "42", + "gameVersion": "22", + "gjp2": "***", #iAndyHD gjp2 + "secret": "Wmfd2893gb7", + "udid": "ffffffff-88c5-aa6d-ffff-ffffcd72151b", + "uuid": "4460760", + "rewardKey": "backstreetboy", + "chk": generate_chk(), +} +# Send POST request +response = requests.post(url, headers=headers, data=data).text +# Decode and print response +print(f"Response: {response}") +decoded_text = decode_response(response) +print(f"Decoded: {decoded_text}") +``` + +**Response** + + +```plain +Response: bHR3IU2oCVGoPDQgAAgEKCwsDDwsLCQIFAR0LBQ==|4adffcb7db1fdad7a665a96fa68da46157cbed78 +Decoded: fS3lX:498043:31:2:1008,37 +``` + + diff --git a/docs/endpoints/socials/acceptGJFriendRequest20.md b/docs/endpoints/socials/acceptGJFriendRequest20.md new file mode 100644 index 000000000..f080a0423 --- /dev/null +++ b/docs/endpoints/socials/acceptGJFriendRequest20.md @@ -0,0 +1,30 @@ +# Accepting Friend Requests + +> This endpoint is used to accept friend requests recieved from other users + +## Parameters + +| Parameter | Explanation | Optional | +| :---------------- | :----------------------------------------------------------------- | -------- | +| `gameVersion` | The Game Version the player is playing on. 22 for 2.2 | `True` | +| `binaryVersion` | The current build the player playing on. 42 for 2.206 | `True` | +| `gdw` | If the player is using Geometry Dash World | `True` | +| `requestID` | The ID of the friend request being accepted | `True` | +| `accountID` | The player's account ID | `False` | +| `targetAccountID` | The account which the player is trying to send a friend request to | `False` | +| `gj2p` | The player's [GJP2](/topics/encryption/gjp.md) | `False` | +| `secret` | The common secret: `Wmfd2893gb7` | `False` | + +## Response + +**Successful Request** + +```py +1 +``` + +**Failed Request** + +```py +-1 +``` diff --git a/docs/endpoints/blockGJUser20.md b/docs/endpoints/socials/blockGJUser20.md similarity index 86% rename from docs/endpoints/blockGJUser20.md rename to docs/endpoints/socials/blockGJUser20.md index 6ae93f424..53431fc8e 100644 --- a/docs/endpoints/blockGJUser20.md +++ b/docs/endpoints/socials/blockGJUser20.md @@ -8,7 +8,7 @@ Blocks a user. **accountID** - The blocking person's account ID -**gjp** - The blocking person's [GJP](/topics/encryption/gjp.md) +**gjp2** - The blocking person's [GJP2](/topics/encryption/gjp.md) **targetAccountID** - The account ID of the person being blocked @@ -16,9 +16,9 @@ Blocks a user. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -53,4 +53,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/socials/deleteGJFriendRequests20.md b/docs/endpoints/socials/deleteGJFriendRequests20.md new file mode 100644 index 000000000..f69c985bf --- /dev/null +++ b/docs/endpoints/socials/deleteGJFriendRequests20.md @@ -0,0 +1,32 @@ +# DeleteFriendRequests + +> This endpoint is used to delete friend requests that the player may have recieved from other players + +## Parameters + +| Parameter | Explanation | Optional | +| :---------------- | :---------------------------------------------------------------------------------- | -------- | +| `gameVersion` | The Game Version the player is playing on. 22 for 2.2 | `True` | +| `binaryVersion` | The current build the player playing on. 42 for 2.206 | `True` | +| `gdw` | If the player is using Geometry Dash World | `True` | +| `accountID` | The player's account ID | `False` | +| `gjp2` | The player's [GJP2](/topics/encryption/gjp.md) | `False` | +| `targetAccountID` | The accountID of the player you are declining the friend request | `False` | +| `accounts` | Account IDs seperated by `,` for users the player is declining friend requests from | `True` | +| `isSender` | If the player is the sender of this friend request | `True` | + +**Note:** `isSender` must be set to `1` if you are deleting a request you have sent + +## Response + +**Successful Request** + +```py +1 +``` + +**Failed Request** + +```py +-1 +``` diff --git a/docs/endpoints/deleteGJMessages20.md b/docs/endpoints/socials/deleteGJMessages20.md similarity index 87% rename from docs/endpoints/deleteGJMessages20.md rename to docs/endpoints/socials/deleteGJMessages20.md index 5a37fff0e..6e510fad8 100644 --- a/docs/endpoints/deleteGJMessages20.md +++ b/docs/endpoints/socials/deleteGJMessages20.md @@ -8,7 +8,7 @@ Deletes a message between two users. **accountID** - The account ID of the user who is deleting the message -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user who is deleting the message +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user who is deleting the message **messageID** - ID of the message being deleted @@ -18,9 +18,9 @@ Deletes a message between two users. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -58,4 +58,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/downloadGJMessage20.md b/docs/endpoints/socials/downloadGJMessage20.md similarity index 90% rename from docs/endpoints/downloadGJMessage20.md rename to docs/endpoints/socials/downloadGJMessage20.md index c90b35b31..4838b3c30 100644 --- a/docs/endpoints/downloadGJMessage20.md +++ b/docs/endpoints/socials/downloadGJMessage20.md @@ -8,7 +8,7 @@ Download a message. **accountID** - The person's account ID -**gjp** - The blocking person's [GJP](/topics/encryption/gjp.md) +**gjp2** - The person's [GJP2](/topics/encryption/gjp.md) **messageID** - The ID of the message to read @@ -16,9 +16,9 @@ Download a message. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 diff --git a/docs/endpoints/socials/getGJFriendRequests20.md b/docs/endpoints/socials/getGJFriendRequests20.md new file mode 100644 index 000000000..5a2e12c76 --- /dev/null +++ b/docs/endpoints/socials/getGJFriendRequests20.md @@ -0,0 +1,35 @@ +# GetFriendRequests + +> This endpoint is to fetch friend requests recieved by other users + +## Parameters + +| Parameter | Explanation | Optional | +| :-------------- | :--------------------------------------------------------- | -------- | +| `gameVersion` | The Game Version the player is playing on. 22 for 2.2 | `True` | +| `binaryVersion` | The current build the player playing on. 42 for 2.206 | `True` | +| `gdw` | If the player is using Geometry Dash World | `True` | +| `page` | The page currently being viewed | `True` | +| `total` | The total friend requests recieved | `True` | +| `getSent` | If the server should fetch requests sent by the player | `True` | +| `accountID` | The player's account ID | `False` | +| `gjp2` | The player's [GJP2](/topics/encryption/gjp.md) | `False` | +| `secret` | The common secret: `Wmfd2893gb7` | `False` | + +## Response + +The response for this endpoint is a `key:value` format with `|` being the seperator for each object. a `#` is used to seperate the metadata + +> the metadata is split up into 3 segments: `total requests: page number : total requests per page` + +**Below is a raw response for a single friend request** + +``` +1:TheWylieMaster:2:84696119:9:31:10:4:11:16:14:0:15:2:16:9276649:32:45404710:35:aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ==:41::37:2024 years#1:0:1 +``` + +if the request fails then the server will return + +```py +-1 +``` diff --git a/docs/endpoints/getGJMessages20.md b/docs/endpoints/socials/getGJMessages20.md similarity index 96% rename from docs/endpoints/getGJMessages20.md rename to docs/endpoints/socials/getGJMessages20.md index ce1ca24f1..9670be9be 100644 --- a/docs/endpoints/getGJMessages20.md +++ b/docs/endpoints/socials/getGJMessages20.md @@ -8,15 +8,15 @@ Gets a user's DMs. **accountID** - The user's account ID -**gjp** - The user's [GJP](/topics/encryption/gjp.md) +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) **secret** - Wmfd2893gb7 ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -43,7 +43,7 @@ import requests data = { "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption + "gjp2": "********", # This would be DevExit's password encoded with GJP2 encryption "secret": "Wmfd2893gb7" } @@ -56,4 +56,4 @@ print(req.text) 6:PasswordFinders:3:17787971:2:5317656:1:52029708:4:Z2FnZw==:8:1:9:0:7:8 minutes|6:denisukrrus:3:111481197:2:11283964:1:50272016:4:UmU6IHdoeQ==:8:1:9:0:7:1 month|6:denisukrrus:3:111481197:2:11283964:1:50246431:4:UmU6IHdoeQ==:8:1:9:0:7:2 months|6:denisukrrus:3:111481197:2:11283964:1:50164382:4:d2h5:8:1:9:0:7:2 months|6:denisukrrus:3:111481197:2:11283964:1:50080479:4:UmU6IGhlcmUgc29tZSB0ZXh0:8:1:9:0:7:2 months|6:denisukrrus:3:111481197:2:11283964:1:49776472:4:aGVyZSBzb21lIHRleHQ=:8:1:9:0:7:2 months|6:denisukrrus:3:111481197:2:11283964:1:49374506:4:c29tZSB0ZXh0:8:1:9:0:7:3 months|6:GDBotAI:3:118270198:2:11676872:1:48319722:4:UmU6IHRlc3Q=:8:1:9:0:7:4 months|6:GDBotAI:3:118270198:2:11676872:1:48319704:4:UmU6IHc=:8:1:9:0:7:4 months|6:GDBotAI:3:118270198:2:11676872:1:48319544:4:UmU6IGRqZGpqZWpldWR1cg==:8:1:9:0:7:4 months|6:poopybobby:3:14711367:2:4884369:1:47666534:4:bG1hbw==:8:1:9:0:7:6 months|6:poopybobby:3:14711367:2:4884369:1:40030444:4:UmU6IFBsZWFzZSBJIGFtIGJpZyBmYW4=:8:1:9:0:7:1 year|6:mbed:3:7381956:2:1403996:1:37193331:4:ZWF0IG1l:8:1:9:0:7:1 year|6:Thatdograscal:3:5785477:2:438738:1:36479601:4:ZQ==:8:1:9:0:7:2 years|6:Destrom:3:53436248:2:7943837:1:33937410:4:SGV5:8:1:9:0:7:2 years|6:Jmoney2638:3:43797799:2:7170197:1:33310194:4:VHJhZGU=:8:1:9:0:7:2 years|6:xxgaruxx:3:42742570:2:7101156:1:31781647:4:aGVsbG8=:8:1:9:0:7:2 years|6:Thekilou13:3:4123328:2:2261538:1:31729107:4:UmU6IEkgbmVlZCBoZWxwLg==:8:1:9:0:7:2 years|6:Thekilou13:3:4123328:2:2261538:1:31718964:4:SSBuZWVkIGhlbHAu:8:1:9:0:7:2 years|6:SpuffyGD:3:5224502:2:1562647:1:31667629:4:cmVzcG9uZA==:8:1:9:0:7:2 years|6:ItsAdvyStlyes:3:13149198:2:3432452:1:31241029:4:SGV5:8:1:9:0:7:2 years|6:kittenspit:3:8254313:2:5249664:1:30287396:4:UmU6IGJhbm5lZCA=:8:1:9:0:7:2 years|6:Krysolite:3:42833795:2:7400547:1:28621608:4:SG9sYS4uLi4uLi4gPSk=:8:1:9:0:7:2 years|6:deadk3v:3:44065476:2:7249929:1:28599619:4:bXkgZnJpZW5k:8:1:9:0:7:2 years|6:BramYT:3:5806701:2:450273:1:27957196:4:TmVjcm9wb2xpWA==:8:1:9:0:7:2 years|6:WhiteRecycling:3:10200743:2:1623259:1:26307526:4:UmU6IExvdyBEZXRhaWwgTW9kZSBHbGl0Y2g_:8:1:9:0:7:3 years|6:AnonIIExt:3:1869127:2:885411:1:25162374:4:UmU6IEknbSBjb25mdXNlZC4uLg==:8:1:9:0:7:3 years|6:Rahmun1122:3:42600801:2:7164807:1:25010114:4:b21mZw==:8:1:9:0:7:3 years|6:Rahmun1122:3:42600801:2:7164807:1:25000287:4:UmU6IExpa2UgQm90:8:1:9:0:7:3 years|6:Rahmun1122:3:42600801:2:7164807:1:25000254:4:UmU6IExpa2UgQm90:8:1:9:0:7:3 years|6:TruKaveKiller:3:11066105:2:2152020:1:23254724:4:UmU6IEhlIGRpZG4ndCBoYWNr:8:1:9:0:7:3 years|6:CreatorBluey:3:6235047:2:3611307:1:21180833:4:UmU6IHd1dA==:8:1:9:0:7:3 years|6:CreatorBluey:3:6235047:2:3611307:1:21144322:4:UmU6IHd1dA==:8:1:9:0:7:3 years|6:GB RubRub Lover:3:2422340:2:2320:1:19707464:4:VGhlIGxldmVsIFVkZSBtYWRl:8:1:9:0:7:3 years|6:Infernos666:3:19144134:2:5570820:1:19035973:4:ag==:8:1:9:0:7:3 years|6:gravefruit:3:20099347:2:5827112:1:18622677:4:UmU6IEZ1Y2tpbmcgSGFja2Vy:8:1:9:0:7:3 years|6:mannewil:3:7866980:2:1088921:1:18433123:4:Z2F5:8:1:9:0:7:3 years|6:ICN:3:30727064:2:6370227:1:17198774:4:aGk=:8:1:9:0:7:3 years|6:NytromityGames:3:18307996:2:5742004:1:16667453:4:UmU6IEZpbm5nZXJiYW5nIGJldGE=:8:1:9:0:7:3 years|6:Acidscarecrow:3:18696064:2:5463621:1:16473096:4:UmU6IGhhY2tlcg==:8:1:9:0:7:3 years|6:NytromityGames:3:18307996:2:5742004:1:16159044:4:UmU6IEZpbm5nZXJiYW5nIGJldGE=:8:1:9:0:7:3 years|6:Acidscarecrow:3:18696064:2:5463621:1:16033619:4:aGFja2Vy:8:1:9:0:7:3 years|6:anisk:3:3996991:2:90613:1:15924087:4:UmU6IFdoeS4uLg==:8:1:9:0:7:3 years|6:TadokiariGD:3:12275930:2:2953169:1:14435552:4:UmU6IEdyYWNpYXM=:8:1:9:0:7:3 years|6:GThom:3:18259928:2:5961642:1:14277244:4:UmU6IERvbid0:8:1:9:0:7:3 years|6:TadokiariGD:3:12275930:2:2953169:1:13447638:4:QW1pZ29z:8:1:9:0:7:3 years|6:TadokiariGD:3:12275930:2:2953169:1:13233338:4:bWllcmRhIGRlIG5pdmVs:8:1:9:0:7:3 years|6:kittenspit:3:8254313:2:5249664:1:13228700:4:cm9idHJvbGwuLi4=:8:1:9:0:7:3 years|6:kittenspit:3:8254313:2:5249664:1:13203838:4:YmFubmVkIA==:8:1:9:0:7:3 years|6:StarFeGD:3:9790502:2:1537139:1:13105832:4:Yg==:8:1:9:0:7:3 years#74:0:50 ``` - \ No newline at end of file + diff --git a/docs/endpoints/getGJUserList20.md b/docs/endpoints/socials/getGJUserList20.md similarity index 96% rename from docs/endpoints/getGJUserList20.md rename to docs/endpoints/socials/getGJUserList20.md index 25744e97b..fd07f5208 100644 --- a/docs/endpoints/getGJUserList20.md +++ b/docs/endpoints/socials/getGJUserList20.md @@ -8,15 +8,15 @@ Gets either your friend list or your blocked list **accountID** - The account ID of the user you want to get the friends/blocklist of -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user you want to get the friends/blocklist of +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user you want to get the friends/blocklist of **secret** - Wmfd2893gb7 ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -37,7 +37,7 @@ import requests data = { "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption + "gjp2": "********", # This would be DevExit's password encoded with GJP2 encryption "secret": "Wmfd2893gb7" } @@ -50,4 +50,4 @@ print(req.text) 1:absolllute:2:18411307:9:11:10:15:11:15:14:0:15:2:16:5391343:18:2:41:|1:AdriRoPi:2:9502327:9:127:10:17:11:29:14:0:15:2:16:1636042:18:0:41:|1:AmorAltra:2:7006921:9:12:10:11:11:4:14:0:15:2:16:1046810:18:0:41:|1:anisk:2:3996991:9:10:10:18:11:37:14:5:15:0:16:90613:18:1:41:|1:AUG0D:2:14975931:9:51:10:33:11:12:14:1:15:0:16:6278067:18:1:41:|1:augii:2:19983031:9:30:10:18:11:11:14:0:15:2:16:5782085:18:0:41:|1:Auradium:2:127356083:9:35:10:1:11:3:14:0:15:2:16:13337396:18:0:41:|1:biapuffy:2:89994953:9:21:10:8:11:40:14:0:15:2:16:12042014:18:0:41:|1:BitZaruU:2:3648180:9:1:10:0:11:3:14:5:15:0:16:2750303:18:0:41:|1:BramYT:2:5806701:9:43:10:21:11:0:14:0:15:2:16:450273:18:0:41:|1:BugBoy:2:6655666:9:15:10:18:11:10:14:6:15:0:16:932726:18:1:41:|1:ChatNoir5:2:17160067:9:22:10:15:11:1:14:1:15:0:16:5769039:18:0:41:|1:ChiChirik:2:2946652:9:46:10:3:11:7:14:0:15:2:16:44549:18:0:41:|1:Cinny:2:99010452:9:23:10:12:11:12:14:1:15:2:16:10605101:18:0:41:|1:colebowl:2:3303760:9:1:10:0:11:3:14:0:15:0:16:22704:18:1:41:|1:COLON:2:4136576:9:60:10:18:11:10:14:0:15:2:16:106255:18:0:41:|1:CR Creebay :2:4136203:9:97:10:12:11:3:14:0:15:2:16:187918:18:1:41:|1:Creator cd:2:4208553:9:35:10:15:11:12:14:0:15:0:16:1827120:18:1:41:|1:CreatorForce:2:11798163:9:37:10:4:11:16:14:0:15:2:16:2563970:18:0:41:|1:CreatorPig:2:6309884:9:8:10:22:11:34:14:6:15:2:16:931102:18:0:41:|1:DarkEmper0r:2:667356:9:20:10:12:11:12:14:3:15:0:16:2661:18:1:41:|1:deadk3v:2:44065476:9:38:10:17:11:3:14:1:15:2:16:7249929:18:0:41:|1:denisukrrus:2:111481197:9:28:10:18:11:12:14:1:15:0:16:11283964:18:0:41:|1:Dragonfire178:2:3788247:9:15:10:15:11:11:14:1:15:2:16:1279201:18:0:41:|1:DrallumGC:2:4494564:9:10:10:22:11:9:14:6:15:2:16:295655:18:0:41:|1:Dyad:2:80155356:9:111:10:3:11:0:14:0:15:0:16:8777883:18:1:41:|1:EliteSG:2:20959785:9:96:10:17:11:12:14:0:15:2:16:6261100:18:0:41:|1:endevvor:2:87464728:9:120:10:4:11:21:14:0:15:0:16:9631328:18:0:41:|1:ErenGD:2:43587008:9:11:10:8:11:40:14:1:15:2:16:7235260:18:1:41:|1:foundmyball:2:15976456:9:24:10:11:11:16:14:5:15:2:16:5056324:18:0:41:|1:Fus1oN:2:18900874:9:98:10:0:11:3:14:0:15:0:16:5508218:18:0:41:|1:GatoFresa:2:6323741:9:1:10:7:11:8:14:6:15:0:16:1245627:18:1:41:|1:GDDD:2:5548469:9:119:10:40:11:41:14:0:15:2:16:4958525:18:0:41:|1:GeDeXy:2:4755134:9:30:10:12:11:5:14:0:15:0:16:3420678:18:0:41:|1:ggeoo:2:118630583:9:102:10:7:11:3:14:0:15:0:16:11725961:18:1:41:|1:Gorshh:2:7628148:9:9:10:25:11:3:14:5:15:2:16:1803304:18:0:41:|1:Griffin28:2:13924338:9:30:10:18:11:3:14:4:15:0:16:3911716:18:1:41:|1:Hackram:2:3314056:9:5:10:18:11:25:14:5:15:0:16:359647:18:0:41:|1:hdmeltdown:2:16436096:9:13:10:18:11:3:14:5:15:2:16:5082253:18:0:41:|1:hola1234:2:17945025:9:1:10:1:11:3:14:0:15:0:16:5320224:18:2:41:|1:Hunnidew:2:3504822:9:60:10:17:11:12:14:0:15:2:16:122675:18:1:41:|1:ICN:2:30727064:9:18:10:37:11:12:14:2:15:2:16:6370227:18:0:41:|1:iIAdezioIi:2:5544596:9:1:10:0:11:3:14:0:15:0:16:354077:18:0:41:|1:IiILevelsIiI:2:11893766:9:21:10:11:11:3:14:2:15:2:16:2690209:18:0:41:|1:IiISwiftIiI:2:4240755:9:15:10:39:11:22:14:6:15:0:16:372921:18:0:41:|1:ItsJustAnotherP:2:21306452:9:98:10:29:11:2:14:0:15:2:16:6088967:18:0:41:|1:ItsNutty:2:29688055:9:21:10:17:11:3:14:5:15:2:16:6314603:18:0:41:|1:JapanChinaHax:2:37589794:9:1:10:0:11:3:14:0:15:0:16:6775461:18:1:41:|1:JaxmanDaBoss:2:11517233:9:25:10:15:11:2:14:1:15:2:16:2879159:18:1:41:|1:jmsaints12:2:4986011:9:17:10:13:11:11:14:1:15:2:16:818108:18:1:41:|1:Juanger YT:2:14711732:9:128:10:0:11:1:14:0:15:0:16:4282353:18:0:41:|1:KittyKit:2:38493936:9:98:10:14:11:12:14:0:15:0:16:6906586:18:1:41:|1:KrazyGFX:2:12182416:9:2:10:12:11:15:14:6:15:0:16:2971696:18:0:41:|1:Krysolite:2:42833795:9:4:10:40:11:37:14:5:15:2:16:7400547:18:0:41:|1:LegeNdiuM:2:3820413:9:128:10:36:11:41:14:0:15:2:16:5013339:18:0:41:|1:Luckyyy:2:11809502:9:3:10:12:11:12:14:4:15:2:16:2674059:18:0:41:|1:mbed:2:7381956:9:110:10:3:11:12:14:0:15:2:16:1403996:18:0:41:|1:moisescrack20:2:5881933:9:16:10:0:11:3:14:0:15:0:16:2707555:18:1:41:|1:mrcreeperington:2:6084355:9:119:10:37:11:40:14:0:15:2:16:603371:18:0:41:|1:Mzt3rz:2:3381641:9:38:10:17:11:12:14:2:15:0:16:610833:18:0:41:|1:N3LY:2:52850644:9:18:10:10:11:3:14:3:15:2:16:11857536:18:0:41:|1:NeKitDS:2:17876467:9:133:10:6:11:3:14:0:15:2:16:5509312:18:0:41:|1:Nighthawk8:2:5332789:9:25:10:10:11:26:14:0:15:0:16:1683204:18:1:41:|1:NioHcreator:2:78104709:9:1:10:18:11:12:14:2:15:2:16:9379353:18:0:41:|1:Nyvanish:2:9274883:9:8:10:1:11:12:14:6:15:2:16:1422516:18:1:41:|1:PasswordFinders:2:17787971:9:63:10:0:11:3:14:0:15:0:16:5317656:18:2:41:|1:pbbbfe:2:13870008:9:129:10:18:11:3:14:0:15:0:16:3794318:18:1:41:|1:Plaxen:2:8649263:9:1:10:0:11:3:14:0:15:0:16:1243033:18:0:41:|1:PlexyGlass:2:98636479:9:133:10:4:11:12:14:0:15:2:16:10561660:18:1:41:|1:Pokima:2:122214721:9:33:10:12:11:41:14:1:15:0:16:12043291:18:0:41:|1:poopybobby:2:14711367:9:18:10:12:11:5:14:3:15:0:16:4884369:18:0:41:|1:PowerGW:2:9920667:9:92:10:21:11:15:14:0:15:2:16:1558318:18:0:41:|1:rexzyy:2:37023496:9:10:10:15:11:11:14:2:15:0:16:6744103:18:0:41:|1:RiGtZ:2:11392286:9:50:10:40:11:3:14:0:15:0:16:2721325:18:0:41:|1:Rippin17:2:7969254:9:47:10:0:11:3:14:0:15:0:16:1114978:18:0:41:|1:ShadowFlare:2:10175886:9:98:10:18:11:12:14:0:15:0:16:1634186:18:0:41:|1:skullforever:2:5471285:9:1:10:2:11:3:14:0:15:0:16:383010:18:1:41:|1:SniDer:2:15142239:9:9:10:12:11:21:14:6:15:0:16:5097653:18:0:41:|1:SpuffyGD:2:5224502:9:32:10:15:11:1:14:0:15:0:16:1562647:18:0:41:|1:Steven0604:2:13104437:9:28:10:9:11:12:14:0:15:0:16:121568:18:0:41:|1:Thaizik:2:5250445:9:126:10:17:11:14:14:0:15:2:16:1667608:18:0:41:|1:Thatdograscal:2:5785477:9:3:10:15:11:5:14:6:15:0:16:438738:18:0:41:|1:thedash8282:2:50843361:9:36:10:41:11:12:14:0:15:2:16:7761927:18:0:41:|1:TheManMadeGoril:2:8697851:9:1:10:0:11:3:14:0:15:0:16:1288840:18:0:41:|1:TheRealRoman:2:13141389:9:1:10:0:11:3:14:0:15:0:16:3459603:18:1:41:|1:TheRealZero:2:5446355:9:47:10:25:11:25:14:0:15:2:16:324047:18:0:41:|1:TheS1:2:1813006:9:22:10:20:11:12:14:0:15:0:16:2426734:18:0:41:|1:ThroShade:2:6918339:9:37:10:18:11:12:14:0:15:2:16:988999:18:0:41:|1:Toxjc47:2:7628745:9:29:10:12:11:25:14:0:15:0:16:1054055:18:0:41:|1:TritheDoge:2:85831492:9:11:10:20:11:38:14:3:15:0:16:9399037:18:1:41:|1:TrueW33D:2:3354630:9:107:10:15:11:12:14:0:15:2:16:1029:18:0:41:|1:TurboDashGD:2:11262496:9:22:10:15:11:15:14:1:15:2:16:3319009:18:1:41:|1:TwilightLink:2:13979575:9:1:10:0:11:3:14:0:15:0:16:4739798:18:0:41:|1:ViuteX :2:20423414:9:18:10:3:11:13:14:0:15:0:16:5891721:18:1:41:|1:WhiteRecycling:2:10200743:9:84:10:12:11:12:14:0:15:0:16:1623259:18:0:41:|1:WitFaithGaming:2:7250600:9:5:10:25:11:3:14:5:15:2:16:1153639:18:0:41:|1:xxeriickgdxx:2:18833528:9:21:10:1:11:3:14:4:15:0:16:5496439:18:0:41:|1:XxmishiroxX:2:4885470:9:11:10:12:11:0:14:0:15:2:16:513390:18:0:41:|1:XxsaulxX902:2:10842216:9:6:10:0:11:12:14:5:15:2:16:2138304:18:1:41:|1:YannickBakFiets:2:9786350:9:17:10:7:11:11:14:1:15:0:16:1536281:18:0:41:|1:Zadik:2:6082346:9:4:10:0:11:3:14:0:15:0:16:807053:18:0:41:|1:Zod1ac:2:5259869:9:35:10:6:11:3:14:2:15:2:16:2499318:18:0:41: ``` - \ No newline at end of file + diff --git a/docs/endpoints/socials/readGJFriendRequest20.md b/docs/endpoints/socials/readGJFriendRequest20.md new file mode 100644 index 000000000..fd3aa437f --- /dev/null +++ b/docs/endpoints/socials/readGJFriendRequest20.md @@ -0,0 +1,29 @@ +# ReadFriendRequests + +> This endpoint is used to read friend requests recieved from other players + +## Parameters + +| Parameter | Explanation | Optional | +| :-------------- | :--------------------------------------------------------- | -------- | +| `gameVersion` | The Game Version the player is playing on. 22 for 2.2 | `True` | +| `binaryVersion` | The current build the player is playing on. 42 for 2.206 | `True` | +| `gdw` | If the player is using Geometry Dash World | `True` | +| `accountID` | The player's account ID | `False` | +| `gjp2` | The player's [GJP2](/topics/encryption/gjp.md) | `False` | +| `requestID` | The ID for the friend request recieved | `False` | +| `secret` | The common secret: `Wmfd2893gb7` | `False` | + +## Response + +**Successful Request** + +```py +1 +``` + +**Failed Request** + +```py +-1 +``` diff --git a/docs/endpoints/removeGJFriend20.md b/docs/endpoints/socials/removeGJFriend20.md similarity index 100% rename from docs/endpoints/removeGJFriend20.md rename to docs/endpoints/socials/removeGJFriend20.md diff --git a/docs/endpoints/unblockGJUser20.md b/docs/endpoints/socials/unblockGJUser20.md similarity index 86% rename from docs/endpoints/unblockGJUser20.md rename to docs/endpoints/socials/unblockGJUser20.md index b5b833647..f4cb8334e 100644 --- a/docs/endpoints/unblockGJUser20.md +++ b/docs/endpoints/socials/unblockGJUser20.md @@ -8,7 +8,7 @@ Unblocks a user. **accountID** - The unblocking person's account ID -**gjp** - The unblocking person's [GJP](/topics/encryption/gjp.md) +**gjp2** - The unblocking person's [GJP2](/topics/encryption/gjp.md) **targetAccountID** - The account ID of the person getting unblocked @@ -16,9 +16,9 @@ Unblocks a user. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -54,4 +54,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/socials/uploadFriendRequest20.md b/docs/endpoints/socials/uploadFriendRequest20.md new file mode 100644 index 000000000..b4983e3a4 --- /dev/null +++ b/docs/endpoints/socials/uploadFriendRequest20.md @@ -0,0 +1,29 @@ +# uploadFriendRequest20 + +> This endpont is used to send a friend request to other players + +## Parameters + +| Parameter | Explanation | Optional | +| :-------------- | :----------------------------------------------------------------- | -------- | +| `gameVersion` | The Game Version the player is playing on. 22 for 2.2 | `True` | +| `binaryVersion` | The current build the player playing on. 42 for 2.206 | `True` | +| `gdw` | If the player is using Geometry Dash World | `True` | +| `accountID` | The player's account ID | `False` | +| `toAccountID` | The account which the player is trying to send a friend request to | `False` | +| `gjp2` | The player's [GJP2](/topics/encryption/gjp.md) | `False` | +| `secret` | The common secret: `Wmfd2893gb7` | `False` | + +## Response + +**Successful Request** + +```py +1 +``` + +**Failed Request** + +```py +-1 +``` diff --git a/docs/endpoints/uploadGJMessage20.md b/docs/endpoints/socials/uploadGJMessage20.md similarity index 89% rename from docs/endpoints/uploadGJMessage20.md rename to docs/endpoints/socials/uploadGJMessage20.md index eb1687936..03cb21b70 100644 --- a/docs/endpoints/uploadGJMessage20.md +++ b/docs/endpoints/socials/uploadGJMessage20.md @@ -8,7 +8,7 @@ Sends a message to a user **accountID** - Account ID of the user sending the message -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user sending the message +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user sending the message **toAccountID** - Account ID of the user retrieving the message @@ -20,9 +20,9 @@ Sends a message to a user ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -60,4 +60,4 @@ print(req.text) 1 ``` - \ No newline at end of file + diff --git a/docs/endpoints/getGJSongInfo.md b/docs/endpoints/songs/getGJSongInfo.md similarity index 88% rename from docs/endpoints/getGJSongInfo.md rename to docs/endpoints/songs/getGJSongInfo.md index bac249186..0d2465063 100644 --- a/docs/endpoints/getGJSongInfo.md +++ b/docs/endpoints/songs/getGJSongInfo.md @@ -12,7 +12,20 @@ Gets info about a newgrounds song. ### Optional Parameters -N/A +**accountID** + +**binaryVersion** - 45 + +**gameVersion** - 22 + +**gjp2** + +**secret** + +**udid** + +**uuid** + ## Response diff --git a/docs/endpoints/getGJTopArtists.md b/docs/endpoints/songs/getGJTopArtists.md similarity index 94% rename from docs/endpoints/getGJTopArtists.md rename to docs/endpoints/songs/getGJTopArtists.md index 87d5fa478..82be84556 100644 --- a/docs/endpoints/getGJTopArtists.md +++ b/docs/endpoints/songs/getGJTopArtists.md @@ -10,9 +10,9 @@ Gets RobTop's handpicked top artists. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -47,4 +47,4 @@ print(req.text) 4:Dimrain47|4:MadhouseDUDE|4:paperskies|4:CloudierMusic|4:Stratales|4:Lockyn|4:DanJohansen|4:BurgeraX|4:ParagonX9|4:Kolkian|4:LemKuuja|4:meganeko:7:UCP3M2myndqXuAEKKnqm_7SQ|4:teminiteofficial:7:UCc_bv_5nmxy2xnPNg9kP3Rg|4:Sharks:7:UCQJuQY3hF4YCHlAR42A5trg|4:F-777|4:DrayxMusic:7:UCSn0s6RSeePeJOiUi6Vdj6g|4:ForeverBound|4:DJ-Zyzyx|4:canonblade|4:Xtrullor:7:UCejLri1RVC7kj8ZVNX2a53g#73:20:20 ``` - \ No newline at end of file + diff --git a/docs/endpoints/songs/musiclibrary.md b/docs/endpoints/songs/musiclibrary.md new file mode 100644 index 000000000..df7ff60ed --- /dev/null +++ b/docs/endpoints/songs/musiclibrary.md @@ -0,0 +1,28 @@ +# musiclibrary.dat + +## Requests + +In Geometry Dash 2.2, the Music library has been added, adding thousands of songs that were not on Newgrounds to the game. + +In 2.206, NCS was added to the music library, which forced RobTop to overhaul the file format. + +The current library (as of 2.206) can be fetched via a GET request to `https://geometrydashfiles.b-cdn.net/music/musiclibrary_02.dat`. + +The old music library can be fetched via a GET request to `https://geometrydashfiles.b-cdn.net/music/musiclibrary.dat`. + +You can also fetch the latest version of the Music Library by sending a GET request to `https://geometrydashfiles.b-cdn.net/music/musiclibrary_version_02.txt` (for the old library, the link is `https://geometrydashfiles.b-cdn.net/music/musiclibrary_version.txt`. As of the writing, the latest version for the old library is `114` and this is unlikely to change in the future). + +The Music library format is covered [here](/resources/client/musiclibrary.md). + +## Downloading Songs + +You can download a track by sending a GET request to `https://geometrydashfiles.b-cdn.net/music/{id}.mp3`. All Music Library song IDs have an offset of 10,000,000 to not interfere with the Newgrounds songs. + +## CDN Tokens + +The GD client currently sends two parameters for every `https://geometrydashfiles.b-cdn.net` request: `token` and `expires`. While these aren't required as of the time of writing, they may be required in the future. Click [here](/topics/cdn_token.md) to find instructions +how to generate the CDN tokens. + +## Archive + +The music library and SFX library versions are auto-archived by Cvolton at https://cvolton.eu/sfx. diff --git a/docs/endpoints/songs/sfxlibrary.md b/docs/endpoints/songs/sfxlibrary.md new file mode 100644 index 000000000..74fa1c6bf --- /dev/null +++ b/docs/endpoints/songs/sfxlibrary.md @@ -0,0 +1,17 @@ +# sfxlibrary.dat + +## Requests + +In Geometry Dash 2.2, the Sound Effect library has been added. The library can be fetched via a GET request to `https://geometrydashfiles.b-cdn.net/sfx/sfxlibrary.dat`. + +You can also fetch the latest version of the SFX Library by sending a GET request to `https://geometrydashfiles.b-cdn.net/sfx/sfxlibrary_version.txt`. + +The SFX library format is covered [here](/resources/client/sfxlibrary.md). + +## Downloading SFX + +You can download a sound effect by sending a GET request to `https://geometrydashfiles.b-cdn.net/sfx/s{id}.ogg`. Note that there are quite a bit of unused SFX that you won't find in the library. + +## Archive + +The music library and SFX library versions are auto-archived by Cvolton at https://cvolton.eu/sfx. diff --git a/docs/endpoints/testSong.md b/docs/endpoints/songs/testSong.md similarity index 80% rename from docs/endpoints/testSong.md rename to docs/endpoints/songs/testSong.md index 48d6acf37..16f5e8756 100644 --- a/docs/endpoints/testSong.md +++ b/docs/endpoints/songs/testSong.md @@ -1,5 +1,7 @@ # testSong.php +> This endpoint no longer exists. This page is kept for historical reasons. + Gets whitelist/artist info about a song. **THIS IS A GET REQUEST.** ## Parameters @@ -23,7 +25,7 @@ import requests songID = 787311 -req = requests.post(f'http://boomlings.com/database/testSong.php?songID={songID}') +req = requests.get(f'http://boomlings.com/database/testSong.php?songID={songID}') print(req.text) ``` @@ -42,4 +44,4 @@ Song: Newbie - Space External API allowed. ``` - \ No newline at end of file + diff --git a/docs/endpoints/uploadFriendRequest20.md b/docs/endpoints/uploadFriendRequest20.md deleted file mode 100644 index 5bf79915c..000000000 --- a/docs/endpoints/uploadFriendRequest20.md +++ /dev/null @@ -1,57 +0,0 @@ -# uploadFriendRequest20.php - -Sends a friend request to a user - -## Parameters - -### Required Parameters - -**accountID** - Account ID of the user sending the friend request - -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user sending the friend request - -**toAccountID** - Account ID of the user receiving the friend request - -**secret** - Wmfd2893gb7 - -### Optional Parameters - -**gameVersion** - 21 - -**binaryVersion** - 35 - -**gdw** - 0 - -**comment** - The comment sent with the friend request encoded with [Base 64](/topics/encryption/base64), defaults to nothing if left out - -## Response - -1 if the user hasn't sent a friend request to that account ID already, regardless of whether it exists or not, but returns a 500 error code if the user has already sent a friend request to that account - -## Example - - - -### **Python** - -```py -import requests, base64 - -data = { - "accountID": 173831, # DevExit's account ID - "gjp": "********", # This would be DevExit's password encoded with GJP encryption - "toAccountID": 5317656, - "comment": base64.b64encode(b"Hello good sir!").decode(), - "secret": "Wmfd2893gb7", -} - -r = requests.post('http://boomlings.com/database/uploadFriendRequest20.php', data=data) -print(req.text) -``` - -**Response** -```py -1 -``` - - \ No newline at end of file diff --git a/docs/endpoints/getGJScores20.md b/docs/endpoints/users/getGJScores20.md similarity index 74% rename from docs/endpoints/getGJScores20.md rename to docs/endpoints/users/getGJScores20.md index 0f4171cb7..ce23eaa51 100644 --- a/docs/endpoints/getGJScores20.md +++ b/docs/endpoints/users/getGJScores20.md @@ -10,24 +10,34 @@ Gets the leaderboard scores. ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 **accountID** - The account ID of the user viewing the leaderboards -**gjp** - The user's [GJP](/topics/encryption/gjp.md) +**gjp2** - The user's [GJP2](/topics/encryption/gjp.md) -**type** - Can be `top`, `relative`, `friends`, or `creators`. If left out it defaults to `top` +**count** - Returns the amount of players specified. Limited to 100 -**count** - Returns the amount of players specified +**type** - Can be `top` (`count` defaults to 100), `relative` (`count` defaults to 15 if no `accountID` is provided, or the `accountID`'s profile doesn't have over 501 stars, even if those criteria are met, no matter the `count` value, it will always be 26.), `friends` (`count` defaults to 1 if `accountID` and `gjp2` arent't provided), or `creators`. If left out it defaults to `top` + +## Headers + +### Required Headers + +**User-Agent** = "" ## Response Returns a list of [user objects](/resources/server/user.md) in order based on which leaderboard you're viewing, separated by pipe `|` characters. +**Note:** Sometimes may return empty user objects, possibly because they don't meet star requirement. + +Returns -1 if the `secret` is incorrect or not provided, or if `type` is `friends` and `accountID` is provided, but not `gjp2`. + ## Example @@ -40,10 +50,14 @@ import requests data = { "secret": "Wmfd2893gb7", "type": "top", - "count": 20 + "count": 100 +} + +headers = { + "User-Agent": "" } -req = requests.post('http://boomlings.com/database/getGJScores20.php', data=data) +req = requests.post('http://boomlings.com/database/getGJScores20.php', data=data, headers=headers) print(req.text) ``` @@ -52,4 +66,4 @@ print(req.text) 1:xMiguel007:2:2866103:13:149:17:7219:6:1:9:37:10:35:11:3:14:0:15:2:16:70846:3:65710:8:0:46:12879:4:1073|1:shaggy23:2:1995959:13:149:17:4321:6:2:9:51:10:39:11:29:14:0:15:2:16:2888:3:65595:8:21:46:11847:4:1115|1:Michigun:2:703929:13:149:17:12312:6:3:9:22:10:15:11:12:14:0:15:2:16:34499:3:61161:8:16:46:14600:4:997|1:Cool Dash:2:1148292:13:149:17:7026:6:4:9:37:10:20:11:17:14:0:15:2:16:4825:3:52931:8:0:46:14630:4:641|1:Kaernk:2:1282100:13:149:17:11908:6:5:9:51:10:18:11:12:14:0:15:2:16:118843:3:51821:8:0:46:17073:4:533|1:DeathHogz:2:1396933:13:149:17:5250:6:6:9:57:10:37:11:12:14:0:15:2:16:104119:3:51791:8:2:46:11948:4:435|1:Franchet:2:9576358:13:149:17:9840:6:7:9:30:10:15:11:12:14:0:15:2:16:1999478:3:51504:8:0:46:18616:4:670|1:Leksitoo:2:933105:13:149:17:4125:6:8:9:29:10:5:11:12:14:0:15:2:16:205:3:50530:8:14:46:13852:4:666|1:Superchat:2:2945295:13:149:17:5150:6:9:9:98:10:12:11:17:14:0:15:0:16:1098021:3:45706:8:2:46:14101:4:1201|1:Darky84:2:8513170:13:149:17:5864:6:10:9:37:10:12:11:25:14:0:15:2:16:1244088:3:44313:8:0:46:7444:4:1031|1:IvanNyan:2:14999317:13:149:17:5693:6:11:9:35:10:11:11:23:14:1:15:2:16:5148877:3:43785:8:0:46:6:4:563|1:GK NK 98:2:4803050:13:147:17:6491:6:12:9:2:10:12:11:23:14:6:15:2:16:890741:3:43163:8:2:46:14155:4:586|1:BonnieABoss:2:18158058:13:149:17:5572:6:13:9:105:10:15:11:3:14:0:15:2:16:5810059:3:43099:8:0:46:4743:4:778|1:FixTop100:2:18119007:13:149:17:801:6:14:9:1:10:0:11:3:14:0:15:0:16:5316700:3:43099:8:0:46:3559:4:323|1:TheRealAir:2:9035779:13:149:17:1270:6:15:9:35:10:12:11:25:14:0:15:2:16:2382846:3:42096:8:0:46:12808:4:665|1:XShadowWizardX:2:10670782:13:149:17:6097:6:16:9:85:10:12:11:7:14:0:15:2:16:1919857:3:41209:8:6:46:14083:4:540|1:CleanTop100:2:6552455:13:149:17:7111:6:17:9:30:10:7:11:12:14:0:15:2:16:2835706:3:40810:8:0:46:13243:4:871|1:xSuwako:2:14287615:13:149:17:2778:6:18:9:96:10:37:11:12:14:0:15:2:16:3984642:3:40695:8:0:46:11827:4:878|1:Civitrex2:2:14674984:13:149:17:7418:6:19:9:37:10:12:11:13:14:0:15:2:16:4156730:3:40576:8:0:46:13981:4:922|1:AdrianDlaCruz:2:16219796:13:148:17:2689:6:20:9:103:10:12:11:40:14:0:15:2:16:4771465:3:40286:8:0:46:4489:4:982| ``` - \ No newline at end of file + diff --git a/docs/endpoints/users/getGJUserInfo20.md b/docs/endpoints/users/getGJUserInfo20.md new file mode 100644 index 000000000..6affafeea --- /dev/null +++ b/docs/endpoints/users/getGJUserInfo20.md @@ -0,0 +1,66 @@ +# getGJUserInfo20.php + +Gets info about a user + +## Parameters + +### Required Parameters + +**targetAccountID** - The account ID of the person you want the info of + +**secret** - Wmfd2893gb7 + +### Optional Parameters + +**gameVersion** - 22 + +**binaryVersion** - 35 + +**gdw** - 0 + +**accountID** - Your accountID + +**gjp2** - Your [GJP](/topics/encryption/gjp.md) + +## Response + +Returns a [user object](/resources/server/user.md) for the player you want + +## Example + + + +### **Python** + +```py +import requests + +url = "http://www.boomlings.com/database/getGJUserInfo20.php" +data = { + "secret": "Wmfd2893gb7", + "targetAccountID": "173831" +} +headers = { + "User-Agent": "" # Empty User-Agent +} + +response = requests.post(url, data=data, headers=headers) +print(response.text) + +``` + +### **curl** +```plain +curl -X POST http://www.boomlings.com/database/getGJUserInfo20.php -d "secret=Wmfd2893gb7&targetAccountID=173831" -A "" +``` + + + + + +**Response** +```py +1:meluwudy:2:3935672:13:148:17:1068:10:12:11:15:51:10:3:9990:52:320:46:21504:4:476:8:1:18:0:19:0:50:0:20:UCZoN2WLAooS6uhREa9Cgpwg:21:82:22:17:23:113:24:83:25:85:26:24:28:1:43:2:48:2:53:26:54:3:30:29855:16:173831:31:0:44:logout:45:devexit:49:0:55:143,67,56,112,56,1,1,2,1,0,21,14:56:144,157,134,300,190,88,59,67:57:4,7,7,18,16,5,0:29:1 +``` + + \ No newline at end of file diff --git a/docs/endpoints/getGJUsers20.md b/docs/endpoints/users/getGJUsers20.md similarity index 100% rename from docs/endpoints/getGJUsers20.md rename to docs/endpoints/users/getGJUsers20.md diff --git a/docs/endpoints/updateGJUserScore22.md b/docs/endpoints/users/updateGJUserScore22.md similarity index 60% rename from docs/endpoints/updateGJUserScore22.md rename to docs/endpoints/users/updateGJUserScore22.md index 4956f041c..3a5cfe7be 100644 --- a/docs/endpoints/updateGJUserScore22.md +++ b/docs/endpoints/users/updateGJUserScore22.md @@ -8,10 +8,12 @@ Updates a user's data **accountID** - Account ID of the user updating their profile -**gjp** - The [GJP](/topics/encryption/gjp.md) of the user updating their profile +**gjp2** - The [GJP2](/topics/encryption/gjp.md) of the user updating their profile **stars** - The amount of stars the user has +**moons** - The amount of moons the user has + **demons** - The amount of demons the user has **diamonds** - The amount of diamonds the user has @@ -26,31 +28,47 @@ Updates a user's data **accIcon** - The ID of the user's selected icon. -**accShip** - The ID of the user's selected icon. +**accShip** - The ID of the user's selected ship. + +**accBall** - The ID of the user's selected ball. + +**accBird** - The ID of the user's selected UFO. -**accBall** - The ID of the user's selected icon. +**accDart** - The ID of the user's selected wave. -**accBird** - The ID of the user's selected icon. +**accRobot** - The ID of the user's selected robot. -**accDart** - The ID of the user's selected icon. +**accGlow** - Whether the user has glow enabled. -**accRobot** - The ID of the user's selected icon. +**accSpider** - The ID of the user's selected spider. -**accGlow** - The ID of the user's selected icon. +**accExplosion** - The ID of the user's selected death effect. -**accSpider** - The ID of the user's selected icon. +**accSwing** - The ID of the user's selected swing. -**accExplosion** - The ID of the user's selected icon. +**accJetpack** - The ID of the user's selected jetpack. **seed2** - [See here](/topics/encryption/chk?id=user-profile) +**dinfo** - List of all completed demons (level IDs separated by `,`) + +**dinfow** - Amount of completed weeklies + +**dinfog** - Amount of completed gauntlet demons + +**sinfo** - `{autoClassic},{easyClassic},{normalClassic},{hardClassic},{harderClassic},{insaneClassic},{autoPlatformer},{easyPlatformer},{normalPlatformer},{hardPlatformer},{harderPlatformer},{insanePlatformer}` + +**sinfod** - Amount of completed dailies + +**sinfog** - Amount of completed gauntlet non-demon levels + **secret** - Wmfd2893gb7 ### Optional Parameters -**gameVersion** - 21 +**gameVersion** - 22 -**binaryVersion** - 35 +**binaryVersion** - 42 **gdw** - 0 @@ -60,6 +78,8 @@ Updates a user's data **color2** - Color 2 of the player. Defaults to the default secondary color when left out +**color3** - Glow color of the player. Defaults to secondary color when left out + **special** - Glow (2 if enabled) **seed** - A random set of 10 numbers and letters in A-Za-z0-9 @@ -82,11 +102,13 @@ data = { "gjp": "********", # This would be PasswordFinders' password encoded with GJP encryption "userName": "PasswordFinders", "stars": 6969, + "moons": 696, "demons": 69, "diamonds": 5000, "icon": 0, "color1": 21, "color2": 42, + "color3": -1, "iconType": 0, "coins": 150, "userCoins": 400, @@ -100,10 +122,18 @@ data = { "accGlow": 0, "accSpider": 0, "accExplosion": 1, + "accSwing": 0, + "accJetpack": 0, + "dinfo": "13519,10109", + "dinfow": 0, + "dinfog": 0, + "sinfo": "6,9,6,9,6,9,6,9,6,9,6,9", + "sinfod": 0, + "sinfog": 0, "secret": "Wmfd2893gb7", "seed": ''.join(random.sample("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", 10)) } -data['seed2'] = generate_chk([data['accountID'], data['userCoins'], data['demons'], data['stars'], data['coins'], data['iconType'], data['icon'], data['diamonds'], data['accIcon'], data['accShip'], data['accBall'], data['accBird'], data['accDart'], data['accRobot'], data['accGlow'], data['accSpider'], data['accExplosion']], "85271", "xI35fsAapCRg") +data['seed2'] = generate_chk([data['accountID'], data['userCoins'], data['demons'], data['stars'], data['coins'], data['iconType'], data['icon'], data['diamonds'], data['accIcon'], data['accShip'], data['accBall'], data['accBird'], data['accDart'], data['accRobot'], data['accGlow'], data['accSpider'], data['accExplosion'], len(data['dinfo']), data['dinfow'], data['dinfog'], data['sinfo'], data['sinfod'], data['sinfog']], "85271", "xI35fsAapCRg") r = requests.post('http://boomlings.com/database/updateGJUserScore22.php', data=data) print(req.text) @@ -114,4 +144,4 @@ print(req.text) 17787971 ``` - \ No newline at end of file + diff --git a/docs/index.html b/docs/index.html index 6df068a0c..15a301c27 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,42 +3,42 @@ - Geometry Dash Documentation + GD Documentation + - - + content="GDDocs is a project built to openly give advanced information and readable information for aspiring developers looking to interface with Geometry Dash. Primarily, we aim to create this as a website for people to learn more about the inner workings of geometry dash, along with it's data." /> + + + + + + - - - - - - - - + +
- + - - + + + + - - - - - - - - - - - - - - - - - + + + + + - + \ No newline at end of file diff --git a/docs/reference.md b/docs/reference.md deleted file mode 100644 index fb26ff068..000000000 --- a/docs/reference.md +++ /dev/null @@ -1,58 +0,0 @@ -# Geometry Dash References - -### Audio Track - -Part of levels and requests for them is a term known as `audio track`, or the id of the track of a song in the game. - -| ID | Server ID | Track Name | Track Author | -|------|-----------|--------------------------|--------------| -| - | -1 | Practice: Stay Inside Me | OcularNebula | -| 1 | 0 | Stereo Madness | Foreverbound | -| 2 | 1 | Back on Track | DJVI | -| 3 | 2 | Polargeist | Step | -| 4 | 3 | Dry Out | DJVI | -| 5 | 4 | Base after Base | DJVI | -| 6 | 5 | Cant Let Go | DJVI | -| 7 | 6 | Jumper | Waterflame | -| 8 | 7 | Time Machine | Waterflame | -| 9 | 8 | Cycles | DJVI | -| 10 | 9 | xStep | DJVI | -| 11 | 10 | Clutterfunk | Waterflame | -| 12 | 11 | Theory of Everything | DJ-Nate | -| 13 | 12 | Electroman Adventures | Waterflame | -| 14 | 13 | Clubstep | DJ-Nate | -| 15 | 14 | Electrodynamix | DJ-Nate | -| 16 | 15 | Hexagon Force | Waterflame | -| 17 | 16 | Blast Processing | Waterflame | -| 18 | 17 | Theory of Everything 2 | DJ-Nate | -| 19 | 18 | Geometrical Dominator | Waterflame | -| 20 | 19 | Deadlocked | F-777 | -| 21 | 20 | Fingerdash | MDK | -| 1001 | 21 | The Seven Seas | F-777 | -| 1002 | 22 | Viking Arena | F-777 | -| 1003 | 23 | Airborne Robots | F-777 | -| 3001 | 24 | The Challenge | RobTop | -| 2001 | 25 | Payload | Dex Arson | -| 2002 | 26 | Beast Mode | Dex Arson | -| 2003 | 27 | Machina | Dex Arson | -| 2004 | 28 | Years | Dex Arson | -| 2005 | 29 | Frontlines | Dex Arson | -| 2006 | 30 | Space Pirates | Waterflame | -| 2007 | 31 | Striker | Waterflame | -| 2008 | 32 | Embers | Dex Arson | -| 2009 | 33 | Round 1 | Dex Arson | -| 2010 | 34 | Monster Dance Off | F-777 | -| 4001 | 35 | Press Start | MDK | -| 4002 | 36 | Nock Em | Bossfight | -| 4003 | 37 | Power Trip | Boom Kitty | - -### Secrets - -To interact with the Geometry Dash API you need a 11 character long string called `secret`. As of 2.1 there are `4` secrets that are currently known of. - -| Secret | type | Usage | -|:-------|:-----|:------| -| `Wmfd2893gb7` | **Common Secret** | Used in the majority of requests | -| `Wmfv3899gc9` | **Account Secret** | Used for all account related requests | -| `Wmfv2898gc9` | **Level Secret** | Used in level deletion | -| `Wmfp3879gc3` | **Mod Secret** | Used in moderator only requests | diff --git a/docs/reference/keys.md b/docs/reference/keys.md new file mode 100644 index 000000000..e9314bcac --- /dev/null +++ b/docs/reference/keys.md @@ -0,0 +1,32 @@ +# Keys + +> Geometry Dash uses [XOR Cipher](topics/encryption/xor.md) combined with [base64 encoding](topics/encryption/base64.md) to protect various strings within the client. Multiple Keys are used to protect different aspects of the game + +## XOR Keys + +| Key | Usage | XOR Type | +| :------ | :-------------------------- | :------- | +| `11` | Player Save Data | Singular | +| `14251` | Player Messages | Cycled | +| `19283` | Vault Codes | Cycled | +| `19847` | Daily Challenges | Cycled | +| `26364` | Level Password | Cycled | +| `29481` | Comment Integrity | Cycled | +| `37526` | Account Password | Cycled | +| `39673` | Level Leaderboard Integrity | Cycled | +| `41274` | Level Integrity | Cycled | +| `48291` | Load Data | Cycled | +| `52832` | Multiplayer | Cycled | +| `57709` | Music/SFX Library Secret | Cycled | +| `58281` | Rating Integrity | Cycled | +| `59182` | Chest Rewards | Cycled | +| `85271` | Stat Submission Integrity | Cycled | + +## XOR Types + +> there are **two** ways Geometry Dash applies [XOR Cipher](topics/encryption/xor.md) to strings - **Static** and **Cycled** +> +> - Static Ciphers apply the key as is without any changes applying to the key itself. +> - Cycled Ciphers iterate through each value on the key one by one and then loops back once it reaches the end. + + diff --git a/docs/reference/salts.md b/docs/reference/salts.md new file mode 100644 index 000000000..fcde94c7c --- /dev/null +++ b/docs/reference/salts.md @@ -0,0 +1,38 @@ +# Salts + +> Salts are small strings of random characters appended onto the end of strings. In the context of Geometry Dash, salts are used within request validation to make it a bit more difficult for a 3rd parties to interact with the private API + +## List of known Salts + +| Salt | Usage | +| :------------- | :--------------------------------------- | +| `xI25fpAapCQg` | Level Upload Seed | +| `xPT6iUrtws0J` | Comment Chk | +| `ysg6pUrtjn0J` | Like and Rate Chk | +| `xI35fsAapCRg` | Update Profile Chk | +| `yPg6pUrtWn0J` | Level Leaderboard Chk | +| `ask2fpcaqCQ2` | Vault of Secrets & Chamber of Time Codes | +| `oC36fpYaPtdg` | getGJChallenges hash | +| `pC26fpYaQCtg` | getGJRewards hash | +| `mI29fmAnxgTs` | [GJP2](/topics/gjp.md) | + +## How Salts are used + +- Salts are appended onto the end of a string of data. What happens to the salted string depends on what its used for. + +Below is an example of a salt being implemented within **Vault Codes** + +```js + function generate_vault_code(str /*brainpower*/) + { + const SALT = "ask2fpcaqCQ2"; + const URL_SAFE = true; + + let raw_str = str + SALT; // "brainpowerask2fpcaqCQ2" + let xor_str = xor_cycle_cipher(raw_str, "19283"); + + return base64_encode(xor_str, URL_SAFE).toString(); + } +``` + +Please refer to the Encryption Section for more information about the implementation diff --git a/docs/reference/secrets.md b/docs/reference/secrets.md new file mode 100644 index 000000000..6ab2294cc --- /dev/null +++ b/docs/reference/secrets.md @@ -0,0 +1,183 @@ +# Secrets + +> Secrets are 11 character strings used to validate if requests to the server were from a Geometry Dash client. Rather than generating a new secret for each request sent to the server, RobTop opted with hardcoing these secrets and slightly obfuscating them instead to stop people finding them. + +- **Below is a table showing all known secrets** + +### Table of Secrets + +| Secret | Type | +|:--------------|:-----------| +| `Wmfd2893gb7` | Common | +| `Wmfv3899gc9` | Account/MP | +| `Wmfv2898gc9` | Level | +| `Wmfp3879gc3` | Mod | +| `Wmfx2878gb9` | Admin | + +### Secret Types +**As of Geometry Dash version 2.206, there are 4 *publicly* known secrets** + +- Common Secret +- Account Secret +- Level Secret +- Mod Secret + +### **Common Secret** + +> As of Geometry Dash 2.206, there are 44 endpoints on the server which use the `Common Secret`. Below is a table of all known endpoints which use the Common Secret. + +
+Endpoints + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Endpoint
https://www.boomlings.com/database/getAccountURL.php
https://www.boomlings.com/database/acceptGJFriendRequest20.php
https://www.boomlings.com/database/blockGJUser20.php
https://www.boomlings.com/database/deleteGJAccComment20.php
https://www.boomlings.com/database/deleteGJComment20.php
https://www.boomlings.com/database/deleteGJFriendRequests20.php
https://www.boomlings.com/database/deleteGJMessages20.php
https://www.boomlings.com/database/downloadGJLevel22.php
https://www.boomlings.com/database/downloadGJMessage20.php
https://www.boomlings.com/database/getGJAccountComments20.php
https://www.boomlings.com/database/getGJChallenges.php
https://www.boomlings.com/database/getGJCommentHistory.php
https://www.boomlings.com/database/getGJComments21.php
https://www.boomlings.com/database/getGJDailyLevel.php
https://www.boomlings.com/database/getGJFriendRequests20.php
https://www.boomlings.com/database/getGJGauntlets21.php
https://www.boomlings.com/database/getGJLevelLists.php
https://www.boomlings.com/database/getGJLevelScores211.php
https://www.boomlings.com/database/getGJLevelScoresPlat.php
https://www.boomlings.com/database/getGJLevels21.php
https://www.boomlings.com/database/getGJMapPacks21.php
https://www.boomlings.com/database/getGJMessages20.php
https://www.boomlings.com/database/getGJRewards.php
https://www.boomlings.com/database/getGJScores20.php
https://www.boomlings.com/database/getGJSongInfo.php
https://www.boomlings.com/database/getGJTopArtists.php
https://www.boomlings.com/database/getGJUserList20.php
https://www.boomlings.com/database/getGJUsers20.php
https://www.boomlings.com/database/getSaveData.php
https://www.boomlings.com/database/likeGJItem211.php
https://www.boomlings.com/database/rateGJStars211.php
https://www.boomlings.com/database/readGJFriendRequest20.php
https://www.boomlings.com/database/removeGJFriend20.php
https://www.boomlings.com/database/reportGJLevel.php
https://www.boomlings.com/database/requestUserAccess.php
https://www.boomlings.com/database/restoreGJItems.php
https://www.boomlings.com/database/unblockGJUser20.php
https://www.boomlings.com/database/updateGJDesc20.php
https://www.boomlings.com/database/updateGJUserScore22.php
https://www.boomlings.com/database/uploadFriendRequest20.php
https://www.boomlings.com/database/uploadGJAccComment20.php
https://www.boomlings.com/database/uploadGJComment21.php
https://www.boomlings.com/database/uploadGJLevel21.php
https://www.boomlings.com/database/uploadGJLevelList.php
https://www.boomlings.com/database/uploadGJMessage20.php
+ +
+ +**

Account Secret

** +> As of Geometry Dash 2.206, there are 5 endpoints which use the `Account Secret`. The Account Secret was created for endpoints that deal with important account features such as save data and privacy settings. However, some unused code in 2.2 also reveals that the secret could be used for the upcoming multiplayer mode, however this is subject to change. Below is a table of each endpoint which uses the Account Secret. + +
+Endpoints + + + + + + + + +
Endpoint
http://www.boomlings.com/database/accounts/registerGJAccount.php
http://www.boomlings.com/database/accounts/loginGJAccount.php
http://geometrydash.com/database/accounts/syncGJAccountNew.php
http://geometrydash.com/database/accounts/backupGJAccountNew.php
http://www.boomlings.com/database/updateGJAccSettings20.php
+ +
+ +
+Known Multiplayer Endpoints + + + + + + +
Endpoint
http://www.boomlings.com/database/exitMPLobby.php
http://www.boomlings.com/database/joinMPLobby.php
http://www.boomlings.com/database/uploadMPComment.php
+ +
+ +**

Level Secret

** +> As of Geometry Dash 2.206, there are only 2 endpoints that use the `Level Secret`. The Level Secret is used to handle level deletions. Below you can find the 2 endpoints which use the Level Secret + +
+Endpoints + + + + + +
Endpoint
http://www.boomlings.com/database/deleteGJLevelUser20.php
http://www.boomlings.com/database/deleteGJLevelList.php
+
+ +**

Mod Secret

** +> As of Geometry Dash 2.206, there are only 2 endpoints which use the `Mod Secret`. These endpoints allow hand-picked users called moderators to send in-game levels to the server which then have a chance to earn a star rating. Below are the endpoints in question. + +
+Endpoints + + + + + +
Endpoint
http://www.boomlings.com/database/rateGJDemon21.php
http://www.boomlings.com/database/suggestGJStars20.php
+
+ + +### Admin Secret + +> In the client for Geometry Dash 1.9, RobTop mistakenly included the secret for `Admin Endpoints` inside of a function. Admin Endpoints are only accessible for game admins and they make direct changes in-game that affect everyone. + + + +
+**Below is a screenshot of the Admin Secret being created from Geometry Dash 1.9. RobTop split it into 6 segments to obfuscate it.**
+ + +Currently, the Admin Secret no longer works as RobTop became aware of it +
+ + + +### Secret Structure + +A Secret is split into 5 components and can be divided as such: `x|xxx|xxxx|xx|x`. From analysing the available secrets that we have access too, we have a general idea of what each component may represent. + +> **Note:** *The following is based on analysis using the endpoints the secret is used by as well as the status in the community one is required to have in order to access them. **None of the information below has been proven*** + +- **The first component of a Secret is always a `W`** + +- **The second component seems to denote the permissions level of the user** + `mfd` seems to be endpoints that anyone can use. + `mfv` seems to be endpoints that anyone can use but, they handle important information regarding a users account - save data and levels. + `mfp` seems to be endpoints that are restricted to a select few individuals - Geometry Dash Moderators. + `mfx` seems to be endpoints that are restricted to everyone except Game Admins. + +- **The third component is a 4-digit number which it's purpose is unknown** + `2893` + `3899` + `2898` + `3879` + `2878` + + +- **The fourth component seems denote the purpose of the secret.** + `gb` seems to be general use. + `gc` Seems to be specialised use - managing accounts, sending levels, deleting levels. + +- **The fifth component seems to be overall power the endpoints have.** + `3` being able to submit data that can greatly affect in-game levels + `7` being able to perform general activities on the server + `9` being able to alter accounts and remove levels from the server diff --git a/docs/reference/songs.md b/docs/reference/songs.md new file mode 100644 index 000000000..417b84865 --- /dev/null +++ b/docs/reference/songs.md @@ -0,0 +1,65 @@ +# Official Songs + +> **As of Geometry Dash 2.206, there are 39 songs which are used by the game within official levels.** + + +## Table of Official Songs + +> **Note**: *In the tables below, ID refers to the level ID the level would be if uploaded to the servers and the Server ID refers to an ID within the Song Enum.* + +| ID | Server ID | Track Name | Track Author | +|------|-----------|--------------------------|--------------| +| - | -1 | Practice: Stay Inside Me | OcularNebula | +| 1 | 0 | Stereo Madness | Foreverbound | +| 2 | 1 | Back on Track | DJVI | +| 3 | 2 | Polargeist | Step | +| 4 | 3 | Dry Out | DJVI | +| 5 | 4 | Base after Base | DJVI | +| 6 | 5 | Cant Let Go | DJVI | +| 7 | 6 | Jumper | Waterflame | +| 8 | 7 | Time Machine | Waterflame | +| 9 | 8 | Cycles | DJVI | +| 10 | 9 | xStep | DJVI | +| 11 | 10 | Clutterfunk | Waterflame | +| 12 | 11 | Theory of Everything | DJ-Nate | +| 13 | 12 | Electroman Adventures | Waterflame | +| 14 | 13 | Clubstep | DJ-Nate | +| 15 | 14 | Electrodynamix | DJ-Nate | +| 16 | 15 | Hexagon Force | Waterflame | +| 17 | 16 | Blast Processing | Waterflame | +| 18 | 17 | Theory of Everything 2 | DJ-Nate | +| 19 | 18 | Geometrical Dominator | Waterflame | +| 20 | 19 | Deadlocked | F-777 | +| 21 | 20 | Fingerdash | MDK | +| 22 | 21 | Dash | MDK | +| 23 | 22 | Explorers | Hinkik | +| 1001 | 23 | The Seven Seas | F-777 | +| 1002 | 24 | Viking Arena | F-777 | +| 1003 | 25 | Airborne Robots | F-777 | +| 3001 | 26 | Secret | RobTop | +| 2001 | 27 | Payload | Dex Arson | +| 2002 | 28 | Beast Mode | Dex Arson | +| 2003 | 29 | Machina | Dex Arson | +| 2004 | 30 | Years | Dex Arson | +| 2005 | 31 | Frontlines | Dex Arson | +| 2006 | 32 | Space Pirates | Waterflame | +| 2007 | 33 | Striker | Waterflame | +| 2008 | 34 | Embers | Dex Arson | +| 2009 | 35 | Round 1 | Dex Arson | +| 2010 | 36 | Monster Dance Off | F-777 | +| 4001 | 37 | Press Start | MDK | +| 4002 | 38 | Nock Em | Bossfight | +| 4003 | 39 | Power Trip | Boom Kitty | + +## Other Songs + + + + +
+ +**In 2019, Google removed various Geometry Dash clients from their playstore and RobTop accidentally uploaded a development build of update 2.2 when resolving the issue. In the game files, 2 songs could be found, one of which isn't in the final release, that being Firebird by MDK, which, according to RobTop, was not meant to be a real level.** + + + +
diff --git a/docs/resources/client/gamesave.md b/docs/resources/client/gamesave.md index 32366307d..28b44b9d6 100644 --- a/docs/resources/client/gamesave.md +++ b/docs/resources/client/gamesave.md @@ -13,17 +13,21 @@ Your CCGameManager.dat File contains a lot of information regarding your account | playerUDID| [UDID](/topics/encryption/id?id=udid)| The UDID of the user | | playerName| String| The In-game Name of the player| | playerUserID| Integer| The userID of the player| -| playerFrame| [Icon](enumerations.md)| The ID of the player Cube | -| playerShip| [Icon](enumerations.md)| The ID of the player Ship | -| playerBall| [Icon](enumerations.md)| The ID of the player Ball | -| playerBird| [Icon](enumerations.md)| The ID of the player UFO | -| playerDart| [Icon](enumerations.md)| The ID of the player Wave | -| playerRobot| [Icon](enumerations.md)| The ID of the player Robot | -| playerSpider| [Icon](enumerations.md)| The ID of the player Spider | -| playerColor| [Icon](enumerations.md)| The ID of the player Color | -| playerColor2| [Icon](enumerations.md)| The ID of the player Color2 | -| playerStreak| [Icon](enumerations.md)| The ID of the player Trail | -| playerDeathEffect| [Icon](enumerations.md)| The ID of the player Death Effect | +| playerFrame| [Icon](enumerations.md)| The ID of the player's cube | +| playerShip| [Icon](enumerations.md)| The ID of the player's ship | +| playerBall| [Icon](enumerations.md)| The ID of the player's ball | +| playerBird| [Icon](enumerations.md)| The ID of the player's UFO | +| playerDart| [Icon](enumerations.md)| The ID of the player's wave | +| playerRobot| [Icon](enumerations.md)| The ID of the player's robot | +| playerSpider| [Icon](enumerations.md)| The ID of the player's spider | +| playerSwing| [Icon](enumerations.md)| The ID of the player's swing | +| playerColor| [Icon](enumerations.md)| The ID of the player's primary color | +| playerColor2| [Icon](enumerations.md)| The ID of the player's secondary color | +| playerColor3| [Icon](enumerations.md)| The ID of the player's glow color, -1 if same as secondary color | +| playerStreak| [Icon](enumerations.md)| The ID of the player's trail | +| playerShipStreak| [Icon](enumerations.md)| The ID of the player's ship streak | +| playerDeathEffect| [Icon](enumerations.md)| The ID of the player's death effect | +| playerJetpack| [Icon](enumerations.md)| The ID of the player's jetpack | | playerIconType| [Icon](enumerations.md)| The Index of the player's IconType | | playerGlow| Bool| if Glow is enabled or not | | secretNumber| Integer| the answer to `cod3breaker` in the vault of secrets | @@ -39,35 +43,71 @@ Your CCGameManager.dat File contains a lot of information regarding your account | clickedPractice| Bool | if Practice mode button has been clicked | | showedEditorGuide| Bool | if EditorGuide has been clicked | | showedLowDetailDialog| Bool | if low detail mode has been has been clicked | +| showedRateStarDialog| Bool | Whether the difficulty rating explanation was shown | | bootups| integer | The ammount of times you have opened Geometry Dash | | hasRatedGame| Bool | if you have rated the game | | binaryVersion| integer | The Games Binary Version | -| resolution| integer | The games resolution? | -| texQuality| integer | how high the text quality is | +| resolution| [resolution](/resources/client/gamesave/enums.md) | The games resolution | +| texQuality| integer | how high the text quality is (0 for Auto, 1 for Low, 2 for Medium and 3 for High) | | timeOffset| integer | music offset in milliseconds | +| customFPSTarget| float | the FPS target value | +| dpad01 | [Platformer UI](/resources/client/gamesave/dpad.md) | Platformer UI configuration #1 | +| dpad02 | [Platformer UI](/resources/client/gamesave/dpad.md) | Platformer UI configuration #2 | +| dpad03 | [Platformer UI](/resources/client/gamesave/dpad.md) | Platformer UI configuration #3 | +| dpad04 | [Platformer UI](/resources/client/gamesave/dpad.md) | Platformer UI configuration #4, however it's missing the last 5 options | +| dpad05 | [Platformer UI](/resources/client/gamesave/dpad.md) | Platformer UI configuration #5, however it's missing the last 5 options | +| dpadLayout01 | [Platformer UI](/resources/client/gamesave/dpad.md) | Default UI? | +| dpadLayoutDual01 | [Platformer UI](/resources/client/gamesave/dpad.md) | 2 platformer UIs separated by `;`, then 2 gamepad placements, also separated by `;` +| practiceOpacity | float | The opacity of the practice UI, from 0 to 1| +| practicePosX | float | The X position of the practice UI | +| practicePosY | float | The Y position of the practice UI | + +## Legacy Keys + +These keys were used in old versions of the game but are now obsolete. + +| Key | Type | description | +| :-------| :--- | :-----------| +| musicEnabled | Bool | Whether music is enabled or not. Moved to bgVolume | +| fxEnabled | Bool | Whether sound effects are enabled or not. Moved to sfxVolume | +| moreGamesString | String | The contents of the More Games button. It was a list of values separated by `_`. The values were: `gj` for GD Lite, `boom` for Boomlings, `mu` for Boomlings MatchUp and `mm` for Memory Mastermind. The game allowed repeated values | +| hasNewGames | Bool | Whether there are new games in the More Games button. This showed an exclamation mark icon next to the button | +| gameCenterEnabled | Bool | Whether the Game Center (iOS) was enabled. Moved to valueKeeper::[gv_0034](/resources/client/gamesave/gv.md) | +| lastDay | Integer | Presumably the day when you last played (0-indexed) | +| lastMonth | Integer | Presumably the month when you last played (0-indexed) | +| clickedName | Bool | Whether you clicked on your name in the icon kit to change it | +| autoCheckpoints | Bool | Whether checkpoints are placed automatically in practice mode. Moved to valueKeeper::[gv_0027](/resources/client/gamesave/gv.md) | +| showBPMMarkers | Bool | if BPM markers are shown | +| autoRetryLevel | Bool | If the level automatically restarts after death. Moved to valueKeeper::[gv_0026](/resources/client/gamesave/gv.md) | +| recordGameplay | Bool | Presumably whether to record gameplay with Everyplay | +| showedRateDiffDialog | Bool | Unknown (showedRateStarDialog does the same thing?) | +| commentSortRecent | Bool | unknown (there was no option to sort comments by most liked at the time) | +| kEnableTutorial | Bool | unknown | +| showedFirstTutorial | Bool | unknown | ### GLM | Key | Type | description | | :-------| :--- | :-----------| -| [GLM_01](/resources/client/gamesave/GLM.md#GLM_01) | [Level](/resources/server/level.md)| All Official Levels you have progress on are stored here | -| [GLM_02](/resources/client/gamesave/GLM.md#GLM_02) | [level](/resources/server/level.md) | Uploaded levels - before the account System | -| [GLM_03](/resources/client/gamesave/GLM.md#GLM_03) | [Level](/resources/server/level.md)| online levels played| +| [GLM_01](/resources/client/gamesave/GLM.md#GLM_01) | [Level](/resources/client/level.md)| All Official Levels you have progress on are stored here | +| [GLM_02](/resources/client/gamesave/GLM.md#GLM_02) | [level](/resources/client/level.md) | Uploaded levels - before the account System | +| [GLM_03](/resources/client/gamesave/GLM.md#GLM_03) | [Level](/resources/client/level.md)| online levels played| | [GLM_04](/resources/client/gamesave/GLM.md#GLM_04) | rating| Shows what levels you have rated. was removed after 1.9 | | [GLM_06](/resources/client/gamesave/GLM.md#GLM_06) | AccountIDs| The AccountIDs of all creators you follow | | [GLM_07](/resources/client/gamesave/GLM.md#GLM_07) | levelID| Levels played in last session | | [GLM_08](/resources/client/gamesave/GLM.md#GLM_08) | filters | Search Filters States | | [GLM_09](/resources/client/gamesave/GLM.md#GLM_09) | filters | Search Filters for Online Levels | -| [GLM_10](/resources/client/gamesave/GLM.md#GLM_10) | [Level](/resources/server/level.md)| Completed dailies | +| [GLM_10](/resources/client/gamesave/GLM.md#GLM_10) | [Level](/resources/client/level.md)| Completed dailies | | [GLM_11](/resources/client/gamesave/GLM.md#GLM_11) | Integer| Current Daily ID | | [GLM_12](/resources/client/gamesave/GLM.md#GLM_12) | likes | Something Related to likes | | [GLM_13](/resources/client/gamesave/GLM.md#GLM_13) | levelID | All levels you submitted a rating on | | [GLM_14](/resources/client/gamesave/GLM.md#GLM_14) | reportedLevels| A dictionary of all levels you have reported| | [GLM_15](/resources/client/gamesave/GLM.md#GLM_15) | levelID | all Demon levels you have submitted a rating for | -| [GLM_16](/resources/client/gamesave/GLM.md#GLM_16) | [Level](/resources/server/level.md)| All the levels found in the Gauntlets that you have progress on are stored in here | +| [GLM_16](/resources/client/gamesave/GLM.md#GLM_16) | [Level](/resources/client/level.md)| All the levels found in the Gauntlets that you have progress on are stored in here | | [GLM_17](/resources/client/gamesave/GLM.md#GLM_17) | integer| Current Weekly ID | | [GLM_18](/resources/client/gamesave/GLM.md#GLM_18) | Folder | The Folder Names for saved levels | | [GLM_19](/resources/client/gamesave/GLM.md#GLM_19) | Folder | The Folder names for Local Levels | +| [GLM_20](/resources/client/gamesave/GLM.md#GLM_20) | [Templates](/resources/client/gamesave/template.md) | Your Smart Templates | ### GS @@ -97,33 +137,36 @@ Your CCGameManager.dat File contains a lot of information regarding your account | [GS_23](/resources/client/gamesave/GS_Value?id=gs_23)| Gauntlet Level Progress `{levelID}{percentage}` | | [GS_24](/resources/client/gamesave/GS_Value?id=gs_24)| Daily/Weekly Percentage | | [GS_25](/resources/client/gamesave/GS_Value#GS_25)| All the rewards from completed weekly Demons | +| [GS_26](/resources/client/gamesave/GS_Value#GS_26)| Your active path | +| [GS_27](/resources/client/gamesave/GS_Value#GS_27)| All the list rewards | +| [GS_28](/resources/client/gamesave/GS_Value#GS_28)| Your enabled items (animations) | +| [GS_29](/resources/client/gamesave/GS_Value#GS_29)| Unknown (boolean) | ### GJA | Key | Value| | :-- |:-----------| | GJA_001 | Username| -| GJA_002 | Password (in plaintext)| +| GJA_002 | Password (in plaintext) (2.1 and below)| | GJA_003 | AccountID | - -#### GDL22 GJA -| Key | Value| -| :-- |:-----------| -| GJA_004 | SessionID | +| GJA_004 | Session ID (unused) | +| GJA_005 | Your password with [GJP2](/topics/gjp.md) Encryption| ### LLM | Key | Value| | :-- |:-----------| -| LLM_01 | Local Levels | +| LLM_01 | Local [Levels](/resources/client/level.md) | | LLM_02 | Hardcoded to `binaryVersion`| +| LLM_03 | Local [Lists](/resources/client/gamesave/list.md) | ### MDLM | Key | Value | |:----|:------| -| MDLM_001 | Dictionary of SongInfoObject | +| MDLM_001 | Dictionary of [SongInfoObject](/resources/server/song.md) | | MDLM_002 | Song Priority of a song | +| MDLM_003 | Unknown (dict) | ### KBM diff --git a/docs/resources/client/gamesave/GLM.md b/docs/resources/client/gamesave/GLM.md index 7dbaee00f..587117b5f 100644 --- a/docs/resources/client/gamesave/GLM.md +++ b/docs/resources/client/gamesave/GLM.md @@ -12,7 +12,7 @@ GLM_01 is used to store all progress the player has made on official levels ## GLM_02 -GLM_02 was used before the account system to keep track of who owned Specific levels, currently unused +GLM_02 was used in the earliest versions of the game back when your local levels were stored in CCGameManager.dat instead of being separated to CCLocalLevels.dat, currently unused | Key | description | |:----|:------------| @@ -183,3 +183,23 @@ GLM_19 is a dictionary of the folders for local levels | Key | Value | |:----|:------| | `{folder ID}` | `{Folder Name}` | + +## GLM_20 + +GLM_20 is a dictionary of all the editor smart templates you have saved + +| Key | description | +|:----|:------------| +| kCEK 10 | [level data](resources/client/gamesave/template.md) | + +## GLM_21 + +This GLM is unknown, but it presumably had something to do with lists. It was in 2.200 but is not in 2.206 + +## GLM_22 + +GLM_22 is a dictionary of all your favorited lists + +| Key | description | +|:----|:------------| +| kCEK 12 | [level data](resources/client/gamesave/list.md) | diff --git a/docs/resources/client/gamesave/GS_Value.md b/docs/resources/client/gamesave/GS_Value.md index a255d2ec0..152b3b626 100644 --- a/docs/resources/client/gamesave/GS_Value.md +++ b/docs/resources/client/gamesave/GS_Value.md @@ -21,14 +21,38 @@ GS values contain Information regarding certain aspects of the game | 13 | Total Diamonds | | 14 | current orbs | | 15 | Completed Daily Levels | -| 16 | Fire Shards | -| 17 | Ice Shards | -| 18 | Poison Shards | -| 19 | Shadow Shards | +| 16 | Shadow Shards | +| 17 | Poison Shards | +| 18 | Fire Shards | +| 19 | Ice Shards | | 20 | Lava Shards | -| 21 | Bonus Shards | +| 21 | Demon Keys| | 22 | Total Orbs Collected | +| 23 | Earth Shards | +| 24 | Blood Shards | +| 25 | Metal Shards | +| 26 | Light Shards | +| 27 | Soul Shards | +| 28 | Moons | +| 29 | Diamond Shards | +| 30 | Fire Path Progress | +| 31 | Ice Path Progress | +| 32 | Poison Path Progress | +| 33 | Shadow Path Progress | +| 34 | Lava Path Progress | +| 35 | Earth Path Progress | +| 36 | Blood Path Progress | +| 37 | Metal Path Progress | +| 38 | Light Path Progress | +| 39 | Soul Path Progress | +| 40 | Amount of Completed Gauntlets | +| 41 | List Rewards | +| 42 | Completed Insane Levels | +| 43 | Wraith Chest Keys | | unique_{LevelID}_{Coins Collected} | The Coins Collected on the Official Levels +| unique_secretB03 | Glubfub coin | +| unique_secret04 | Official level page secret coin | +| unique_secret06 | 'Sparky' coin | ## GS_completed @@ -41,10 +65,10 @@ all completed levels | `d_{levelID}` | completed daily | `g_{levelID}` | completed gauntlet | `star_{levelID}` | Collected Stars| -| `dstar_{levelID}` | Collected Stars for daily| +| `dstar_{timelyID}` | Collected Stars for daily| | `gstar_{levelID}` | Collected Stars for gauntlet | | `demon_{levelID}` | Collected Demon | -| `ddemon_{levelID}` | Collected Demon for weekly| +| `ddemon_{timelyID}` | Collected Demon for weekly| | `gdemon_{levelID}` | Collected Demon for gauntlet| ## GS_3 @@ -192,7 +216,7 @@ GS_21 Contains the reward data for the gauntlets you have completed | key | Description | |:----|:------| | `g_{GauntletID}` | the gauntlet number | -| `000{ChestID}` | ChestIDs for Reward Chests | +| `00{ChestID}` | ChestIDs for Reward Chests | | `k_{number}` | RewardItems -> the number corresponds to how many RewardItems the player will recieve | | `kCEK` | The [encoder keys](/resources/client/gamesave/kCEK?id=kcek-8-and-9-structure) | @@ -209,6 +233,13 @@ GS_21 Contains the reward data for the gauntlets you have completed | 0007 | YouTube Chest | | 0008 | Twitter Chest | | 0009 | Facebook Chest | +| 0010 | Twitch Chest | +| 0011 | Discord Chest | +| 0022 | Zolguroth Chest (repeatedly poke Shopkeeper and return to shop) | +| 0023 | Help Button Chest | +| 0024 | Reddit Chest | + +All chests between 0012 and 0021 are Ad Chests from the free versions of the game. ## GS_22 @@ -244,3 +275,39 @@ GS_25 Contains the reward data for every weekly demon you have completed | `d100{number}` | The Timely ID of the weekly Demon you have beaten | | `k_{number}` | RewardItems -> the number corresponds to how many RewardItems the player will recieve | | `kCEK` | The [encoder keys](/resources/client/gamesave/kCEK?id=kcek-8-and-9-structure) | + +## GS_26 + +GS_26 is your currently active path. The value is the same as the path key in GS_value. Contary to most GS's, it's an integer value and not a dictionary. + +| Structure | +|:----------| +| `GS_26{path}` | + +## GS_27 + +GS_27 contains the List Rewards you have acquired + +| Structure | +|:----------| +|`lr_{listID}{diamonds}`| + +## GS_28 + +GS_28 contains your enabled items. All of them are strings (although they act as booleans). The animations (18-20) are set to 1 after doing.. something? even if not unlocked. The music unlocker is set to 1 by default as soon as you start the game. The music customize is set to 1 permanently when you unlock it. + +| Key | Description | +|:------|:---------------------| +| 17_12 | Music Unlocker | +| 18_12 | Slow Robot Walk | +| 19_12 | Fast Robot Run | +| 20_12 | Spider Naruto Run | +| 21_12 | Music Customizer | + +## GS_29 + +GS_29 is unknown but is related to trying to fix some path bug. Contary to most GS's, it's a boolean value and not a dictionary. + +| Structure | +|:----------| +| `GS_29` | diff --git a/docs/resources/client/gamesave/achievement.md b/docs/resources/client/gamesave/achievement.md index 2b7e8bc16..1c23ddd9a 100644 --- a/docs/resources/client/gamesave/achievement.md +++ b/docs/resources/client/gamesave/achievement.md @@ -1,12 +1,12 @@ # Achievements -Geometry Dash has 266 achievements that you can aquire, 2 of them are exclusive to the Steam version of the game. You can find out how to get all the achievements [here](https://geometry-dash.fandom.com/wiki/Achievements). +Geometry Dash has 581 achievements that you can acquire, 2 of them are exclusive to the Steam version of the game. You can find out how to get all the achievements [here](https://geometry-dash.fandom.com/wiki/Achievements). -keep in mind that adding the achievements into your save data manually will only show them as completed in game but won't give you the reward for the completion +Keep in mind that adding the achievements into your save data manually will only show them as completed in game, but won't give you the reward for the completion. ## Achievement key structure -Inside CCGameManager.dat, every achievement you have progress on can be found in the `reportedAchievements` Dictionary. they are all organised like this +Inside CCGameManager.dat, every achievement you have progress on can be found in the `reportedAchievements` dictionary. They are all organised like this: ```xml geometry.ach.[internal achievement name][percentage complete]geometry.ach.[internal achievement name][percentage complete] ``` @@ -57,8 +57,18 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.level20a | Beat Deadlocked in practice Mode | | geometry.ach.level21b | Beat Fingerdash in normal Mode | | geometry.ach.level21a | Beat Fingerdash in practice Mode | -| geometry.ach.steam01 | Beat Stereo Madness, Back On Track and Polargeist in normal mode (steam exlusive) | -| geometry.ach.steam02 | Beat Clubstep in normal mode (steam exlusive) | +| geometry.ach.level22b | Beat Dash in normal Mode | +| geometry.ach.level22a | Beat Dash in practice Mode | +| geometry.ach.tower01 | Beat The Tower| +| geometry.ach.tower02 | Beat The Sewers| +| geometry.ach.tower03 | Beat The Cellar| +| geometry.ach.tower04 | Beat The Secret Hollow| +| geometry.ach.tower01Coin | Beat The Tower with all 3 Coins| +| geometry.ach.tower02Coin | Beat The Sewers with all 3 Coins| +| geometry.ach.tower03Coin | Beat The Cellar with all 3 Coins| +| geometry.ach.tower04Coin | Beat The Secret Hollow with all 3 Coins| +| geometry.ach.steam01 | Beat Stereo Madness, Back On Track and Polargeist in normal mode (steam exclusive) | +| geometry.ach.steam02 | Beat Clubstep in normal mode (steam exclusive) | | geometry.ach.demoncoin01 | Beat Clubstep in with all coins| | geometry.ach.demoncoin02 | Beat Theory Of Everything 2 in with all coins| | geometry.ach.demoncoin03 | Beat Deadlocked in with all coins| @@ -72,14 +82,31 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.mappacks08 | Complete 35 Map Packs | | geometry.ach.mappacks09 | Complete 40 Map Packs | | geometry.ach.mappacks10 | Complete 45 Map Packs | +| geometry.ach.gauntlets01 | Complete 1 Gauntlet | +| geometry.ach.gauntlets02 | Complete 5 Gauntlets | +| geometry.ach.gauntlets03 | Complete 10 Gauntlets | +| geometry.ach.gauntlets04 | Complete 15 Gauntlets | +| geometry.ach.daily01 | Complete a Daily Level | +| geometry.ach.daily02 | Complete 25 Daily Levels | +| geometry.ach.daily03 | Complete 50 Daily Levels | +| geometry.ach.daily04 | Complete 100 Daily Levels | +| geometry.ach.daily05 | Complete 150 Daily Levels | +| geometry.ach.daily06 | Complete 250 Daily Levels | +| geometry.ach.daily07 | Complete 365 Daily Levels | | geometry.ach.custom01 | Complete 1 User Created Level in Normal Mode| -| geometry.ach.custom02 | Complete 10 User Created Level in Normal Mode| -| geometry.ach.custom03 | Complete 50 User Created Level in Normal Mode| -| geometry.ach.custom04 | Complete 100 User Created Level in Normal Mode| -| geometry.ach.custom05 | Complete 200 User Created Level in Normal Mode| -| geometry.ach.custom06 | Complete 300 User Created Level in Normal Mode| -| geometry.ach.custom07 | Complete 500 User Created Level in Normal Mode| -| geometry.ach.custom08 | Complete 1,000 User Created Level in Normal Mode| +| geometry.ach.custom02 | Complete 10 User Created Levels in Normal Mode| +| geometry.ach.custom03 | Complete 50 User Created Levels in Normal Mode| +| geometry.ach.custom04 | Complete 100 User Created Levels in Normal Mode| +| geometry.ach.custom05 | Complete 200 User Created Levels in Normal Mode| +| geometry.ach.custom06 | Complete 300 User Created Levels in Normal Mode| +| geometry.ach.custom07 | Complete 500 User Created Levels in Normal Mode| +| geometry.ach.custom08 | Complete 1,000 User Created Levels in Normal Mode| +| geometry.ach.custom09 | Complete 1,500 User Created Levels in Normal Mode| +| geometry.ach.custom10 | Complete 2,000 User Created Levels in Normal Mode| +| geometry.ach.custom11 | Complete 2,500 User Created Levels in Normal Mode| +| geometry.ach.custom12 | Complete 3,000 User Created Levels in Normal Mode| +| geometry.ach.custom13 | Complete 4,000 User Created Levels in Normal Mode| +| geometry.ach.custom14 | Complete 5,000 User Created Levels in Normal Mode| | geometry.ach.stars01 | Collect 100 stars| | geometry.ach.stars02 | Collect 200 stars| | geometry.ach.stars03 | Collect 300 stars| @@ -106,6 +133,39 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.stars24 | Collect 8,000 stars| | geometry.ach.stars25 | Collect 9,000 stars| | geometry.ach.stars26 | Collect 10,000 stars| +| geometry.ach.stars27 | Collect 12,000 stars| +| geometry.ach.stars28 | Collect 14,000 stars| +| geometry.ach.stars29 | Collect 16,000 stars| +| geometry.ach.stars30 | Collect 18,000 stars| +| geometry.ach.stars31 | Collect 20,000 stars| +| geometry.ach.stars32 | Collect 22,500 stars| +| geometry.ach.stars33 | Collect 25,000 stars| +| geometry.ach.moons01 | Collect 100 moons| +| geometry.ach.moons02 | Collect 200 moons| +| geometry.ach.moons03 | Collect 300 moons| +| geometry.ach.moons04 | Collect 400 moons| +| geometry.ach.moons05 | Collect 500 moons| +| geometry.ach.moons06 | Collect 600 moons| +| geometry.ach.moons07 | Collect 700 moons| +| geometry.ach.moons08 | Collect 800 moons| +| geometry.ach.moons09 | Collect 900 moons| +| geometry.ach.moons10 | Collect 1,000 moons| +| geometry.ach.moons11 | Collect 1,500 moons| +| geometry.ach.moons12 | Collect 2,000 moons| +| geometry.ach.moons13 | Collect 2,500 moons| +| geometry.ach.moons14 | Collect 3,000 moons| +| geometry.ach.moons15 | Collect 3,500 moons| +| geometry.ach.moons16 | Collect 4,000 moons| +| geometry.ach.moons17 | Collect 4,500 moons| +| geometry.ach.moons18 | Collect 5,000 moons| +| geometry.ach.moons19 | Collect 5,500 moons| +| geometry.ach.moons20 | Collect 6,000 moons| +| geometry.ach.moons21 | Collect 6,500 moons| +| geometry.ach.moons22 | Collect 7,000 moons| +| geometry.ach.moons23 | Collect 7,500 moons| +| geometry.ach.moons24 | Collect 8,000 moons| +| geometry.ach.moons25 | Collect 9,000 moons| +| geometry.ach.moons26 | Collect 10,000 moons| | geometry.ach.demon01 | Beat 1 Demon| | geometry.ach.demon02 | Beat 2 Demons| | geometry.ach.demon03 | Beat 3 Demons| @@ -118,6 +178,22 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.demon10 | Beat 40 Demons| | geometry.ach.demon11 | Beat 50 Demons| | geometry.ach.demon12 | Beat 60 Demons| +| geometry.ach.demon13 | Beat 70 Demons| +| geometry.ach.demon14 | Beat 80 Demons| +| geometry.ach.demon15 | Beat 90 Demons| +| geometry.ach.demon16 | Beat 100 Demons| +| geometry.ach.demon17 | Beat 120 Demons| +| geometry.ach.demon18 | Beat 140 Demons| +| geometry.ach.demon19 | Beat 160 Demons| +| geometry.ach.demon20 | Beat 180 Demons| +| geometry.ach.demon21 | Beat 200 Demons| +| geometry.ach.demon22 | Beat 225 Demons| +| geometry.ach.demon23 | Beat 250 Demons| +| geometry.ach.demon24 | Beat 275 Demons| +| geometry.ach.demon25 | Beat 300 Demons| +| geometry.ach.demon26 | Beat 350 Demons| +| geometry.ach.demon27 | Beat 400 Demons| +| geometry.ach.demon28 | Beat 500 Demons| | geometry.ach.coins01 | Collect 5 Secret Coins| | geometry.ach.coins02 | Collect 10 Secret Coins| | geometry.ach.coins03 | Collect 15 Secret Coins| @@ -143,6 +219,9 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.coins23 | Collect 115 Secret Coins| | geometry.ach.coins24 | Collect 120 Secret Coins| | geometry.ach.coins25 | Collect 130 Secret Coins| +| geometry.ach.coins26 | Collect 140 Secret Coins| +| geometry.ach.coins27 | Collect 150 Secret Coins| +| geometry.ach.coins28 | Collect 160 Secret Coins| | geometry.ach.usercoins01 | Collect 1 user Coin| | geometry.ach.usercoins02 | Collect 10 user Coins| | geometry.ach.usercoins03 | Collect 20 user Coins| @@ -175,6 +254,14 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.usercoins30 | Collect 800 user Coins| | geometry.ach.usercoins31 | Collect 900 user Coins| | geometry.ach.usercoins32 | Collect 1,000 user Coins| +| geometry.ach.usercoins33 | Collect 1,200 user Coins| +| geometry.ach.usercoins34 | Collect 1,400 user Coins| +| geometry.ach.usercoins35 | Collect 1,600 user Coins| +| geometry.ach.usercoins36 | Collect 1,800 user Coins| +| geometry.ach.usercoins37 | Collect 2,000 user Coins| +| geometry.ach.usercoins38 | Collect 2,300 user Coins| +| geometry.ach.usercoins39 | Collect 2,600 user Coins| +| geometry.ach.usercoins40 | Collect 3,000 user Coins| | geometry.ach.diamonds01 | Collect 100 Diamonds| | geometry.ach.diamonds02 | Collect 250 Diamonds| | geometry.ach.diamonds03 | Collect 500 Diamonds| @@ -185,6 +272,20 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.diamonds08 | Collect 3,000 Diamonds| | geometry.ach.diamonds09 | Collect 4,000 Diamonds| | geometry.ach.diamonds10 | Collect 5,000 Diamonds| +| geometry.ach.diamonds11 | Collect 6,000 Diamonds| +| geometry.ach.diamonds12 | Collect 7,000 Diamonds| +| geometry.ach.diamonds13 | Collect 9,000 Diamonds| +| geometry.ach.diamonds14 | Collect 11,000 Diamonds| +| geometry.ach.diamonds15 | Collect 14,000 Diamonds| +| geometry.ach.diamonds16 | Collect 17,000 Diamonds| +| geometry.ach.diamonds17 | Collect 20,000 Diamonds| +| geometry.ach.diamonds18 | Collect 25,000 Diamonds| +| geometry.ach.lists01 | Claim a List Reward| +| geometry.ach.lists02 | Claim 25 List Rewards| +| geometry.ach.lists03 | Claim 50 List Rewards| +| geometry.ach.lists04 | Claim 100 List Rewards| +| geometry.ach.lists05 | Claim 150 List Rewards| +| geometry.ach.lists06 | Claim 200 List Rewards| | geometry.ach.shardFire01 | Collect 5 Fire Shards| | geometry.ach.shardFire02 | Collect 15 Fire Shards| | geometry.ach.shardFire03 | Collect 35 Fire Shards| @@ -210,28 +311,77 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.shardLava03 | Collect 35 Lava Shards| | geometry.ach.shardLava04 | Collect 65 Lava Shards| | geometry.ach.shardLava05 | Collect 100 Lava Shards| -| geometry.ach.shardBonus01 | Collect 5 of each Shard| -| geometry.ach.shardBonus02 | Collect 15 of each Shard| -| geometry.ach.shardBonusa03 | Collect 35 of each Shard| -| geometry.ach.shardBonus04 | Collect 65 of each Shard| -| geometry.ach.shardBonus05 | Collect 100 of each Shard| -| geometry.ach.followCreator | follow 1 Creator| -| geometry.ach.followCreator2 | follow 10 Creators| +| geometry.ach.shardBonus01 | Collect 5 of each Tier 1 Shard| +| geometry.ach.shardBonus02 | Collect 15 of each Tier 1 Shard| +| geometry.ach.shardBonus03 | Collect 35 of each Tier 1 Shard| +| geometry.ach.shardBonus04 | Collect 65 of each Tier 1 Shard| +| geometry.ach.shardBonus05 | Collect 100 of each Tier 1 Shard| +| geometry.ach.shardEarth01 | Collect 5 Earth Shards| +| geometry.ach.shardEarth02 | Collect 15 Earth Shards| +| geometry.ach.shardEarth03 | Collect 35 Earth Shards| +| geometry.ach.shardEarth04 | Collect 65 Earth Shards| +| geometry.ach.shardEarth05 | Collect 100 Earth Shards| +| geometry.ach.shardBlood01 | Collect 5 Blood Shards| +| geometry.ach.shardBlood02 | Collect 15 Blood Shards| +| geometry.ach.shardBlood03 | Collect 35 Blood Shards| +| geometry.ach.shardBlood04 | Collect 65 Blood Shards| +| geometry.ach.shardBlood05 | Collect 100 Blood Shards| +| geometry.ach.shardMetal01 | Collect 5 Metal Shards| +| geometry.ach.shardMetal02 | Collect 15 Metal Shards| +| geometry.ach.shardMetal03 | Collect 35 Metal Shards| +| geometry.ach.shardMetal04 | Collect 65 Metal Shards| +| geometry.ach.shardMetal05 | Collect 100 Metal Shards| +| geometry.ach.shardLight01 | Collect 5 Light Shards| +| geometry.ach.shardLight02 | Collect 15 Light Shards| +| geometry.ach.shardLight03 | Collect 35 Light Shards| +| geometry.ach.shardLight04 | Collect 65 Light Shards| +| geometry.ach.shardLight05 | Collect 100 Light Shards| +| geometry.ach.shardSoul01 | Collect 5 Soul Shards| +| geometry.ach.shardSoul02 | Collect 15 Soul Shards| +| geometry.ach.shardSoul03 | Collect 35 Soul Shards| +| geometry.ach.shardSoul04 | Collect 65 Soul Shards| +| geometry.ach.shardSoul05 | Collect 100 Soul Shards| +| geometry.ach.shardBonusB01 | Collect 5 of each Tier 2 Shard| +| geometry.ach.shardBonusB02 | Collect 15 of each Tier 2 Shard| +| geometry.ach.shardBonusB03 | Collect 35 of each Tier 2 Shard| +| geometry.ach.shardBonusB04 | Collect 65 of each Tier 2 Shard| +| geometry.ach.shardBonusB05 | Collect 100 of each Tier 2 Shard| +| geometry.ach.followCreator | Follow 1 Creator| +| geometry.ach.followCreator2 | Follow 10 Creators| +| geometry.ach.followCreator3 | Follow 25 Creators| +| geometry.ach.followCreator4 | Follow 50 Creators| | geometry.ach.friends01 | Befriend 1 user| | geometry.ach.friends02 | Befriend 10 users| +| geometry.ach.friends03 | Befriend 25 users| +| geometry.ach.friends04 | Befriend 50 users| | geometry.ach.youtube | subscribe to [RobTop](https://www.youtube.com/user/RobTopGames) on YouTube| -| geometry.ach.youtube | Follow [RobTop](https://twitter.com/robtopgames) on Twitter| -| geometry.ach.youtube | Like [RobTop](https://www.facebook.com/geometrydash) on Facebook| +| geometry.ach.twitter | Follow [RobTop](https://twitter.com/robtopgames) on Twitter| +| geometry.ach.facebook | Like [RobTop](https://www.facebook.com/geometrydash) on Facebook| | geometry.ach.attempt01 | Do 100 Attempts | | geometry.ach.attempt02 | Do 500 Attempts | | geometry.ach.attempt03 | Do 2,000 Attempts | | geometry.ach.attempt04 | Do 10,000 Attempts | | geometry.ach.attempt05 | Do 20,000 Attempts | +| geometry.ach.attempt06 | Do 30,000 Attempts | +| geometry.ach.attempt07 | Do 40,000 Attempts | +| geometry.ach.attempt08 | Do 60,000 Attempts | +| geometry.ach.attempt09 | Do 80,000 Attempts | +| geometry.ach.attempt10 | Do 100,000 Attempts | +| geometry.ach.attempt11 | Do 135,000 Attempts | +| geometry.ach.attempt12 | Do 185,000 Attempts | +| geometry.ach.attempt13 | Do 250,000 Attempts | +| geometry.ach.attempt14 | Do 300,000 Attempts | | geometry.ach.jump01 | Jump 1,000 Times | | geometry.ach.jump02 | Jump 10,000 Times | | geometry.ach.jump03 | Jump 20,000 Times | | geometry.ach.jump04 | Jump 50,000 Times | | geometry.ach.jump05 | Jump 100,000 Times | +| geometry.ach.jump06 | Jump 200,000 Times | +| geometry.ach.jump07 | Jump 300,000 Times | +| geometry.ach.jump08 | Jump 400,000 Times | +| geometry.ach.jump09 | Jump 500,000 Times | +| geometry.ach.jump10 | Jump 750,000 Times | +| geometry.ach.jump11 | Jump 1,000,000 Times | | geometry.ach.submit | Submit an online level | | geometry.ach.rate | Click the supporter icon when it's locked | | geometry.ach.rateDiff | Rate the stars of an online level | @@ -244,11 +394,126 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.like02b | Like or dislike 500 online levels | | geometry.ach.like03 | Like or dislike 1,000 online levels | | geometry.ach.like04 | Like or dislike 2,000 online levels | +| geometry.ach.like05 | Like or dislike 3,000 online levels | +| geometry.ach.like06 | Like or dislike 4,000 online levels | | geometry.ach.moreGames | Click the "More Games" button | | geometry.ach.special01 | Die at over 95% on a main level | | geometry.ach.creator01 | Get 100 likes on your level | | geometry.ach.creator02 | Get a star rate on your level | | geometry.ach.creator03 | Get 50 likes on your level | +| geometry.ach.creator04 | Get 250 likes on your level | +| geometry.ach.creator05 | Get 500 likes on your level | +| geometry.ach.creator06 | Get 1,000 likes on your level | +| geometry.ach.path01.00 | Unlock the Path of Fire | +| geometry.ach.path01.01 | Reach Path of Fire Rank 1 | +| geometry.ach.path01.02 | Reach Path of Fire Rank 2 | +| geometry.ach.path01.03 | Reach Path of Fire Rank 3 | +| geometry.ach.path01.04 | Reach Path of Fire Rank 4 | +| geometry.ach.path01.05 | Reach Path of Fire Rank 5 | +| geometry.ach.path01.06 | Reach Path of Fire Rank 6 | +| geometry.ach.path01.07 | Reach Path of Fire Rank 7 | +| geometry.ach.path01.08 | Reach Path of Fire Rank 8 | +| geometry.ach.path01.09 | Reach Path of Fire Rank 9 | +| geometry.ach.path01.10 | Reach Path of Fire Rank 10 | +| geometry.ach.path02.00 | Unlock the Path of Ice | +| geometry.ach.path02.01 | Reach Path of Ice Rank 1 | +| geometry.ach.path02.02 | Reach Path of Ice Rank 2 | +| geometry.ach.path02.03 | Reach Path of Ice Rank 3 | +| geometry.ach.path02.04 | Reach Path of Ice Rank 4 | +| geometry.ach.path02.05 | Reach Path of Ice Rank 5 | +| geometry.ach.path02.06 | Reach Path of Ice Rank 6 | +| geometry.ach.path02.07 | Reach Path of Ice Rank 7 | +| geometry.ach.path02.08 | Reach Path of Ice Rank 8 | +| geometry.ach.path02.09 | Reach Path of Ice Rank 9 | +| geometry.ach.path02.10 | Reach Path of Ice Rank 10 | +| geometry.ach.path03.00 | Unlock the Path of Poison | +| geometry.ach.path03.01 | Reach Path of Poison Rank 1 | +| geometry.ach.path03.02 | Reach Path of Poison Rank 2 | +| geometry.ach.path03.03 | Reach Path of Poison Rank 3 | +| geometry.ach.path03.04 | Reach Path of Poison Rank 4 | +| geometry.ach.path03.05 | Reach Path of Poison Rank 5 | +| geometry.ach.path03.06 | Reach Path of Poison Rank 6 | +| geometry.ach.path03.07 | Reach Path of Poison Rank 7 | +| geometry.ach.path03.08 | Reach Path of Poison Rank 8 | +| geometry.ach.path03.09 | Reach Path of Poison Rank 9 | +| geometry.ach.path03.10 | Reach Path of Poison Rank 10 | +| geometry.ach.path04.00 | Unlock the Path of Shadow | +| geometry.ach.path04.01 | Reach Path of Shadow Rank 1 | +| geometry.ach.path04.02 | Reach Path of Shadow Rank 2 | +| geometry.ach.path04.03 | Reach Path of Shadow Rank 3 | +| geometry.ach.path04.04 | Reach Path of Shadow Rank 4 | +| geometry.ach.path04.05 | Reach Path of Shadow Rank 5 | +| geometry.ach.path04.06 | Reach Path of Shadow Rank 6 | +| geometry.ach.path04.07 | Reach Path of Shadow Rank 7 | +| geometry.ach.path04.08 | Reach Path of Shadow Rank 8 | +| geometry.ach.path04.09 | Reach Path of Shadow Rank 9 | +| geometry.ach.path04.10 | Reach Path of Shadow Rank 10 | +| geometry.ach.path05.00 | Unlock the Path of Lava | +| geometry.ach.path05.01 | Reach Path of Lava Rank 1 | +| geometry.ach.path05.02 | Reach Path of Lava Rank 2 | +| geometry.ach.path05.03 | Reach Path of Lava Rank 3 | +| geometry.ach.path05.04 | Reach Path of Lava Rank 4 | +| geometry.ach.path05.05 | Reach Path of Lava Rank 5 | +| geometry.ach.path05.06 | Reach Path of Lava Rank 6 | +| geometry.ach.path05.07 | Reach Path of Lava Rank 7 | +| geometry.ach.path05.08 | Reach Path of Lava Rank 8 | +| geometry.ach.path05.09 | Reach Path of Lava Rank 9 | +| geometry.ach.path05.10 | Reach Path of Lava Rank 10 | +| geometry.ach.path06.00 | Unlock the Path of Earth | +| geometry.ach.path06.01 | Reach Path of Earth Rank 1 | +| geometry.ach.path06.02 | Reach Path of Earth Rank 2 | +| geometry.ach.path06.03 | Reach Path of Earth Rank 3 | +| geometry.ach.path06.04 | Reach Path of Earth Rank 4 | +| geometry.ach.path06.05 | Reach Path of Earth Rank 5 | +| geometry.ach.path06.06 | Reach Path of Earth Rank 6 | +| geometry.ach.path06.07 | Reach Path of Earth Rank 7 | +| geometry.ach.path06.08 | Reach Path of Earth Rank 8 | +| geometry.ach.path06.09 | Reach Path of Earth Rank 9 | +| geometry.ach.path06.10 | Reach Path of Earth Rank 10 | +| geometry.ach.path07.00 | Unlock the Path of Blood | +| geometry.ach.path07.01 | Reach Path of Blood Rank 1 | +| geometry.ach.path07.02 | Reach Path of Blood Rank 2 | +| geometry.ach.path07.03 | Reach Path of Blood Rank 3 | +| geometry.ach.path07.04 | Reach Path of Blood Rank 4 | +| geometry.ach.path07.05 | Reach Path of Blood Rank 5 | +| geometry.ach.path07.06 | Reach Path of Blood Rank 6 | +| geometry.ach.path07.07 | Reach Path of Blood Rank 7 | +| geometry.ach.path07.08 | Reach Path of Blood Rank 8 | +| geometry.ach.path07.09 | Reach Path of Blood Rank 9 | +| geometry.ach.path07.10 | Reach Path of Blood Rank 10 | +| geometry.ach.path08.00 | Unlock the Path of Metal | +| geometry.ach.path08.01 | Reach Path of Metal Rank 1 | +| geometry.ach.path08.02 | Reach Path of Metal Rank 2 | +| geometry.ach.path08.03 | Reach Path of Metal Rank 3 | +| geometry.ach.path08.04 | Reach Path of Metal Rank 4 | +| geometry.ach.path08.05 | Reach Path of Metal Rank 5 | +| geometry.ach.path08.06 | Reach Path of Metal Rank 6 | +| geometry.ach.path08.07 | Reach Path of Metal Rank 7 | +| geometry.ach.path08.08 | Reach Path of Metal Rank 8 | +| geometry.ach.path08.09 | Reach Path of Metal Rank 9 | +| geometry.ach.path08.10 | Reach Path of Metal Rank 10 | +| geometry.ach.path09.00 | Unlock the Path of Light | +| geometry.ach.path09.01 | Reach Path of Light Rank 1 | +| geometry.ach.path09.02 | Reach Path of Light Rank 2 | +| geometry.ach.path09.03 | Reach Path of Light Rank 3 | +| geometry.ach.path09.04 | Reach Path of Light Rank 4 | +| geometry.ach.path09.05 | Reach Path of Light Rank 5 | +| geometry.ach.path09.06 | Reach Path of Light Rank 6 | +| geometry.ach.path09.07 | Reach Path of Light Rank 7 | +| geometry.ach.path09.08 | Reach Path of Light Rank 8 | +| geometry.ach.path09.09 | Reach Path of Light Rank 9 | +| geometry.ach.path09.10 | Reach Path of Light Rank 10 | +| geometry.ach.path10.00 | Unlock the Path of Souls | +| geometry.ach.path10.01 | Reach Path of Souls Rank 1 | +| geometry.ach.path10.02 | Reach Path of Souls Rank 2 | +| geometry.ach.path10.03 | Reach Path of Souls Rank 3 | +| geometry.ach.path10.04 | Reach Path of Souls Rank 4 | +| geometry.ach.path10.05 | Reach Path of Souls Rank 5 | +| geometry.ach.path10.06 | Reach Path of Souls Rank 6 | +| geometry.ach.path10.07 | Reach Path of Souls Rank 7 | +| geometry.ach.path10.08 | Reach Path of Souls Rank 8 | +| geometry.ach.path10.09 | Reach Path of Souls Rank 9 | +| geometry.ach.path10.10 | Reach Path of Souls Rank 10 | | geometry.ach.secret01 | Destroy an icon on the main menu | | geometry.ach.secret02 | Destroy 50 icons on the main menu | | geometry.ach.secret02b | Destroy 100 icons on the main menu | @@ -268,6 +533,8 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.secret15 | Type 'gandalfpotter' into the 2.0 Vault | | geometry.ach.secret16 | Consecutively type '8', '16', '30', '32', '46' and '84' into the 2.0 Vault | | geometry.ach.secret17 | Type your username into the 2.0 Vault | +| geometry.ach.secret18 | Destroy 750 icons on the main menu | +| geometry.ach.secret19 | Type 'finalboss' into the 2.0 Vault | | geometry.ach.v2.secret01 | Type 'brainpower' into the Vault of Secrets | | geometry.ach.v2.secret02 | Type 'cod3breaker' and the solution to the puzzle into the Vault of Secrets | | geometry.ach.v2.secret03 | Solve the 'glubfub' puzzle | @@ -276,11 +543,15 @@ Inside CCGameManager.dat, every achievement you have progress on can be found in | geometry.ach.v2.secret06 | Type 'seven' into the Vault of Secrets | | geometry.ach.v2.secret07 | Type 'gimmiethecolor' into the Vault of Secrets | | geometry.ach.v2.secret08 | Type 'thechickenisonfire' into the Vault of Secrets | +| geometry.ach.v2.secret09 | Type 'd4shg30me7ry' into the Vault of Secrets | +| geometry.ach.v2.secret10 | Type 'thechickenisready' into the Vault of Secrets | | geometry.ach.v3.secret01 | Type 'darkness' into the Chamber of Time | | geometry.ach.v3.secret02 | Type 'silence' into the Chamber of Time | | geometry.ach.v3.secret03 | Type 'river' into the Chamber of Time | | geometry.ach.v3.secret04 | Type 'hunger' into the Chamber of Time | | geometry.ach.v3.secret05 | Type 'volcano' into the Chamber of Time | +| geometry.ach.v3.secret06 | Type 'backontrack' into the Chamber of Time | +| geometry.ach.v3.secret07 | Type 'givemehelper' into the Chamber of Time | ## GD Meltdown Achievements | Internal name | How it's acquired | diff --git a/docs/resources/client/gamesave/dpad.md b/docs/resources/client/gamesave/dpad.md new file mode 100644 index 000000000..b06f49f56 --- /dev/null +++ b/docs/resources/client/gamesave/dpad.md @@ -0,0 +1,19 @@ +# Platformer UI + +The Platformer UI object is formatted as follows: + +`{width},{height},{scale},{opacity},{xPos},{yPos},{modeB},{deadzone},{radius},{snap},{split}` + +| Key | Type | Description | +| :-----------------| :----------| :-----------------------------------------------------------------------| +| width | integer | The width of the button hitbox | +| height | integer | The height of the button hitbox | +| scale | float | The scale of the buttons | +| opacity | integer | The button opacity (from 0 to 255) | +| xPos | float | The X position of the buttons | +| yPos | float | The Y position of the buttons | +| modeB | boolean | The ModeB checkbox | +| deadzone | float | The deadzone between the buttons | +| radius | float | The distance between the buttons | +| snap | boolean | The Snap checkbox | +| split | boolean | The Split checkbox | diff --git a/docs/resources/client/gamesave/enums.md b/docs/resources/client/gamesave/enums.md new file mode 100644 index 000000000..08a687465 --- /dev/null +++ b/docs/resources/client/gamesave/enums.md @@ -0,0 +1,46 @@ +# Enumerations + +## Texture Quality + +This enum represents the quality of the game's textures. + +| Value | Name | +| :-----|:------ | +| 0 | Auto | +| 1 | Low | +| 2 | Medium | +| 3 | High | + +## Resolution + +This enum represents the game window's resolution. + +| Value | Width | Height | Aspect Ratio | +|:------|:------|:-------|:-------------| +| 1 | 640 | 480 | 4:3 | +| 2 | 720 | 480 | 3:2 | +| 3 | 720 | 576 | 5:4 | +| 4 | 800 | 600 | 4:3 | +| 5 | 1024 | 768 | 4:3 | +| 6 | 1152 | 864 | 4:3 | +| 7 | 1176 | 664 | 147:83 | +| 8 | 1280 | 720 | 16:9 | +| 9 | 1280 | 768 | 5:3 | +| 10 | 1280 | 800 | 16:10 | +| 11 | 1280 | 960 | 4:3 | +| 12 | 1280 | 1024 | 5:4 | +| 13 | 1360 | 768 | 85:48 | +| 14 | 1366 | 768 | 683:384 | +| 15 | 1440 | 900 | 16:10 | +| 16 | 1600 | 900 | 16:9 | +| 17 | 1600 | 1024 | 25:16 | +| 18 | 1600 | 1200 | 4:3 | +| 19 | 1680 | 1050 | 16:10 | +| 20 | 1768 | 992 | 221:124 | +| 21 | 1920 | 1080 | 16:9 | +| 22 | 1920 | 1200 | 16:10 | +| 23 | 1920 | 1440 | 4:3 | +| 24 | 2048 | 1536 | 4:3 | +| 25 | 2560 | 1440 | 16:9 | +| 26 | 2560 | 1600 | 16:10 | +| 27 | 3840 | 2160 | 16:9 | diff --git a/docs/resources/client/gamesave/gv.md b/docs/resources/client/gamesave/gv.md index ef0834697..18843fa3f 100644 --- a/docs/resources/client/gamesave/gv.md +++ b/docs/resources/client/gamesave/gv.md @@ -37,6 +37,7 @@ Game Variables (gvs) are found inside of the [ValueKeeper](/resources/client/gam | gv_0032 | forceTimerEnabled -> seems to be unused | | gv_0033 | ChangeSongPath | | gv_0034 | GameCenterEnabled | +| gv_0035 | Small Grid Step - 1.9 | | gv_0036 | PreviewMode (editor) | | gv_0037| showGround (editor) | | gv_0038 | showGrid (editor) | @@ -46,14 +47,14 @@ Game Variables (gvs) are found inside of the [ValueKeeper](/resources/client/gam | gv_0042 | increaseMaxLevels | | gv_0043 | effectLinesEnabled (editor) | | gv_0044 | drawTriggerBoxes (editor) | -| gv_0045 | debugDraw (editor) | +| gv_0045 | Show Hitboxes (editor) | | gv_0046 | hideUIOnTest (editor) | | gv_0047 | showedProfileText | | gv_0048 | viewedOwnProfile | | gv_0049 | buttonsPerRow (editor) | | gv_0050 | buttonRows (editor) | | gv_0051 | showedNGMessage (editor) | -| gv_0052 | fastPracticeReset| +| gv_0052 | Enable Faster Reset | | gv_0053 | Free Games Popup | | gv_0055 | checkIfServerOnline.php | | gv_0056 | disableObjectAlert | @@ -71,6 +72,7 @@ Game Variables (gvs) are found inside of the [ValueKeeper](/resources/client/gam | gv_0068 | quickCheckpointMode | | gv_0069 | commentMode | | gv_0070 | showedUnlistedLevelMessage | +| gv_0071 | Hide Practice Buttons | | gv_0072 | disableGravityEffect | | gv_0073| newCompletedFilter| | gv_0074| showRestartButton| @@ -90,7 +92,7 @@ Game Variables (gvs) are found inside of the [ValueKeeper](/resources/client/gam | gv_0092| onlineLevels folder number | | gv_0093| increaseLocalLevelsPerPage | | gv_0094| moreCommentsMode | -| gv_0095| flippyUselessMode (Just Dont)| +| gv_0095| flippyUselessMode ("Do Not..." (formerly "Just Don't"))| | gv_0096| switchWaveTrailColor| | gv_0097| enableLinkControls (editor) | | gv_0098| levelLeaderboardType | @@ -98,26 +100,65 @@ Game Variables (gvs) are found inside of the [ValueKeeper](/resources/client/gam | gv_0100| practiceDeathEffect| | gv_0101| forceSmoothFix | | gv_0102 | smoothFixInEditor | - - -### 2019 Leak GV's - -| Key | Value| -| :-- |:-----------| -| gv_0103 | Layer Locking | +| gv_0103 | Layer Locking (editor) | | gv_0104 | Record Order | | gv_0105 | StartPos Playback | | gv_0106 | Show Meltdown Promo | | gv_0108 | Auto Low Detail | | gv_0109 | Level Info Label | | gv_0110 | Fast Editor Preview | +| gv_0112 | Increase Scale Limit (editor) | | gv_0113 | Flip Platformer Controls | | gv_0115 | Show FPS | | gv_0116 | Use Custom FPS | | gv_0117 | Preview Particle | | gv_0118 | Preview Animation | | gv_0119 | Dont Save Level Data | - +| gv_0121 | Hide Invisible (editor) | +| gv_0122 | Disable Menu Music | +| gv_0125 | Unlock Practice Music (editor) | +| gv_0126 | Decimal Percentage | +| gv_0127 | Save Gauntlet Levels | +| gv_0128 | Lock Cursor In-Game | +| gv_0129 | Disable Portal Labels | +| gv_0130 | Enable Orb Labels | +| gv_0131 | Use Nearby as Reference (smart template) | +| gv_0132 | Dont Delete (smart template) | +| gv_0133 | Group ID Filter (editor) | +| gv_0134 | Hide Attempts | +| gv_0135 | Hide Attempts in Practice Mode | +| gv_0136 | Enable Extra LDM | +| gv_0137 | Hide Particle Icons (editor) | +| gv_0139 | Color Channel Filter (editor) | +| gv_0140 | Disable Orb Scale | +| gv_0141 | Disable Trigger Orb Scale | +| gv_0142 | Reduce Audio Quality | +| gv_0144 | Audio Visualizer | +| gv_0145 | Show Time | +| gv_0146 | Disable Checkpoints | +| gv_0147 | Search Folder Name (SFX Library) | +| gv_0148 | Compact Mode (SFX Library) | +| gv_0149 | Show Clicks (editor) | +| gv_0150 | Auto Pause on Test (editor) | +| gv_0151 | Start Optimization (editor) | +| gv_0152 | Hide Path (editor) | +| gv_0153 | Explode Player on Death | +| gv_0155 | Disable Shader Anti-Aliasing | +| gv_0156 | Disable Paste State Groups (editor) | +| gv_0157 | Level Upload Guidelines Shown | +| gv_0158 | Preview Shaders (editor) | +| gv_0159 | Audio Fix 01 | +| gv_0163 | Enable Quick Keys | +| gv_0164 | Level Leaderboard Mode | +| gv_0166 | Show Hitboxes (in practice mode) | +| gv_0167 | Confirm Exit | +| gv_0168 | Fast Menu | +| gv_0169 | Small Warp Buttons (editor) | +| gv_0170 | Borderless Fullscreen | +| gv_0171 | Disable Player Hitbox | +| gv_0172 | Disable Shake (2.2) | +| gv_0173 | Ignore Damage (playtesting outside of editor) | +| gv_0174 | Hide Playtest Text | # Unlocked Game Variables @@ -151,14 +192,19 @@ Unlocked Game Variables (ugv) are used to check if ingame events have been compl | ugv_22 | YouTube Chest unlocked | | ugv_23 | FaceBook Chest unlocked | | ugv_24 | Twitter Chest unlocked | - -### 2019 Leak UGV's - -| Key | Value| -| :-- |:-----------| -| ugv_25 | FireBird GateKeeper | -| ugv_26 | Twitch Chest Unlocked | -| ugv_27 | Discord Chest Unlocked | +| ugv_25 | Explorers unlocked | +| ugv_26 | Twitch Chest unlocked | +| ugv_27 | Discord Chest unlocked | +| ugv_28 | Clicked The Tower | +| ugv_29 | Entered The Tower | +| ugv_30 | Accepted Geometry Dash's Terms of Service | +| ugv_31 | Zolguroth Encountered | +| ugv_32 | Reddit Chest unlocked | +| ugv_33 | The Tower Floor 1 completed | +| ugv_34 | Diamond Shop Unlocked | +| ugv_35 | Mechanic Unlocked | +| ugv_36 | Mechanic Dialogue | +| ugv_37 | Diamond Shopkeeper Dialogue | ### Undiscovered/unknown GV's @@ -167,13 +213,10 @@ Unlocked Game Variables (ugv) are used to check if ingame events have been compl | 0017 | **False** | | | 0020 | **False** | | | 0021 | **False** | | -| 0035 | **False** | | | 0054 | **False** || -| 0071 | **False** || | 0080 | **False** || | 0085 | **False** || | 0086 | **False** || | 0087 | **False** || -| 0112 | **True** | Used in `EditorUI` (**2019 Leak**) | | 0114 | **True** | Used in `PlayerObject` (**2019 Leak**) | | 0120 | **True** | Shown in RobTop's 2020 Twitch Streams | diff --git a/docs/resources/client/gamesave/kCEK.md b/docs/resources/client/gamesave/kCEK.md index 3e28efe51..7a6a13fb6 100644 --- a/docs/resources/client/gamesave/kCEK.md +++ b/docs/resources/client/gamesave/kCEK.md @@ -7,6 +7,9 @@ | 7 | [GJChallengeItem](resources/client/gamesave/quests.md) | Contains Keys that the game uses to show quests when offline | | 8 | [GJRewardItem](resources/client/gamesave/kCEK.md) | object that holds `GJRewardObject` | | 9 | [GJRewardObject](resources/client/gamesave/kCEK.md) | contains data for Rewards | +| 10 | [GJSmartTemplate](resources/client/gamesave/template.md) | Contains data about smart template | +| 11 | [GJSmartRefab](resources/client/gamesave/template.md) | Contains data for presets in smart template | +| 12 | [GJLevelList](/resources/client/gamesave/list.md) | Contains keys for level lists | ## kCEK 8 and 9 Structure @@ -35,6 +38,12 @@ | 7 | Orbs | | 8 | Diamonds | | 9 | Custom Object (shares the same ID for Demon guardian key, icons and colours) | +| 10| Earth Shard | +| 11| Blood Shard | +| 12| Metal Shard | +| 13| Light Shard | +| 14| Soul Shard | +| 15| Gold Key | ### GJRewardItem GS Keys @@ -50,4 +59,4 @@ | ID | type | |:---|:-----| | 1 | 4 hour chest | -| 2 | 24 hour chest | \ No newline at end of file +| 2 | 24 hour chest | diff --git a/docs/resources/client/gamesave/list.md b/docs/resources/client/gamesave/list.md new file mode 100644 index 000000000..5ab2f1ac8 --- /dev/null +++ b/docs/resources/client/gamesave/list.md @@ -0,0 +1,44 @@ +# Client List Resource + +## List + +A level list is a list of levels - playable objects in Geometry Dash, namely coming with data that explains on what this list is and which levels it contains. It is stored in [XML](https://en.wikipedia.org/wiki/XML) format, and each level entry is a dictionary, containing key/value pairs denoting the list's properties. + +### Level Data + +**Level Structure** + +| Key | Name/Value | Type | Description | +| :--- | :----------------------------- | :--------------------------------------------------------------- | :------------------------------------------------------------------------------- | +| k1 | Level ID | **integer** | the id of the list | +| k2 | Level Name | **string** | the name of the list | +| k3 | Description | **string** | the list description, encoded in [base64](https://en.wikipedia.org/wiki/Base64) | +| k5 | Creator | **string** | the name of the level creator | +| k7 | list difficulty | **integer** | the difficulty the list has | +| k11 | Downloads | **integer** | the amount of times the list's been downloaded | +| k15 | Uploaded | **bool** | whether the list is uploaded to the server or not | +| k16 | List Version | **integer** | the version of the list | +| k21 | List Type | **Integer** | The List Type (2 = Local, 3 = Saved, 4 = Online) | +| k22 | Like Rating | **integer** | the level's like rating (`likes - dislikes`) | +| k25 | isDemon | **Bool** | if the level is demon or not | +| k26 | Stars | **integer** | the stars the level is worth | +| k27 | Featured | **Bool** | Whether the list is featured or not | +| k42 | Original | **integer** | The ID the of the original list (if the list was copied) | +| k46 | List Revision | **integer** | the revision of the list | +| k60 | AccountID | **integer** | the Creators AccountID | +| k79 | Unlisted | **bool** | Whether the list can only be found by searching the ID | +| k82 | Favourited | **Bool** | if you put the list in your favourites | +| k83 | levelOrder | **integer** | ordering for lists | +| k96 | Level IDs | **string** | Comma-separated list of all IDs in the list in order | +| k97 | Levels | **Dictionary** | Dictionary of all the [levels](/resources/client/level.md) in the list | +| k98 | Upload Date | **Integer** | UNIX timestamp of the level's upload date (in seconds) | +| k99 | Update Date | **Integer** | UNIX timestamp of the level's update date (in seconds) | +| k113 | Diamond Reward | **Integer** | Amount of diamonds awarded upon beating the required number of levels | +| k114 | Required Levels | **Integer** | How many levels have to be beaten to claim the award | + +**Current Unknown Values** + +| Key | Type | Info | +| :-- | :---------- | :------------------------------------------------------- | +| k94 | **Bool** | | +| k100 | **Bool** | | diff --git a/docs/resources/client/gamesave/template.md b/docs/resources/client/gamesave/template.md new file mode 100644 index 000000000..9fe257c85 --- /dev/null +++ b/docs/resources/client/gamesave/template.md @@ -0,0 +1,52 @@ +# Smart Templates (kCEK 10 & 11) + +Smart Templates are user-made templates used for the Auto Build feature. They are stored in [GLM_20](/resources/client/gamesave/GLM.md#GLM_20). + +## kCEK 10 + +| Key | Value | Type | +|:----|:-------------------|:----------| +| 1 | Smart Template ID | integer | +| 2 | Name | string | +| 3 | Template Variations| dict | +| 4 | Unknown | integer | +| 5 | Unknown | integer | +| 6 | Unknown | integer | +| 7 | Unknown | integer | + +## kCEK 11 + +kCEK 11's are stored in key 3 of kCEK 10 in the following format: + +``` +100010000 + + _isArr + + k_0 + + kCEK11 + 11,1,2,0,3,-90;1,2895,2,0,3,-90,34,1; + + k_1 + + kCEK11 + 1... + + +``` + +kCEK 11 key 1 is the [object string](/resources/client/level-components/level-string.md) of the template variation. The object string is **unencoded** and stored raw. The center point is at X: 0, Y: -90. + +The keys of the dictionary are 9-bit bitfields (binary numbers) representing the exact type of template variation. The first bit is reserved and is always set to `1`. Other bits represent: + +| Bit | Position | +|:------|:-------------| +| 2 | Top Middle | +| 3 | Bottom Middle| +| 4 | Center Left | +| 5 | Center Right | +| 6 | Top Left | +| 7 | Top Right | +| 8 | Bottom Left | +| 9 | Bottom Right | diff --git a/docs/resources/client/gamesave/valueKeeper.md b/docs/resources/client/gamesave/valueKeeper.md index 8ca8d8b2f..c5c362973 100644 --- a/docs/resources/client/gamesave/valueKeeper.md +++ b/docs/resources/client/gamesave/valueKeeper.md @@ -7,13 +7,17 @@ Value Keeper is a giant Dictionary found in CCGameManager.dat that contains the | prefix | Description | |:-------|:------------| | [gv_{ID}](/resources/client/gamesave/gv.md) | enabled Game Variables | -| i_{ID} | The playerCubes you have unlocked | -| ship_{ID} | The PlayerShips you have unlocked | -| ball_{ID} | The PlayerBalls you have unlocked | -| bird_{ID} | The PlayerBirdss you have unlocked | -| dart_{ID} | The PlayerDarts you have unlocked | -| robot_{ID} | The PlayerRobots you have unlocked | +| i_{ID} | The cubes you have unlocked | +| ship_{ID} | The ships you have unlocked | +| ball_{ID} | The balls you have unlocked | +| bird_{ID} | The birds you have unlocked | +| dart_{ID} | The waves you have unlocked | +| robot_{ID} | The robots you have unlocked | +| spider_{ID} | The spiders you have unlocked | +| swing_{ID} | The swings you have unlocked | +| jetpack_{ID} | The jetpacks you have unlocked | | special_{ID} | The PlayerStreaks you have unlocked| | c0_{ID} | The Colour1's you have unlocked | | c1_{ID} | The Colour2's you have unlocked | | death_{ID} | Unlocked Death Effects | +| shipstreak_{ID} | Unlocked ship streaks | diff --git a/docs/resources/client/level-components/color-string.md b/docs/resources/client/level-components/color-string.md index cd9d6ca64..ecc806dd5 100644 --- a/docs/resources/client/level-components/color-string.md +++ b/docs/resources/client/level-components/color-string.md @@ -1,6 +1,5 @@ # Client Color String Resource -## Color String The color string contains the information for the initial state of the colors being used in the level. Its raw representation is formatted as follows: `{color}|{color}|...`, where `color` is formatted as: diff --git a/docs/resources/client/level-components/enumerations.md b/docs/resources/client/level-components/enumerations.md new file mode 100644 index 000000000..1471ad570 --- /dev/null +++ b/docs/resources/client/level-components/enumerations.md @@ -0,0 +1,82 @@ +# Enumerations (Enums) + +Here you will find the enumerations that are related to the level string. + +## Gamemode + +| Key | Name | +|:-----|:-------| +| 0 | Cube | +| 1 | Ship | +| 2 | Ball | +| 3 | UFO | +| 4 | Wave | +| 5 | Robot | +| 6 | Spider | +| 7 | Swing | + +## Speed + +| Key | Name | Actual Speed (in Units/s) | +|:-----|:-------|:--------------------------| +| 0 | 1x | 251.16 | +| 1 | 0.5x | 311.58 | +| 2 | 2x | 387.42 | +| 3 | 3x | 468.00 | +| 4 | 4x | 576.00 | + +## Easing + +| Key | Name | +|:-----|:--------------------| +| 0 | None | +| 1 | Ease In Out | +| 2 | Ease In | +| 3 | Ease Out | +| 4 | Elastic In Out | +| 5 | Elastic In | +| 6 | Elastic Out | +| 7 | Bounce In Out | +| 8 | Bounce In | +| 9 | Bounce Out | +| 10 | Exponential In Out | +| 11 | Exponential In | +| 12 | Exponential Out | +| 13 | Sine In Out | +| 14 | Sine In | +| 15 | Sine Out | +| 16 | Back In Out | +| 17 | Back In | +| 18 | Back Out | + +## Pulse Mode + +| Key | Name | +|:-----|:-------| +| 0 | Color | +| 1 | HSV | + +## Pulse Target Type + +| Key | Name | +|:-----|:---------| +| 0 | Channel | +| 1 | Group | + +## Touch Toggle Mode + +| Key | Name | +|:-----|:-----------| +| 0 | None | +| 1 | Toggle On | +| 2 | Toggle Off | + +## Instant Count Comparison + +| Key | Name | +|:-----|:-----------| +| 0 | Equals | +| 1 | Larger | +| 2 | Smaller | + + diff --git a/docs/resources/client/level-components/guideline-string.md b/docs/resources/client/level-components/guideline-string.md index 99821541c..2be5e1e83 100644 --- a/docs/resources/client/level-components/guideline-string.md +++ b/docs/resources/client/level-components/guideline-string.md @@ -1,7 +1,6 @@ # Client Guideline String Resource -## Guideline String -The guideline string is the linearized string format of the guidelines of a level. It is contained within the [inner level string](inner-level-string.md). It is formatted as follows: +The guideline string is the linearized string format of the guidelines of a level. It is contained within the [inner level string](level-start.md). It is formatted as follows: `{guideline}~{guideline}~{guideline}~...`, where `guideline` is formatted as: @@ -9,11 +8,11 @@ The guideline string is the linearized string format of the guidelines of a leve E.g.: -`0.5~0.8~0.7~0.9~1~1` represents the following guidelines: +`0.5~0~0.7~0.9~1~1` represents the following guidelines: -- Guideline (`0.5~0.8`) +- Guideline (`0.5~0`) - Timestamp: 0.5 - - Color value: 0.8 (orange) + - Color value: 0 (orange) - Guideline (`0.7~0.9`) - Timestamp: 0.7 - Color value: 0.9 (yellow) @@ -26,13 +25,13 @@ The valid supported color values are the following: | Value | Color | |:------|:-------| -| 0.8 | orange | +| 0 | orange | | 0.9 | yellow | | 1.0 | green | ***Unexpected Behavior Information*** -- A color value of 0 will result in an orange guideline. +- A color value of 0.8 will result in an orange guideline. - Color values less than 0.8 will result in a transparent guideline. - Color values above 0.8 that do not match the yellow or the green guideline color values will result in an orange guideline. diff --git a/docs/resources/client/level-components/inner-level-string.md b/docs/resources/client/level-components/inner-level-string.md deleted file mode 100644 index 800dad35d..000000000 --- a/docs/resources/client/level-components/inner-level-string.md +++ /dev/null @@ -1,96 +0,0 @@ -# Client Inner Level String Resource - -## Inner Level String -The inner level string consists of information about the starting state of the level and the objects it contains. It is encoded in [base64](). Its raw representation is formatted as follows: - -`{level_start};{object_string}`, where - -- `level_start` is the level start object, -- `object_string` is the [object string](). - -Theoretically, the inner level string in its entirety is the object string, however the level start object is treated specially, unlike every other object, and doesn't even have an ID. - -## Level Start Object -The level start object is still an object and formated exactly like a normal [level object](level-object.md), but has the following properties instead: - -| Key | Name | Type | Description | -|:-----|:-----------------------|:--------------------------------------------|:---------------------------------------------------------------------------------------------------------------| -| kA1 | AudioTrack | **Integer** | The audio track which the level uses | -| kA2 | Gamemode | **[Gamemode](enumerations.md)** | the gamemode the player starts with | -| kA3 | Mini Mode | **bool** | determines whether the player starts off as mini Mode | -| kA4 | Speed | **[Speed](enumerations.md)** | the speed of the level at the starts | -| kA6 | Background Texture ID | **integer** | the ID of the background texture that is being used in the level
(enumerated in the same order as appears) | -| kA7 | Ground Texture ID | **integer** | the ID of the ground texture that is being used in the level
(enumerated in the same order as appears) | -| kA8 | Dual Mode | **bool** | determines whether the player starts off in dual Mode | -| kA9 | Level/Start Pos Object | **bool** | determines whether this object represents a Level Start or a Start Pos object (true for the latter) | -| kA10 | 2-Player Mode | **bool** | determines whether 2-Player Mode is toggled on for this level | -| kA11 | Flip Gravity | **bool** | determines whether the player starts off in flipped Gravity | -| kA13 | Song Offset | **float** | the song offset in seconds from which the level begins | -| kA14 | Guidelines | **[Guideline String](guideline-string.md)** | the editor song guidelines of the level | -| kA15 | Fade In | **bool** | determines whether the song will fade in as soon as the level starts | -| kA16 | Fade Out | **bool** | determines whether the song will fade in as soon as the level ends | -| kA17 | Ground Line | **integer** | the ID of the ground line that is being used in the level | -| kA18 | Font | **integer** | the ID of the font that is being used in the level | -| kA22 | Platformer Mode | **Bool** | If platformer Mode is toggled on or off (currently only available in the December 2019 2.2 leaks) | -| kS38 | Colors | **[Color String](color-string.md)** | the color channels that are being used in this level | -| kS39 | Color Page | **integer** | the color page which was last displayed in the color channel display window | - -***Pre-2.0 Keys*** - -The following keys were valid prior to 2.0 and are deprecated, since they are included in the new `kS38` key. They all represented a color channel that is now indexed through the respective color channel ID. - -| Key | Name | Color Channel ID | -|:-----|:-------------|:-----------------| -| kS29 | BG Color | 1000 | -| kS30 | Ground Color | 1001 | -| kS31 | Line Color | 1002 | -| kS32 | Object Color | 1004 | -| kS33 | Color 1 | 1 | -| kS34 | Color 2 | 2 | -| kS35 | Color 3 | 3 | -| kS36 | Color 4 | 4 | -| kS37 | 3DL Color | 1003 | - -***Pre-1.9 Keys*** - -The following keys were valid prior to 1.9 and were deprecated as of 1.9, since they were included in the keys `kS29`-`kS33`. Each color, instead of using one key, used 3 keys to represent Red, Green, and Blue values, respectively. In 1.7 and 1.8, the game used a fourth key to determine if a color channel used a player color, and which player color it would use. - -| Key(s) | Name | Key in 1.9 | -|:----------|:--------------------|:-----------| -| kS1-kS3 | BG Color | kS29 | -| kS4-kS6 | Ground Color | kS30 | -| kS7-kS9 | Line Color | kS31 | -| kS10-kS12 | Object Color | kS32 | -| kS13-kS15 | Obj-2 Color | kS33 | -| kS16 | BG Player Color | kS29 | -| kS17 | Ground Player Color | kS30 | -| kS18 | Line Player Color | kS31 | -| kS19 | Object Player Color | kS32 | -| kS20 | Obj-2 Player Color | kS33 | - -Keys `kS16`-`kS20` used to determine the player color that was being used and their values were interpreted according to the following table: - -| Value | Color | -|:------|:---------------| -| 0 | Neither | -| 1 | Player Color 1 | -| 2 | Player Color 2 | - -### Start Pos Object -The Start Pos object has the same special properties the level start object has, with a few not working. `kA9` must be set to `1` in the case that the object is indeed a Start Pos. - -Specifically, the only functional properties in a Start Pos object are the ones involving gameplay state, thus excluding visual-related ones (BG/ground textures, font, colors, etc.) - -Confirmed properties that do not work in the Start Pos: - -| Key | -|:-----| -| kA6 | -| kA7 | -| kA10 | -| kA13 | -| kA14 | -| kA15 | -| kA16 | -| kA18 | -| kS* | diff --git a/docs/resources/client/level-components/level-colors.md b/docs/resources/client/level-components/level-colors.md index 019e0e7d2..022aca1ee 100644 --- a/docs/resources/client/level-components/level-colors.md +++ b/docs/resources/client/level-components/level-colors.md @@ -39,7 +39,7 @@ This class contains a dynamic color copied from another color channel. This colo | Copy Opacity | **bool** | This determines whenever CopyColor should also copy the opacity belonging to the color channel in `Copy Color ID` | | Opacity | **float** | The alpha component of the Copy Color. If `Copy Opacity` is true. This property is ignored. | | Blending | **bool** | The blending property of the CopyColor since it cannot be copied | -| Copy HSV | **[HSV](resources/client/level-components/level-object.md?id=object-string)** | The HSV property that changes the color's tint depending on the value | +| Copy HSV | **[HSV](resources/client/level-components/level-string.md?id=object-string)** | The HSV property that changes the color's tint depending on the value | ### Determining which class is used Here is a simple JavaScript function that determines what color class the color object has: @@ -64,18 +64,19 @@ Here are all of the different color id's: | `1 - 999` | **Custom colors** | These are the colors that are avalible for the creator to use | | `1000` | **BG** | This is the color of the background | | `1001` | **G1** | This is the primary color of the ground | -| `1002` | **LINE** | This is the color of the ground line | +| `1002` | **Line** | This is the color of the ground line | | `1003` | **3DL** | This is the color of the 3D line objects | -| `1004` | **OBJ** | This is the OBJ color | +| `1004` | **Obj** | This is the OBJ color | | `1005` | **P1** | This is the static color channel refering to the primary color of the player's icon | | `1006` | **P2** | This is the static color channel refering to the secondary color of the player's icon | | `1007` | **LBG** | This is the static color channel that is a lighter version of `BG` | | `1009` | **G2** | This is the secondary color of the ground | -| `1010` | **BLACK** | This is the static color channel which is always `r: 0, g: 0, b: 0`. Used in saws that are black by default | +| `1010` | **Black** | This is the static color channel which is always `r: 0, g: 0, b: 0`. Used in saws that are black by default | +| `1011` | **White** | This is the static color channel which is always `r: 255, g: 255, b: 255`. | +| `1012` | **Lighter** | A lighter version of the primary color in objects. Used in the white small blocks found in `build tab 2 on page 6`. | +| `1013` | **MG** | This is the primary color of the middleground | +| `1014` | **MG2** | This is the secondary color of the middleground | -### Undiscovered color channel IDs -`WHITE`: Static color that is always `r: 255, g: 255, b: 255` -`LIGHTER`: A lighter version of the primary color in objects. Used in the white small blocks found in `build tab 2 on page 6`. ### 1.9 color channel ID's GD's 1.9 version used a different ID scheme to identify color channels. In 2.0+, these IDs are still present, but only used in the legacy `1.9 Color Channel ID` property of 1.9 objects. They are as follows: diff --git a/docs/resources/client/level-components/level-start.md b/docs/resources/client/level-components/level-start.md new file mode 100644 index 000000000..30dfdecec --- /dev/null +++ b/docs/resources/client/level-components/level-start.md @@ -0,0 +1,125 @@ +# Client Level Start String + +The start level string consists of information about the starting state of the level and the objects it contains. It is encoded in [base64](). Its raw representation is formatted as follows: + +`{level_start};{object_string}`, where + +- `level_start` is the level start object, +- `object_string` is the [object string](). + +Theoretically, the start level string in its entirety is the object string, however the level start object is treated specially, unlike every other object, and doesn't even have an ID. + +## Level Start Object +The level start object has the following properties, formatted as `{key},{value},{key},{value}` + +| Key | Name | Type | Description | +|:-----|:-----------------------|:--------------------------------------------|:---------------------------------------------------------------------------------------------------------------| +| kA1 | AudioTrack | **Integer** | The audio track which the level uses | +| kA2 | Gamemode | **[Gamemode](resources/client/level-components/enumerations?id=gamemode)** | the gamemode the player starts with | +| kA3 | Mini Mode | **bool** | determines whether the player starts off as mini Mode | +| kA4 | Speed | **[Speed](resources/client/level-components/enumerations?id=speed)** | the speed of the level at the start | +| kA5 | Obj-2 Blending | **bool** | (deprecated since 1.9) Whether Object-2 (color channel 1) uses blending or not | +| kA6 | Background Texture ID | **integer** | the ID of the background texture that is being used in the level
(enumerated in the same order as appears) | +| kA7 | Ground Texture ID | **integer** | the ID of the ground texture that is being used in the level
(enumerated in the same order as appears) | +| kA8 | Dual Mode | **bool** | determines whether the player starts off in dual Mode | +| kA9 | Level/Start Pos Object | **bool** | determines whether this object represents a Level Start or a Start Pos object (true for the latter) | +| kA10 | 2-Player Mode | **bool** | determines whether 2-Player Mode is toggled on for this level | +| kA11 | Flip Gravity | **bool** | determines whether the player starts off in flipped Gravity | +| kA12 | Color3 Blending (UNUSED)| **bool** | Unused, apparently meant to determine whether color channel 3 is blending or not | +| kA13 | Song Offset | **float** | the song offset in seconds from which the level begins | +| kA14 | Guidelines | **[Guideline String](resources/client/level-components/guideline-string.md)** | the editor song guidelines of the level | +| kA15 | Fade In | **bool** | determines whether the song will fade in as soon as the level starts | +| kA16 | Fade Out | **bool** | determines whether the song will fade in as soon as the level ends | +| kA17 | Ground Line | **integer** | the ID of the ground line that is being used in the level | +| kA18 | Font | **integer** | the ID of the font that is being used in the level | +| kA20 | Reverse Gameplay | **bool** | Whether gameplay is reversed at the start | +| kA22 | Platformer Mode | **Bool** | If the level is in Classic Mode (0) or Platformer Mode (1) | +| kA25 | Middleground Texture ID| **integer** | the ID of the middleground texture that is being used in the level
(enumerated in the same order as appears)| +| kA27 | Allow Multi-Rotation | **bool** | (Compatibility setting) Whether you can run multiple rotation actions on the same target group or not | +| kA28 | Mirror Mode | **bool** | Whether the screen is mirrored at the start | +| kA29 | Rotate Gameplay | **bool** | Whether gameplay is rotated at the start | +| kA31 | Enable Player Squeeze | **bool** | (Compatibility setting) Whether the player can be killed by being squeezed between 2 objects | +| kA32 | Fix Gravity Bug | **bool** | (Compatibility setting) Whether the bug where flipped gravity behaves differently from normal is fixed | +| kA33 | Fix Negative Scale | **bool** | (Compatibility setting) Whether negatively scaled objects have correct hitboxes (1) or not (0) | +| kA34 | Fix Robot Jump | **bool** | (Compatibility setting) Whether the robot can jump very high with a pad (0) or not (1) | +| kA36 | Spawn Group | **integer** | The group at which the player spawns | +| kA37 | Dynamic Level Height | **bool** | (Compatiblity setting) Whether the level ceiling is affected by higher objects | +| kA38 | Sort Groups | **bool** | (Compatibility setting) Whether groups are spawn triggered left to right or not | +| kA39 | Fix Radius Collision | **bool** | (Compatibility setting) Whether the player's hitbox has a circle for circular hazard collision or not | +| kA40 | Enable 2.2 Changes | **bool** | (Compatibility setting) Whether miscellaneous 2.2 changes are enabled or not | +| kA41 | Allow Static-Rotate | **bool** | (Compatibility setting) Whether static objects can be rotated or not | +| kA42 | Reverse Sync | **bool** | (Compatibility setting) Whether the player's speed is changed when reversing to preserve music sync | +| kA43 | No Time Penalty | **bool** | Whether you get a point penalty for time (0) or not (1) in platformer | +| kA45 | Decrease Boost Slide | **bool** | (Compatibility setting) Whether the player receives a lesser boost in Platformer when boosted by dash orb or moving platforms| +| kS38 | Colors | **[Color String](resources/client/level-components/color-string.md)** | the color channels that are being used in this level | +| kS39 | Color Page | **integer** | the color page which was last displayed in the color channel display window | + +***Pre-2.0 Keys*** + +The following keys were valid prior to 2.0 and are deprecated, since they are included in the new `kS38` key. They all represented a color channel that is now indexed through the respective color channel ID. + +| Key | Name | Color Channel ID | +|:-----|:-------------|:-----------------| +| kS29 | BG Color | 1000 | +| kS30 | Ground Color | 1001 | +| kS31 | Line Color | 1002 | +| kS32 | Object Color | 1004 | +| kS33 | Color 1 | 1 | +| kS34 | Color 2 | 2 | +| kS35 | Color 3 | 3 | +| kS36 | Color 4 | 4 | +| kS37 | 3DL Color | 1003 | + +***Pre-1.9 Keys*** + +The following keys were valid prior to 1.9 and were deprecated as of 1.9, since they were included in the keys `kS29`-`kS33`. Each color, instead of using one key, used 3 keys to represent Red, Green, and Blue values, respectively. In 1.7 and 1.8, the game used a fourth key to determine if a color channel used a player color, and which player color it would use. + +| Key(s) | Name | Key in 1.9 | +|:----------|:--------------------|:-----------| +| kS1-kS3 | BG Color | kS29 | +| kS4-kS6 | Ground Color | kS30 | +| kS7-kS9 | Line Color | kS31 | +| kS10-kS12 | Object Color | kS32 | +| kS13-kS15 | Obj-2 Color | kS33 | +| kS16 | BG Player Color | kS29 | +| kS17 | Ground Player Color | kS30 | +| kS18 | Line Player Color | kS31 | +| kS19 | Object Player Color | kS32 | +| kS20 | Obj-2 Player Color | kS33 | + +Keys `kS16`-`kS20` used to determine the player color that was being used and their values were interpreted according to the following table: + +| Value | Color | +|:------|:---------------| +| 0 | Neither | +| 1 | Player Color 1 | +| 2 | Player Color 2 | + +### Start Pos Object +The Start Pos object has the same special properties the level start object has, with a few not working and with a few exclusive ones, listed at the bottom. `kA9` must be set to `1` in the case that the object is indeed a Start Pos. + +Specifically, the only functional properties in a Start Pos object are the ones involving gameplay state, thus excluding visual-related ones (BG/ground textures, font, colors, etc.) + +Confirmed properties that do not work in the Start Pos: + +| Key | +|:-----| +| kA5 | +| kA6 | +| kA7 | +| kA10 | +| kA13 | +| kA14 | +| kA15 | +| kA16 | +| kA18 | +| kS* | + +Since 2.2, the Start Pos object has its own unique properties that do not work in the Level Start object. This is a table of all of them: + +| Key | Name | Type | Description | +|:-----|:-----------------------|:--------------------------------------------|:---------------------------------------------------------------------------------------------------------------| +| kA19 | Target Order | **Integer** | The Target Order property of the Start Pos | +| kA21 | Disable | **bool** | Whether the Start Pos is disabled | +| kA26 | Target Order | **Integer** | The Target Channel property of the Start Pos | +| kA35 | Reset Camera | **bool** | Whether the camera is reset when playing from this Start Pos | diff --git a/docs/resources/client/level-components/level-object.md b/docs/resources/client/level-components/level-string.md similarity index 85% rename from docs/resources/client/level-components/level-object.md rename to docs/resources/client/level-components/level-string.md index e68ca8687..b842b2b97 100644 --- a/docs/resources/client/level-components/level-object.md +++ b/docs/resources/client/level-components/level-string.md @@ -1,9 +1,6 @@ -# Client Level Object Resource +# Client Level String Resource -## Level Object -A level object is an object within a Geometry Dash level, containing data about its behavior. - -### Object String +## Object String The object string is contained within the [inner level string](). It is formatted as follows: `{object};{object};{object};...`, where `object` is formatted as: @@ -41,8 +38,10 @@ Property keys reflect the keys found in the following table, whereas property va | **[HSV]()** | `{h}a{s}a{v}a{s_checked}a{v_checked}`, where each name reflects the respective HSV property | | **string** | raw string without quotation marks (e.g. `ExampleString`) | -### Level Object Data -**Level Object Structure** +### Level String Data +**Level String Structure** + +2.2 adds a ton of new object properties. Most of the object properties can be found [here](https://flowvix.github.io/gd-info-explorer/). You can access the old table of object properties (with all 2.1 properties and a few 2.2 properties) below if you need it for whatever reason. | Key | Name/Value | Type | Description | |:----|:-------------------------------------|:------------------------------------------------|:-----------------------------------------------------------------------------------| @@ -55,7 +54,7 @@ Property keys reflect the keys found in the following table, whereas property va | 7 | Red | **integer** | the Red component of the color in a trigger | | 8 | Green | **integer** | the Green component of the color in a trigger | | 9 | Blue | **integer** | the Blue component of the color in a trigger | -| 10 | Duration | **float** | the duration of an effect in a trigger | +| 10 | Duration | **float** | the duration of an effect in a trigger. In the random trigger, this is the chance instead| | 11 | Touch Triggered | **bool** | the Touch Triggered property of a trigger | | 12 | Secret Coin ID | **integer** | the ID of a Secret Coin | | 13 | Special Object Checked | **bool** | the checked property of some special objects (gamemode, speed, dual portals, etc.) | @@ -133,16 +132,31 @@ Property keys reflect the keys found in the following table, whereas property va | 96 | Disable Glow | **bool** | the Disable Glow property of the object | | 97 | Custom Rotation Speed | **float** | the Custom Rotation Speed property of the rotating object in degrees per second | | 98 | Disable Rotation | **bool** | the Disable Rotation property of the rotating object | -| 99 | Multi Activate (Orbs) | **bool** | the Multi Activate property of Orbs | +| 99 | Multi Activate (Orbs) | **bool** | the Multi Activate property of Orbs | | 100 | Enable Use Target | **bool** | the Enable Use Target property of the Move trigger | | 101 | Target Pos Coordinates | **[Target Pos Coordinates](enumerations.md)** | the Target Pos Coordinates property of the Move trigger | | 102 | Editor Disable | **bool** | the Editor Disable property of the Spawn trigger | | 103 | High Detail | **bool** | the High Detail property of the object | -| 104 | Multi Activate (Triggers) | **bool** | The Multi Activate Property of Triggers | +| 104 | Multi Activate (Triggers) | **bool** | The Multi Activate Property of Triggers | | 105 | Max Speed | **float** | the Max Speed property of the Follow Player Y trigger | | 106 | Randomize Start | **bool** | the Randomize Start property of the animated object | | 107 | Animation Speed | **float** | the Animation Speed property of the animated object | | 108 | Linked Group ID | **integer** | the Linked Group ID property of the object | +| 110 | Exit Static | **bool** | the Exit Static property of the Static Camera trigger | +| 111 | Free Mode | **bool** | the Free Mode property of the Camera Mode trigger or portal | +| 112 | Edit Camera Settings | **bool** | the Edit Camera Settings property of the Camera Mode trigger or portal | +| 113 | Easing (Free Mode) | **integer** | the Easing property in a Camera Mode trigger or portal | +| 114 | Padding | **float** | the Paddding property in a Camera Mode trigger or portal | +| 115 | ord | **integer** | the Ord property of the object | +| 116 | No Effects | **bool** | Whether the object doesn't emit any special effects (e.g. portal flash) | +| 117 | Reverse | **bool** | Whether the orb/pad reverses your direction | +| 120 | Time Mod | **float** | The time modifier in the Time Warp trigger | +| 121 | No Touch | **bool** | Whether the object has no hitbox | +| 128 | Scale X | **float** | The X Scale of the object | +| 128 | Scale X | **float** | The X Scale of the object | +| 131 | Warp Y angle | **float** | The Y angle warp value of the object | +| 132 | Warp X angle | **float** | The X angle warp value of the object | + ***Undiscovered Existing Features*** @@ -152,6 +166,8 @@ The following features are discovered in current local save files, however their |:----|:------------|:---------------------------------------------------------------------------------------------------| | 36 | **bool** | suspected to be handling whether an object's X position is locked and unaffected by a Move trigger | | 74 | **bool**(?) | only found in the Follow Player Y trigger | +| 155 | **integer** | Suspected to be something related to optimizing colors. Appears on all objects | +| 156 | **integer** | Same as 155 | ***Potentially Discarded Features*** diff --git a/docs/resources/client/level-components/particle-string.md b/docs/resources/client/level-components/particle-string.md new file mode 100644 index 000000000..cdcc9660b --- /dev/null +++ b/docs/resources/client/level-components/particle-string.md @@ -0,0 +1,86 @@ +# Particle String + +The particle string is a long string of values separated by the `a` character that defines how particle systems in-game such as the particle object behave. Here is an example of a particle string: + +``` +30a-1a1.3a0.2a20a90a0a10a5a20a20a0a0a8a0a0a0a4a1a0a0a0a0a1a0a1a0a1a0a1a1a0a0a0 a0a0.784314a0a1a0a1a0a0.27a0a0.27a0a0a0a0a0a0a0a0a2a1a0a0a0a0a0a0a0.25a0a0a0a0a0a0a0a0a0a0a0 +``` + +(this is the particle string of the particles around the Mythic rating fire) + +## The Values + +| Index | Name | Description | +| :-----| :------------------------| :------------------------------------------------------------------------| +| 1 | Max Particles | The maximum number of particles to be generated | +| 2 | Duration | The animation duration. `-1` for indefinite | +| 3 | Lifetime | How long the particles last. `-1` for indefinite | +| 4 | Lifetime +- | Random offset for Lifetime | +| 5 | Emission | Particle spawn rate. `-1` for max | +| 6 | Angle | In what direction the particles are generated towards | +| 7 | Angle +- | Random offset for Angle | +| 8 | Speed | How fast the particles are | +| 9 | Speed +- | Random offset for Speed | +| 10 | PosVar X | The horizontal particle spawn area | +| 11 | PosVar Y | The vertical particle spawn area | +| 12 | Gravity X | The horizontal gravitational pull | +| 13 | Gravity Y | The vertical gravitational pull | +| 14 | AccelRad | Radial particle acceleration | +| 15 | AccelRad +- | Random offset for AccelRad | +| 16 | AccelTan | Tangential particle acceleration | +| 17 | AccelTan +- | Random offset for AccelTan | +| 18 | StartSize | The scale the particles start at | +| 19 | StartSize +- | Random offset for StartSize | +| 20 | StartSpin | The rotation of the particles when they spawn | +| 21 | StartSpin +- | Random offset for StartSpin | +| 22 | StartR | The red color value the particles start at | +| 23 | StartR +- | Random offset for StartR | +| 24 | StartG | The green color value the particles start at | +| 25 | StartG +- | Random offset for StartG | +| 26 | StartB | The blue color value the particles start at | +| 27 | StartB +- | Random offset for StartB | +| 28 | StartA | The opacity the particles start at | +| 29 | StartA +- | Random offset for StartA | +| 30 | EndSize | The scale the particles end at | +| 31 | EndSize +- | Random offset for EndSize | +| 32 | EndSpin | The rotation of the particles at the end of their lifetime | +| 33 | EndSpin +- | Random offset for EndSpin | +| 34 | EndR | The red color value the particles end at | +| 35 | EndR +- | Random offset for EndR | +| 36 | EndG | The green color value the particles end at | +| 37 | EndG +- | Random offset for EndG | +| 38 | EndB | The blue color value the particles end at | +| 39 | EndB +- | Random offset for EndB | +| 40 | EndA | The opacity the particles end at | +| 41 | EndA +- | Random offset for EndA | +| 42 | Fade in | Fade time for particles to appear | +| 43 | Fade in +- | Random offset for Fade in | +| 44 | Fade out | Fade time for particles to disappear | +| 45 | Fade out +- | Random offset for Fade out | +| 46 | StartRad | Start radius of the particles | +| 47 | StartRad +- | Random offset for StartRad | +| 48 | EndRad | End radius of the particles | +| 49 | EndRad +- | Random offset for EndRad | +| 50 | RotSec | Rotation per second | +| 51 | RotSec +- | Random offset for RotSec | +| 52 | Mode | 0 for Gravity, 1 for Radius | +| 53 | Mode 2 | 0 for Free, 1 for Relative, 2 for Grouped | +| 54 | Additive | Whether the particles color is blending or not | +| 55 | Start spin = end | Whether the particles have the same rotation through their lifetime | +| 56 | Start rot is dir | Whether the particles' direction will be their starting rotation | +| 57 | Dynamic rotation | Whether particles rotate towards the direction they are going towards | +| 58 | Texture | The particle texture. 0 for Square, and the ID matches the order in the particle object | +| 59 | Uniform obj color | Whether the particles color is the same through their lifetime | +| 60 | FrictionP | Particle friction | +| 61 | FrictionP +- | Random offset for FrictionP | +| 62 | Respawn | Particle respawning rate | +| 63 | Respawn +- | Random offset for Respawn | +| 64 | Order Sensitive | The newest particles are layered on top of the older ones | +| 65 | Start size = end | Whether the particles have the same size through their lifetime | +| 66 | Start rad = end | Whether the particles have the same radius through their lifetime | +| 67 | StartRGB Var Sync | Whether to sync the Start R/G/B values | +| 68 | EndRGB Var Sync | Whether to sync the End R/G/B values | +| 69 | FrictionS | Particle speed friction | +| 70 | FrictionS +- | Random offset for FrictionS | +| 71 | FrictionR | Particle rotation friction | +| 72 | FrictionR +- | Random offset for FrictionR | diff --git a/docs/resources/client/level.md b/docs/resources/client/level.md index 0ffb5c189..e67472ecf 100644 --- a/docs/resources/client/level.md +++ b/docs/resources/client/level.md @@ -9,37 +9,37 @@ A level is a playable object in Geometry Dash, namely coming with data that expl **Level Structure** | Key | Name/Value | Type | Description | -| :-- | :----------------------------- | :--------------------------------------------------------------- | :------------------------------------------------------------------------------- | -| k1 | Level ID | **integer** | the id of the level | -| k2 | Level Name | **string** | the name of the level | -| k3 | Description | **string** | the level description, encoded in [base64](https://en.wikipedia.org/wiki/Base64) | -| k4 | Inner Level String | **[inner level string](/resources/client/level-components/inner-level-string.md)** | the inner level string, or the playable level | -| k5 | Creator | **[user](./user.md)Name** | the name of the level creator | -| k6 | UserID | **integer** | The UserID of the level Creator | -| k7 | level difficulty | **integer** | the difficulty the level has | -| k8 | Official Song ID | **[Audio Track](/reference?id=audio-track)** | the official Song ID (if used) | -| k9 | Rating | **integer** | The rating a level has | -| k10 | RatingSum | **integer** | the sum of all the ratings a level has | -| k11 | Downloads | **integer** | the amount of times the level's been downloaded | -| k12 | setCompletes | **integer** | level completions for that particular level | -| k13 | isEditable | **Bool** | used to stop people editing online and Official levels | -| k14 | Verified | **bool** | whether the level is verified or not | -| k15 | Uploaded | **bool** | whether the level is uploaded to the server or not | -| k16 | Level Version | **integer** | the version of the level | -| k17 | Game Version | **integer** | The Games Version | -| k18 | Attempts | **integer** | the number of attempts that are made to this level | -| k19 | Normal Mode Percentage | **integer** | the max percentage that has been achieved in normal mode in this level | -| k20 | Practice Mode Percentage | **integer** | the max percentage that has been achieved in practice mode in this level | -| k21 | levelType | **Integer** | The Level Type (1 = Official, 2 = Local, 3 = Saved, 4 = Online) | -| k22 | Like Rating | **integer** | the level's like rating (`likes - dislikes`) | -| k23 | Length | **[Length](enumerations.md)** | the level's length | -| k24 | Dislikes | **integer** | how many dislikes a level has (unused) | -| k25 | isDemon | **Bool** | if the level is demon or not | -| k26 | Stars | **integer** | the stars the level is worth | -| k27 | FeatureScore | **integer** | A featured levels Feature Score | -| k33 | Auto | **Bool** | If the level is auto | -| k34 | Replay Data | **[Gziped String](/topics/encryption/zip.md)** | Contains a Gzipped String which contains replay data for levels | -| k35 | isPlayable? | **Bool** | if the level is downloaded (honestly not much is known about this) | +| :--- | :----------------------------- | :--------------------------------------------------------------- | :------------------------------------------------------------------------------- | +| k1 | Level ID | **integer** | the id of the level | +| k2 | Level Name | **string** | the name of the level | +| k3 | Description | **string** | the level description, encoded in [base64](https://en.wikipedia.org/wiki/Base64) | +| k4 | Start Level Object | **[Start Object String](/resources/client/level-components/level-start.md)** | the inner level string, or the playable level | +| k5 | Creator | **[user](./user.md)Name** | the name of the level creator | +| k6 | UserID | **integer** | The UserID of the level Creator | +| k7 | level difficulty | **integer** | the difficulty the level has | +| k8 | Official Song ID | **[Audio Track](/reference?id=audio-track)** | the official Song ID (if used) | +| k9 | Rating | **integer** | The rating a level has | +| k10 | RatingSum | **integer** | the sum of all the ratings a level has | +| k11 | Downloads | **integer** | the amount of times the level's been downloaded | +| k12 | setCompletes | **integer** | level completions for that particular level | +| k13 | isEditable | **Bool** | used to stop people editing online and Official levels | +| k14 | Verified | **bool** | whether the level is verified or not | +| k15 | Uploaded | **bool** | whether the level is uploaded to the server or not | +| k16 | Level Version | **integer** | the version of the level | +| k17 | Game Version | **integer** | The Games Version | +| k18 | Attempts | **integer** | the number of attempts that are made to this level | +| k19 | Normal Mode Percentage | **integer** | the max percentage that has been achieved in normal mode in this level | +| k20 | Practice Mode Percentage | **integer** | the max percentage that has been achieved in practice mode in this level | +| k21 | levelType | **Integer** | The Level Type (1 = Official, 2 = Local, 3 = Saved, 4 = Online) | +| k22 | Like Rating | **integer** | the level's like rating (`likes - dislikes`) | +| k23 | Length | **[Length](enumerations.md)** | the level's length | +| k24 | Dislikes | **integer** | how many dislikes a level has (unused) | +| k25 | isDemon | **Bool** | if the level is demon or not | +| k26 | Stars | **integer** | the stars the level is worth | +| k27 | FeatureScore | **integer** | A featured levels Feature Score | +| k33 | Auto | **Bool** | If the level is auto | +| k34 | Replay Data | **[Gziped String](/topics/encryption/zip.md)** | Contains a Gzipped String which contains replay data for levels | +| k35 | isPlayable? | **Bool** | if the level is downloaded (honestly not much is known about this) | | k36 | Jumps | **integer** | total Jumps on a level | | k37 | required coins | **Integer** | coins required to unlock an official level | | k38 | isUnlocked | **Bool** | is Official level Unlocked | @@ -62,16 +62,16 @@ A level is a playable object in Geometry Dash, namely coming with data that expl | k62 | Second Coin Acquired | **bool** | whether the second coin is acquired during verification | | k63 | Third Coin Acquired | **bool** | whether the third coin is acquired during verification | | k64 | Total Coins | **Integer** | How many Coins the level has | -| k65 | areCoinsVerified | **Bool** | denotes if the coins are verified or not | +| k65 | areCoinsVerified | **Bool** | denotes if the coins are verified or not | | k66 | Requested Stars | **integer** | the requested stars during publication of the level | -| k67 | [Capacity String](/resources/client/level-components/capacity-string.md) | **String** | Contains batch information about levels | +| k67 | [Capacity String](/resources/client/level-components/capacity-string.md)| **String** | Contains batch information about levels | | k68 | triggeredAntiCheat | **Bool** | if you trigger the anticheat when beating demons | | k69 | High Object Count | **Bool** | If a level has a high object count | -| k71 | Mana Orb Percentage | **integer** | the percentage up until the orb reward has been granted | +| k71 | Mana Orb Percentage | **integer** | the percentage up until the orb reward has been granted | | k72 | hasLowDetailMode | **Bool** | If a level has LDM | | k73 | toggleLDM | **Bool** | If a LDM is Enabled | | k74 | timelyID | **integer** | the timelyID for a level | -| k75 | isEpic | **Bool** | if a level has been awarded an epic rating | +| k75 | epicRating | **integer** | The level's epic rating. 0 for None, 1 for Epic, 2 for Legendary, 3 for Mythic | | k76 | demon type | **integer** | Demon Type Enum | | k77 | isGauntlet | **Bool** | is the level in a gauntlet | | k78 | isAltGame | **Bool** | Levels that were completed on the free games | @@ -85,8 +85,40 @@ A level is a playable object in Geometry Dash, namely coming with data that expl | k86 | Player Time | **integer** | the amount of time on a players best attempt | | k87 | level Seed | **[LevelScoreSeed](/topics/encryption/chk?id=level-leaderboard)**| Contains info to verify the integrity of levelScores | | k88 | Level Progress | **String** | Contains a list of high score differences seperated by a `,` | -| k89 | vfDChk | **Bool** | used to check for level completion | -| k90 | Leaderboard percentage | **integer** | Contains the percentage for level Leaderboards | +| k89 | vfDChk | **Bool** | used to check for level completion | +| k90 | Leaderboard percentage | **integer** | Contains the percentage for level Leaderboards | +| k95 | Verification Time | **integer** | Verification time in physics steps (240 steps per second) | +| k104 | Song list | **string** | List of song IDs separated by `,` | +| k105 | SFX list | **string** | List of SFX IDs separated by `,` | +| k107 | Best Time | **integer** | Your best time on the level in milliseconds | +| k108 | Best Points | **integer** | Your point highscore on the level | +| k109 | Local Best Times | **integer** | List of your best times on the level in milliseconds, separated by `,` | +| k110 | Local Best Points | **integer** | List of your point highscores on the level, separated by `,` | +| k111 | Platformer Seed | **integer** | Checksum for platformer completions, see below for code | +| k112 | No Shake | **bool** | Whether the player has disabled shake for this level | + +The classic level seed is generated like this (please note you need to add another `1482` when submitting the level score): + +```py +def generate_classic_leaderboard_seed( + jumps: int, percentage: int, seconds: int, has_played: bool = True +) -> int: + + return ( + 1482 * has_played + + (jumps + 3991) * (percentage + 8354) + + ((seconds + 4085) ** 2) - 50028039 + ) +``` + +The platformer seed is generated like this: + +```py +def generatePlatformerHash(bestTime, bestPoints): + number = (((bestTime + 7890) % 34567) * 601 + ((abs(bestPoints) + 3456) % 78901) * 967 + 94819) % 94433 + return ((number ^ number >> 16) * 829) % 77849 + +``` **Last Editor State Key/Value Pairs** @@ -118,3 +150,5 @@ The build tab page is 5, and the button settings are 6x2 (default), meaning the | k92 | **integer** | | | k93 | **Bool** | unlimited Objects? | | k94 | **Bool** | Platformer? | +| k101 | **string** | seems to be 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0. Seems to also correlate with k47 | +| k106 | **integer** | corresponds to key 54 on the servers | diff --git a/docs/resources/client/musiclibrary.md b/docs/resources/client/musiclibrary.md new file mode 100644 index 000000000..725290dd3 --- /dev/null +++ b/docs/resources/client/musiclibrary.md @@ -0,0 +1,68 @@ +# Music Library format + +The Music Library is stored in your save directory as `musiclibrary.dat`. To learn how to retreive it from the servers, refer to [this page](/endpoints/songs/musiclibrary.md). + +To decode musiclibrary.dat, you need to [Base64](/topics/encryption/base64.md) URL-Safe decode it and [Zlib](/topics/encryption/zip.md) inflate it. + +The Music library is split into 4 parts: + +``` +{version}|{artists}|{songs}|{tags} +``` + +## Version + +This is just the music library version. Not much more to say here. + +## Artists + +These are all of the artists, separated by `;` in the following format: + +`{artistID},{name},{website},{youtubeChannel}` + +The website is the full link, but it is URL encoded. The YouTube channel link is only the part that comes after `https://youtube.com/channel/`. If either of these are missing, there will be a space character in place of them. + +Here is an example of an artist: + +``` +286,F-777,https%3A%2F%2Fjessevalentinemusic.bandcamp.com,UC6MNIegxWVDe6tOjL92QkUw +``` + +## Songs + +This is where the old (2.205-) and new (2.206+) music libraries diverge. In both libraries, the songs are separated with `;`. However, the new music library has each song in this format: + +`{id},{name},{artistID},{filesize},{duration},{tags},{musicPlatform},{extraArtists},{externalLink},{newButton},{priorityOrder},{songNumber}` + +Meanwhile the old music library has each song in this format: + +`{id},{name},{artistID},{filesize},{duration},{tags}` + +Here's an explanation for each key: + +| Name | Type | Description | +| :------------ | :-------- | :---------------------------------------------------------------------------------------------------------- | +| id | integer | The song ID | +| name | string | The song name | +| artistID | integer | The primary artist ID | +| filesize | integer | The file size of the song in bytes | +| duration | integer | The song's duration in seconds | +| tags | string | The song's tags, separated by dots. The string also has extra dots at the start and end for unknown reasons | +| musicPlatform | integer | The platform this song comes from. 0 for None, 1 for NCS. | +| extraArtists | string | The IDs of the artists that contributed to the song, separated with `.` | +| externalLink | string | The external link to the song (NOT the GD download link) | +| newButton | boolean | Whether there's a yellow `NEW` icon next to the song | +| priorityOrder | integer | The priority order of the song. Usually, songs are ordered alphabetically, but if the song has this number set above 0, then it gets put above all the other songs. The song also receives a blue `NEW` icon. All of the songs with this property have the previous property set as 0. | +| songNumber | integer | The number of the song in the list. Starts at 1. Usage is unknown | + +## Tags + +These are all the tags that you can filter songs by. They are stored in the following format: + +`{id},{name};{id},{name}...` + +All tags as of version `117`: + +``` +144,16bit;49,8bit;126,Acion;20,Action;114,Adventure;90,African;84,Aggressive;37,Alert;187,Alternative dance;211,Alternative hip-hop;186,Alternative pop;17,Ambiance;121,Ambience;124,Ambient;169,Angry;194,Anti-pop;66,Asian;115,Atmosphere;70,Atmospheric;204,Bass house;179,Bass music;39,Battle;232,Big room house;100,Blues;58,Boss;75,Bouncy;147,Brazilian phonk;184,Breakbeat;79,Bright;16,Calm;82,Calming;40,Casual;127,Celtic;165,Chasing;123,Chill;199,Chill bass;192,Chill house;195,Chill pop;48,Chiptune;23,Christmas;128,Cinematic;94,Classical;233,Color bass;41,Combat;9,Comedy;231,Complextro;89,Contemporary;72,Creepy;145,Cute;43,Cyberpunk;155,Dance;173,Dance-pop;221,Dance-punk;203,Dance-rock;11,Dark;190,Deep house;129,Desert;99,Disco;209,Disco house;14,Doom;205,Downtempo;149,Dreamy;78,Driving;109,Drone;164,Drum & bass;42,Drums;217,Drumstep;191,Dubstep;38,Dungeon;133,Dystopian;156,Eccentric;108,Edm;76,Eerie;6,Electro;219,Electro house;35,Electronic;174,Electronic pop;176,Electronic rock;96,Electronica;157,Elegant;64,Energetic;44,Environment;18,Epic;143,Ethnic;150,Euphoric;36,Event;177,Experimental;15,Fantasy;167,Fear;118,Field;132,Fire;158,Floating;139,Forest;85,Funk;10,Funny;210,Future bass;214,Future bounce;223,Future funk;197,Future house;212,Future rave;200,Future trap;201,Futurepop;110,Futuristic;153,Garage;159,Glamorous;216,Glitch hop;74,Grooving;54,Guitar;24,Happy;213,Hardcore;171,Hardstyle;135,Harp;166,Heavy;148,Hip-hop;91,Holiday;180,Hopeful;22,Horror;154,House;12,Humor;25,Humorous;56,Hybrid;59,Indie;208,Indie dance;29,Instrumental;8,Intense;95,Jazz;170,Jersey club;53,Jrpg;207,Jump-up;175,Laid back;101,Latin;193,Latin-dance;47,Light;188,Liquid;34,Lofi;182,Lofi hip-hop;61,Loop;113,Low-fi;28,Medieval;222,Melbourne bounce;178,Melodic dubstep;185,Melodic house;13,Metal;181,Midtempo bass;146,Military;196,Minimal;102,Modern;206,Moodshappy;105,Musical;26,Mysterious;46,Mystery;77,Mystical;140,Nature;189,Neurofunk;67,Nordic;19,Orchestral;183,Peaceful;57,Percussion;112,Phonk;30,Piano;32,Pirate;63,Platformer;98,Polka;86,Pop;202,Progressive electro;229,Progressive electron;218,Progressive house;73,Puzzle;142,Quirky;104,Reggae;80,Relaxed;62,Relaxing;160,Restless;45,Retro;52,Retrowave;92,Rock;161,Romantic;21,Rpg;151,Running;31,Sad;168,Scary;50,Sci-fi;137,Scifi;117,Sentimental;162,Sexy;122,Sfx;60,Short;87,Silentfilmscore;106,Ska;215,Slap house;131,Slavic;163,Smooth;172,Sneaking;7,Sneaky;81,Somber;71,Soundscape;88,Soundtrack;33,Spooky;103,Stings;134,Suspense;27,Suspenseful;130,Synth;220,Synth-pop;5,Synthwave;136,Tavern;198,Tech house;141,Techno;69,Texture;120,Theme;138,Tonal;119,Town;125,Trance;116,Tranquil;111,Trap;230,Tropical house;93,Unclassifiable;55,Underscore;68,Unnerving;51,Upbeat;83,Uplifting;107,Urban;152,Weird;65,Western;97,World; +``` diff --git a/docs/resources/client/sfxlibrary.md b/docs/resources/client/sfxlibrary.md new file mode 100644 index 000000000..2f85dde2d --- /dev/null +++ b/docs/resources/client/sfxlibrary.md @@ -0,0 +1,42 @@ +# SFX Library format + +The SFX Library is stored in your save directory as `sfxlibrary.dat`. To learn how to retreive it from the servers, refer to [this page](/endpoints/songs/sfxlibrary.md). + +To decode sfxlibrary.dat, you need to [Base64](/topics/encryption/base64.md) URL-Safe decode it and [Zlib](/topics/encryption/zip.md) inflate it. + +The SFX library is split into 2 parts: + +``` +{files}|{credits} +``` + +## Files & folders + +`{files}` is a list of files and folders, separated with semicolons. The file format is as follows: + +`{id},{name},{isFolder},{parentFolder},{filesize},{duration}` + +| Key | Type | Description | +| :----------- | :-------- | :--------------------------------------------------------------- | +| id | `integer` | The sound effect/folder ID | +| name | `string` | The sound effect/folder name | +| isFolder | `boolean` | Whether this is a sound effect or folder | +| parentFolder | `integer` | The parent folder | +| filesize | `integer` | The sound effect file size in bytes | +| duration | `integer` | The duration of the sound effect, in `seconds * 100` | + +The first "folder" is a special case: it has an ID of 1 and its name corresponds to the SFX library version. + +## Credits + +The `{credits}` part is very simple. It's a list of all the companies/people from which RobTop has licensed the sound effects, in this format: + +`{name},{link};{name},{link}...` + +where `name` is the name of the company and `link` is a link to their website. + +Example (SFX library version **95**): + +``` +Epic Stock Media,https://epicstockmedia.com;Cyberwave Orchestra,https://cyberwaveorchestra.com;Fusehive,http://fusehive.com;SoundMorph,https://www.soundmorph.com;Stormwave Audio,https://stormwave-audio.com;David Dumais,https://www.daviddumaisaudio.com;Sharks,https://www.sharkstunes.com; +``` diff --git a/docs/resources/server/comment.md b/docs/resources/server/comment.md index 05d7801e3..f103144f5 100644 --- a/docs/resources/server/comment.md +++ b/docs/resources/server/comment.md @@ -40,7 +40,7 @@ A list of all known keys can be found in the table below | Key | Name/Value | Type | Description |-----|---------------------------|----------------------------------------------|-------------------------------------------------------------------------- -| 1 | levelID | **Integer** | The levelID linked to the comment +| 1 | levelID | **Integer** | The levelID linked to the comment. This ID is negative if the comment is on a list | 2 | comment | **String** | The comment left by the user, encoded in [base64](/topics/encryption/base64.md) | 3 | authorPlayerID | **Integer** | The player ID of the comment author | | 4 | likes | **Integer** | The amount of likes the comment has @@ -49,7 +49,7 @@ A list of all known keys can be found in the table below | 7 | spam | **Bool** | If a comment has been flagged as spam | 8 | authorAccountID | **Integer** | The accountID of the comment author | | 9 | age | **String** | How long ago the comment was posted (e.g. "2 months") -| 10 | percent* | **Integer** | The percent the player put in their comment +| 10 | percent* | **Integer** | The percent the player put in their comment. Also doesn't apply to List Comments | 11 | modBadge* | **Integer** | The Mod Badge of a moderator commenting | 12 | moderatorChatColor* | **String** | Comma separated list of the RGB values of the moderator's chat color - only appears if the players `modBadge > 0` diff --git a/docs/resources/server/friendrequest.md b/docs/resources/server/friendrequest.md index c915465b6..c72dec8ce 100644 --- a/docs/resources/server/friendrequest.md +++ b/docs/resources/server/friendrequest.md @@ -17,7 +17,7 @@ The server response returns a [User String](/resources/server/user) in a respons Each `key` is tied to a component within the client and the `value` sets data for the specific component. A list of all known keys can be found in the table below -#### Friend Request Structure +### Friend Request Structure **Note:** We will use "other user" to describe the user on the other side of the friend request, be it the receiving end or the sending end. @@ -36,6 +36,6 @@ A list of all known keys can be found in the table below | 37 | age | **String** | How long ago the friend request was sent (e.g. "2 months") | 41 | NewFriendRequest | **Bool** | if the friend request is new -### Trivia +#### Trivia - By sending yourself a friend request through 3rd party tools, you can actually block yourself. Doing so doesn't have any serious consequences as you can see in this [video by Cvolton](https://www.youtube.com/watch?v=R18tKYFrIqE) diff --git a/docs/resources/server/gauntlet.md b/docs/resources/server/gauntlet.md index 3b4292a4c..9766a4070 100644 --- a/docs/resources/server/gauntlet.md +++ b/docs/resources/server/gauntlet.md @@ -4,10 +4,8 @@ Gauntlets are a collection of themed levels created by the users of Geometry Dash which were hand-picked by RobTop. - - As of Geometry Dash 2.11, There are `15 gauntlets` - - - According to the [December 2019 2.2 leaks](https://www.reddit.com/r/geometrydash/comments/e9b0y6/update_22_leaks_megathread/), 2.2 will include `44 Gauntlets` - - **A list of the Gauntlet Names can be found [here](/resources/server/gauntlet?id=gauntlet-names)**
*Keep in mind that these are subject to change* + - As of Geometry Dash 2.206, there are `27 gauntlets` + - **A list of the Gauntlet Names can be found [here](/resources/server/gauntlet?id=gauntlet-names)**
*Keep in mind that these are subject to change* A typical gauntlet server response is structured with a `key:value:key:value` pairing and is then split with a `|` @@ -33,54 +31,65 @@ A list of all known keys can be found in the table below Here is a table which shows which `gauntletID` corresponds to a specific gauntlet +Note: Bolded gauntlet names are the ones that are added in-game, and the rest are yet to be added -| ID ⠀| Name | +| ID | Name | |:---|:-----| - | 1| Fire| - | 2| Ice| - | 3| Poison| - | 4| Shadow| - | 5| Lava| - | 6| Bonus| - | 7| Chaos| - | 8| Demon| - | 9| Time| - | 10| Crystal| - | 11| Magic| - | 12| spike| - | 13| Monster| - | 14| Doom| - | 15| Death| - | 16| Forest| - | 17| Rune| - | 18| Force| - | 19| Spooky| - | 20| Dragon| - | 21| Water| - | 22| Haunted| - | 23| Acid| - | 24| Witch| - | 25| Power| - | 26| Potion| - | 27| Snake| - | 28| Toxic| - | 29| Halloween| - | 30| Treasure| - | 31| Ghost| - | 32| Gem| - | 33| Inferno| - | 34| Portal| - | 35| Strange| - | 36| Fantasy| - | 37| Christmas| - | 38| Surprise| - | 39| Mystery| - | 40| Cursed| - | 41| Cyborg| - | 42| Castle| - | 43| Grave| - | 44| Temple| +| 1 | **Fire** | +| 2 | **Ice** | +| 3 | **Poison** | +| 4 | **Shadow** | +| 5 | **Lava** | +| 6 | **Bonus** | +| 7 | **Chaos** | +| 8 | **Demon** | +| 9 | **Time** | +| 10 | **Crystal** | +| 11 | **Magic** | +| 12 | **Spike**| +| 13 | **Monster** | +| 14 | **Doom** | +| 15 | **Death** | +| 16 | **Forest** | +| 17 | Rune | +| 18 | **Force** | +| 19 | Spooky | +| 20 | Dragon | +| 21 | **Water** | +| 22 | **Haunted** | +| 23 | Acid | +| 24 | Witch | +| 25 | Power | +| 26 | Potion | +| 27 | Snake | +| 28 | Toxic | +| 29 | **Halloween** | +| 30 | Treasure | +| 31 | Ghost | +| 32 | Gem | +| 33 | **Inferno** | +| 34 | **Portal** | +| 35 | **Strange** | +| 36 | **Fantasy** | +| 37 | **Christmas** | +| 38 | Surprise | +| 39 | **Mystery** | +| 40 | **Cursed** | +| 41 | **Cyborg** | +| 42 | **Castle** | +| 43 | Grave | +| 44 | Temple | +| 46 | **World** | +| 47 | **Galaxy** | +| 48 | **Universe** | +| 49 | **Discord** | +| 50 | **Split** | +| 51 | **NCS I** | +| 52 | **NCS II** | +| ? | Space | +| ? | Cosmos | ### Trivia -- Gauntlets use the same response parser as [MapPacks](/resources/server/mappack) but they do not share the full range of features mappacks have \ No newline at end of file +- Gauntlets use the same response parser as [MapPacks](/resources/server/mappack) but they do not share the full range of features mappacks have +- The **Spike Gauntlet** used to be improperly capitalized in 2.1 as `spike` gauntlet diff --git a/docs/resources/server/hashes.md b/docs/resources/server/hashes.md new file mode 100644 index 000000000..2849ac75f --- /dev/null +++ b/docs/resources/server/hashes.md @@ -0,0 +1,89 @@ +# Hashes + +The hash is a segment commonly sent in requests, which is checked by the GD Client to ensure the validity of the request. This was added in 2.02 to combat GDPSes (GD Private Servers). + +The hash is commonly generated by adding some segments from the response all into one string, then adding a salt and SHA-1 hashing it. + +## getGJLevels + +For each level, add these segments: + +- The first digit of the level ID +- The last digit of the level ID +- The stars the level awards +- The amount of coins in the level +- 0 if the level has unverified coins, 1 if verified + +Salt: `xI25fpAapCQg` + +## downloadGJLevel + +This endpoint has 2 hashes, the first of which is generated differently from most of the hashes. + +### downloadGJLevel Hash 1 + +This hash is generated from the undecoded level string by splitting the level string into 40 equal segments and taking 40 characters evenly, then adding the salt and SHA-1 hashing the result. Here's a JS function that does just that: + +```js +function generateDownloadHash(levelString) { + if (levelString.length < 41) return sha1(`${levelString}xI25fpAapCQg`); + let hash = `????????????????????????????????????????xI25fpAapCQg`; + let m = Math.floor(levelString.length / 40); + let i = 40; + while (i) { + hash = hash.slice(0, --i) + levelString[i*m] + hash.slice(i+1); + } + return sha1(hash); +} +``` + +### downloadGJLevel Hash 2 + +Segments: + +- Player ID of the creator +- The stars the level awards +- 0 if the level is not demon, 1 if the level is demon +- The ID of the level +- 0 if the coins are unverified, 1 if verified +- The level's feature score. 0 if not featured +- The level's password (0 for no copy, 1 for free copy, and for any other password if the number is less than 1,000,000, add 1,000,000 to it) +- The level's daily number (if the level was never daily or you don't get a daily number from the response, just put 0) + +Salt: `xI25fpAapCQg` + +## getGJMapPacks + +For each pack, add these segments: + +- The first digit of the pack ID +- The last digit of the pack ID +- The stars the pack awards +- The coins the pack awards + +Salt: `xI25fpAapCQg` + +## getGJGauntlets + +For each gauntlet, add these segments: + +- The gauntlet ID +- The IDs of the levels in the gauntlet, separated by `,` + +Salt: `xI25fpAapCQg` + +## getGJChallenges + +The hash just takes the entire **undecoded** response string (excluding the 5 characters appended to the front). + +Salt: `oC36fpYaPtdg` + +## getGJRewards + +The hash is generated the same way as getGJChallenges, with the exception of a different salt. + +Salt: `pC26fpYaQCtg` + +## getGJLevelLists + +This endpoint returns a hash, but since the GD client doesn't check it, the way to generate this hash is unknown. diff --git a/docs/resources/server/leaderboardscore.md b/docs/resources/server/leaderboardscore.md index 4f7dc05cb..f9df9b1f3 100644 --- a/docs/resources/server/leaderboardscore.md +++ b/docs/resources/server/leaderboardscore.md @@ -22,10 +22,15 @@ A typical leaderboard server response is structured with a `key:value:key:value` 1:TheWylieMaster:2:84696119:9:1:10:4:11:16:14:3:15:2:16:9276649:3:34:6:1:13:0:42:1 second ``` +### **Platformer Level Leaderboard Example** +```md +1:nebularius:2:245629812:9:24:10:17:11:3:14:5:15:2:16:28102632:3:750:6:1:42:21 hours +``` + ### **Top Leaderboard Example** ```md -1:xMiguel007:2:2866103:13:149:17:7219:6:1:9:37:10:35:11:3:14:0:15:2:16:70846:3:65710:8:0:46:12879:4:1073 +1:Smiffy777:2:7708568:13:164:17:57051:6:1:9:115:10:29:11:14:14:0:15:2:16:1413859:3:273000:52:4684:8:0:46:204136:4:7018 ``` @@ -33,25 +38,44 @@ A typical leaderboard server response is structured with a `key:value:key:value` Each `key` is tied to a component within the client and the `value` sets data for the specific component. A list of all known keys can be found in the table below -### Leaderboard Score Structure - -**Note:** keys marked by a `*` are only used for level leaderboards +### Level Leaderboard Score Structure | Key | Name/Value | Type | Description |-----|---------------------------|----------------------------------------------|-------------------------------------------------------------------------- | 1 | userName | **String** | The username of the user | 2 | playerID | **Integer** | The player ID of the user. **This is different than the account ID** -| 3 | percentage* | **Integer** | Percentage the user has on the level +| 3 | percentage | **Integer** | Percentage the user has on the level. If the level is platformer, this is the time in milliseconds instead | 6 | ranking | **Integer** | What rank they are (e.g. Viprin would have 1 in the creating leaderboard) | 9 | Icon | **Integer** | Which icon the user is using, starting with 1 as the first icon | 10 | playerColor | **Integer** | The user's primary player color, presumably ordered cronologically from left to right per update | 11 | playerColor2 | **Integer** | The user's secondary player color, presumably ordered cronologically from left to right per update -| 13 | coins | **Integer** | Secret coins/number of usercoins you get on a level* -| 14 | iconType | **Integer** | The user's icon type indexing an array of `icon, ship, ball, ufo, wave, robot, spider` +| 13 | coins | **Integer** | Number of usercoins you get on a level +| 14 | iconType | **Integer** | The user's icon type indexing an array of `icon, ship, ball, ufo, wave, robot, spider, swing, jetpack` | 15 | special | **Integer** | functions the same as glow however it returns a 2 rather than a 1 | 16 | accountID | **Integer** | The user's account ID. **This is different than the player ID** | 42 | age | **String** | How long ago the score was set (e.g. "2 months") +### Top Leaderboard Score Structure + +| Key | Name/Value | Type | Description +|-----|---------------------------|----------------------------------------------|-------------------------------------------------------------------------- +| 1 | userName | **String** | The username of the user +| 2 | playerID | **Integer** | The player ID of the user. **This is different than the account ID** +| 3 | stars | **Integer** | The amount of stars the player has +| 4 | demons | **integer** | The amount of demons the player has +| 6 | ranking | **Integer** | What rank they are (e.g. Viprin would have 1 in the creating leaderboard) +| 8 | creatorPoints | **Integer** | The amount of creator points the player has +| 9 | Icon | **Integer** | Which icon the user is using, starting with 1 as the first icon +| 10 | playerColor | **Integer** | The user's primary player color, presumably ordered cronologically from left to right per update +| 11 | playerColor2 | **Integer** | The user's secondary player color, presumably ordered cronologically from left to right per update +| 13 | coins | **Integer** | Secret coins +| 14 | iconType | **Integer** | The user's icon type indexing an array of `icon, ship, ball, ufo, wave, robot, spider, swing, jetpack` +| 15 | special | **Integer** | functions the same as glow however it returns a 2 rather than a 1 +| 16 | accountID | **Integer** | The user's account ID. **This is different than the player ID** +| 17 | userCoins | **Integer** | The amount of user coins the player has +| 46 | diamonds | **Integer** | The amount of diamonds the player has +| 52 | moons | **Integer** | The amount of moons the player has + ### Trivia -- The key structure that leaderboards follow are exactly the same as the structure used for [Player Profiles](/resources/server/user.md) \ No newline at end of file +- The key structure that leaderboards follow are exactly the same as the structure used for [Player Profiles](/resources/server/user.md) diff --git a/docs/resources/server/level.md b/docs/resources/server/level.md index 78448fd49..290cdda09 100644 --- a/docs/resources/server/level.md +++ b/docs/resources/server/level.md @@ -4,7 +4,6 @@ A level is a playable object in Geometry Dash, namely coming with data that explains on what it is, and the string that the client interprets, known as a [level string](/topics/levelstring_encoding_decoding). - A typical level server response is structured with a `key:value:key:value` pairing and is then split with a `|` *keep in mind that the value for key `4` will be replaced with `{levelString}` as it is too big to show* @@ -27,8 +26,8 @@ Keys indicated with an asterisk (\*) are only returned from the downloadGJLevel2 | Key | Name/Value | Type | Description |-----|---------------------------|----------------------------------------------|-------------------------------------------------------------------------- -| 1 | levelID | **Integer** | The id of the level -| 2 | levelName | **String** | The name of the level +| 1 | levelID | **Integer** | The id of the level +| 2 | levelName | **String** | The name of the level | 3 | description | **String** | The level description, encoded in [base64](/topics/encryption/base64.md) | 4* | levelString | **[Level String](/topics/levelstring_encoding_decoding)**| All the data for the level | 5 | version | **Integer** | The version of the level published @@ -44,12 +43,12 @@ Keys indicated with an asterisk (\*) are only returned from the downloadGJLevel2 | 16 | dislikes | **Integer** | dislikes - likes | | 17 | demon | **Bool** | If the level's difficulty is demon | 18 | stars | **Integer** | The amount of stars rewarded for completing the level -| 19 | featureScore | **Integer** | 0 if the level is not featured, otherwise a positive number. The higher it is, the higher the level appears on the featured levels list. +| 19 | featureScore | **Integer** | 0 if the level is not featured, otherwise a positive number. The higher it is, the higher the level appears on the featured levels list. | 25 | auto | **Bool** | If the level's difficulty is auto | 26 | recordString | **String** | appears in the [GJGameLevel parser](https://imgur.com/a/S2bWLCC) but is unused | 27* | password | **Encrypted String** | The password required to copy the level. It is XOR encrypted with a key of 26364 -| 28* | uploadDate | **String** | The approximate date the level was uploaded on -| 29* | updateDate | **String** | The approximate date the level was last updated on +| 28* | uploadDate | **String** | The approximate date the level was uploaded on +| 29* | updateDate | **String** | The approximate date the level was last updated on | 30 | copiedID | **Integer** | The ID the of the original level (if the level was copied) | 31 | twoPlayer | **Bool** | Whether the level uses two player mode | 35 | customSongID | **Integer** | The ID of the custom Newgrounds song used in the level @@ -59,19 +58,24 @@ Keys indicated with an asterisk (\*) are only returned from the downloadGJLevel2 | 39 | starsRequested | **Integer** | The star value requested for the level | 40* | lowDetailMode | **Bool** | If the level has a low detail checkbox | 41* | dailyNumber | **Integer** | Daily/weekly levels only. Returns which daily/weekly the level was (e.g. the 500th daily level). Subtract 100,000 if the level is weekly -| 42 | epic | **Integer** | If the level has an epic rating +| 42 | epic | **Integer** | The epic rating for the level. 0 = none, 1 = epic, 2 = legendary, 3 = mythic. | 43 | demon Difficulty | **Integer** | The difficulty of the demon rating. 3 = easy, 4 = medium, 0 = hard, 5 = insane, 6 = extreme. Can also be used to determine the level difficulty non-demons had before rating as a side-effect of the voting system. | 44 | isGauntlet | **Bool** | if the level is in a gauntlet | | 45 | objects | **Integer** | The amount of objects in the level, used to determine if the level is considered "large". It caps at 65535 | 46 | editorTime | **Integer** | the total number of seconds spend on the current copy of a level | 47 | editorTime(Copies) | **Integer** | The accumulative total of seconds spend on previous copies of the level -| 48 | settingsString [Unused] | **String** | It was found in early 2.1 coming from the servers and was removed shortly after. The `December 2019 2.2 Leaks` however have information regarding it showing that it is called `settingsString` but, there is no information regarding its usage | +| 48 | settingsString [Unused] | **String** | It was found in early 2.1 coming from the servers and was removed shortly after. The `December 2019 2.2 Leaks` however have information regarding it showing that it is called `settingsString` but, there is no information regarding its usage | +| 52* | songIDs | **Comma-Separated List** | The list of all song IDs in the level, separated by commas +| 53* | sfxIDs | **Comma-Separated List** | The list of all SFX IDs in the level, separated by commas +| 54 | unknown | **Integer** | Unknown value, perhaps robtop-only? (corresponds to k106 in the save file) | +| 57* | verificationTime | **Integer** | How long the level took to verify (in frames, assume 240 FPS) ### Trivia -- The getGJLevels endpoint returns the keys `46` and `47` however they aren't actually correct +- The getGJLevels endpoint returns the keys `46` and `47` however they aren't actually correct (as of 2.1) -- key `45` caps at the 16-bit integer limit so any level with more objects won't be accurate +- Keys `46` and `47` are also capped at the 24-bit integer limit so any time longer than around 4660 hours won't be accurate -- Surprisingly, key `42` is classified as an `Integer` rather than a `Bool` +- key `45` caps at the 16-bit integer limit so any level with more objects won't be accurate +- Key `57` caps at the 24-bit integer limit so any time longer than around 465 hours won't be accurate diff --git a/docs/resources/server/list.md b/docs/resources/server/list.md new file mode 100644 index 000000000..ff86d3670 --- /dev/null +++ b/docs/resources/server/list.md @@ -0,0 +1,32 @@ +# Server List Resource + +## List Object +A level list is a list of levels - playable objects in Geometry Dash. + +A typical list server response is structured with a `key:value:key:value` pairing and is then split with a `|` +#### **List Example Response** +```md +1:4788:2:Progression Level 1:3:R0QgQmVnaW5uZXJzIExpc3QgbGV2ZWwgMSEgR29vZCBsaXN0IG9mIGxldmVscyB0byBpbnRyb2R1Y2UgZnJpZW5kcyB0byB0aGUgZ2FtZSE=:5:5:49:6061424:50:tricipital:10:1451689:7:1:14:61715:19:1:51:90752263,59760047,88982532,78743788,88022936,89413344,90994090,74542823,74612523,55037478:55:20:56:5:28:1703050435:29:1703402400#15479163:tricipital:6061424#9999:0:10#f5da5823d94bbe7208dd83a30ff427c7d88fdb99 +``` + +Each `key` is tied to a component within the client and the `value` sets data for the specific component. +A list of all known keys can be found in the table below + +#### List Structure +| Key | Name/Value | Type | Description +|-----|---------------------------|----------------------------------------------|-------------------------------------------------------------------------- +| 1 | listID | **Integer** | The id of the list +| 2 | listName | **String** | The name of the list +| 3 | description | **String** | The list description, encoded in [base64](/topics/encryption/base64.md) +| 5 | version | **Integer** | The version of the list published +| 7 | difficulty | **Integer** | The difficulty face for the list. -1 = N/A, 0 = Auto, 1 = Easy, 2 = Normal, 3 = Hard, 4 = Harder, 5 = Insane, 6 = Easy Demon, 7 = Medium Demon, 8 = Hard Demon, 9 = Insane Demon, 10 = Extreme Demon +| 10 | downloads | **Integer** | The amount of times the list has been downloaded +| 14 | likes | **Integer** | likes - dislikes | +| 19 | rated | **Bool** | If the list is rated or not +| 28 | uploadDate | **String** | The Unix timestamp of when the list was uploaded +| 29 | updateDate | **String** | The Unix timestamp of when the list was last updated +| 49 | accountID | **Integer** | The account ID of the list author +| 50 | username | **String** | The username of the list author +| 51 | levelIDs | **Comma-Separated List** | All level IDs in the list, separated by commas +| 55 | listReward | **Integer** | The amount of diamonds awarded upon beating the required amount of levels in the list +| 56 | listRewardRequirement | **Integer** | The amount of levels needed to claim the list reward diff --git a/docs/resources/server/restore.md b/docs/resources/server/restore.md index 24088bf5b..33f9d52ab 100644 --- a/docs/resources/server/restore.md +++ b/docs/resources/server/restore.md @@ -2,7 +2,7 @@ ## RestoreGJItems -`Restore` was a feature used in Geometry dash from versions `1.7 - 2.0` and it was used as an alternative to the [Account System](/topics/accounts.md) that is currently used as of 2.11 +`Restore` was a feature used in Geometry dash from versions `1.7 - 2.0` and it was used as an alternative to the [Account System](resources/topics/accounts.md) that is currently used as of 2.206 A typical Gauntlet Server response is structured with a `key:value:key:value` pairing *Official Levels behave a bit differently as you will find out further into his page* diff --git a/docs/resources/server/song.md b/docs/resources/server/song.md index d8ffc899b..db7e4c055 100644 --- a/docs/resources/server/song.md +++ b/docs/resources/server/song.md @@ -1,4 +1,4 @@ -# Server Song Resource +# Client & Server Song Resource ## Song @@ -24,6 +24,8 @@ A typical song server response is structured with a `key~|~value~|~key~|~value` Each `key` is tied to a component within the client and the `value` sets data for the specific component. A list of all known keys can be found in the table below +The IDs for Music Library songs start at 10,000,000 + #### Song Structure | Key | Name/Value | Type | Description @@ -38,4 +40,11 @@ A list of all known keys can be found in the table below | 8 | isVerified | **Bool** | if the song artist is scouted on newgrounds | 9 | songPriority | **Integer** | priority over the song list | 10 | link | **String** | Link to the song's mp3 - +| 11 | nongEnum | **Integer** | Type of NONG. 0 for none, 1 for NCS. +| 12 | extraArtistIDs | **Array\[Integer]** | IDs of extra artists, separated by `.` +| 13 | new | **Boolean** | Whether the NEW icon shows up or not +| 14 | newType | **Integer** | Type of NEW icon. 0 for Yellow, 1 for Blue +| 15 | extraArtistNames | **Array** | Artist names in this format: `{id},{name},{id},{name}` + +#### **Trivia** +- The savefile song structure uses the exact same keys as the server response diff --git a/docs/resources/server/user.md b/docs/resources/server/user.md index 9fc247bf8..2f783c078 100644 --- a/docs/resources/server/user.md +++ b/docs/resources/server/user.md @@ -10,7 +10,7 @@ A typical user server response is structured with a `key:value:key:value` pairin #### **User Example Response** ```md -1:TheWylieMaster:2:84696119:13:67:17:176:10:4:11:16:3:1725:46:2991:4:33:8:0:18:0:19:0:50:0:20:../watch?v=dQw4w9WgXcQ:21:31:22:10:23:30:24:1:25:35:26:23:28:1:43:11:48:1:30:0:16:9276649:31:0:44:TheWylieMaster:45::49:0:38:0:39:0:40:0:41:1:29:1 +1:TheWylieMaster:2:84696119:13:73:17:251:10:4:11:16:51:21:3:2441:52:80:46:4062:4:42:8:0:18:0:19:0:50:1:20:%%00:21:31:22:10:23:30:24:86:25:35:26:23:28:1:43:11:48:1:53:1:54:5:30:283041:16:9276649:31::44:TheWylieMaster:45:wyliemaster:49:0:55:32,4,2,0,0,0,0,0,0,0,4,0:56:134,109,93,101,49,13,32,45:57:0,1,4,9,3,0,0:29:1 ``` @@ -27,8 +27,8 @@ A list of all known keys can be found in the table below | 7 | accountHighlight | **Integer** | The accountID of the player. Is used for highlighting the player on the leaderboards | | 8 | creatorpoints | **Integer** | The count of creatorpoints player have | | 9 | iconID | **Integer** | maybe... [link](https://github.com/gd-programming/gddocs/pull/16/files#r417926661) | -| 10 | playerColor | **Integer** | First color of the player use | -| 11 | playerColor2 | **Integer** | Second color of the player use | +| 10 | color | **Integer** | First color of the player use | +| 11 | color2 | **Integer** | Second color of the player use | | 13 | secretCoins | **Integer** | The count of coins player have | | 14 | iconType | **Integer** | The iconType of the player use | | 15 | special | **Integer** | The special number of the player use | @@ -60,6 +60,13 @@ A list of all known keys can be found in the table below | 48 | accExplosion | **Integer** | The explosion number of the player use | | 49 | modlevel | **Integer** | 0: None, 1: Normal Mod(yellow), 2: Elder Mod(orange) | | 50 | commentHistoryState | **Integer** | 0: All, 1: Only friends, 2: None | +| 51 | color3 | **Integer** | The ID of the player's glow color | +| 52 | moons | **Integer** | The amount of moons the player has | +| 53 | accSwing | **Integer** | The player's swing | +| 54 | accJetpack | **Integer** | The player's jetpack | +| 55 | demons | **String** | Breakdown of the player's demons, in the format `{easy},{medium},{hard}.{insane},{extreme},{easyPlatformer},{mediumPlatformer},{hardPlatformer},{insanePlatformer},{extremePlatformer},{weekly},{gauntlet}` +| 56 | classicLevels | **String** | Breakdown of the player's classic mode non-demons, in the format `{auto},{easy},{normal},{hard},{harder},{insane},{daily},{gauntlet}` +| 57 | platformerLevels | **String** | Breakdown of the player's platformer mode non-demons, in the format `{auto},{easy},{normal},{hard},{harder},{insane}` ### Trivia diff --git a/docs/stylesheets/colour_tags.css b/docs/stylesheets/colour_tags.css new file mode 100644 index 000000000..64ba06ad7 --- /dev/null +++ b/docs/stylesheets/colour_tags.css @@ -0,0 +1,64 @@ +@font-face { + font-family: "TeX Gyre Heros"; + src: url("../assets/fonts/texgyreheros-regular.woff") format("woff"); +} + +gdfmt { + font-family: "TeX Gyre Heros"; + + cb { + color: #4A52E1; + } + + cg { + color: #40E348; + } + + cl { + color: #60ABEF; + } + + cj { + color: #32C8FF; + } + + cy { + color: #FFFF00; + } + + co { + color: #FFA54B; + } + + cr { + color: #FF5A5A; + } + + cp { + color: #FF00FF; + } + + ca { + color: #9632FF; + } + + cd { + color: #FF96FF; + } + + cc { + color: #FFFF96; + } + + cf { + color: #96FFFF; + } + + cs { + color: #FFDC41; + } + + c { + color: #FF0000; + } +} \ No newline at end of file diff --git a/docs/stylesheets/imageStyles.css b/docs/stylesheets/imageStyles.css new file mode 100644 index 000000000..54a0d3bd4 --- /dev/null +++ b/docs/stylesheets/imageStyles.css @@ -0,0 +1,16 @@ +.admin { + margin: 10px; + border: 2px solid #344640; + border-radius: 7px; + padding: 10px; + background-color: #2D2D2D; +} + +.alertlayer +{ + margin: 10px; + border: 2px solid #344640; + border-radius: 7px; + padding: 10px; + background-color: #001033; +} \ No newline at end of file diff --git a/docs/stylesheets/style.css b/docs/stylesheets/style.css new file mode 100644 index 000000000..cb0498180 --- /dev/null +++ b/docs/stylesheets/style.css @@ -0,0 +1,23 @@ +/* Custom colour theme. */ +:root { + --mono-hue: 160; + --theme-hue: 160; + --theme-saturation: 100%; + --table-cell-border-width: 1px; + --table-head-border-width: 1px; + --table-head-background: #17201e; +} + +details > summary:hover { + background: rgba(0, 0, 0, 0.1); + transition: 0.1s; + border-radius: 0.2em; +} + +details[open] { + background: rgba(0, 0, 0, 0.05); +} + +details[open] > *:not(summary) { + margin: 0.5em; +} \ No newline at end of file diff --git a/docs/topics/cdn_token.md b/docs/topics/cdn_token.md new file mode 100644 index 000000000..89e896d5b --- /dev/null +++ b/docs/topics/cdn_token.md @@ -0,0 +1,19 @@ +# CDN Tokens + +For both musiclibrary and sfxlibrary, there are 2 optional parameters that are sent by the GD client but are currently **not** required to receive the data. One still may want to implement these into their code for future-proofing. + +#### `expires` + +This is the UNIX timestamp (seconds) that is an hour later than the current timestamp. + +#### `token` + +Tokens are generated this way (pseudocode): + +``` +# The endpoint is the part of the URL after https://geometrydashfiles.b-cdn.net. So, for example, the endpoint for https://geometrydashfiles.b-cdn.net/sfx/sfxlibrary_version.txt would be /sfx/sfxlibrary_version.txt +hash = md5("8501f9c2-75ba-4230-8188-51037c4da102{endpoint}{expires}") +base64EncodeUrlSafe(hash.digest(ASCII)) +``` + +Please note that the hash must be digested as ASCII, not hexadecimal! diff --git a/docs/topics/encryption/AES.md b/docs/topics/encryption/AES.md new file mode 100644 index 000000000..73e8e7cd9 --- /dev/null +++ b/docs/topics/encryption/AES.md @@ -0,0 +1,92 @@ +# AES + +> AES (The Advanced Encryption Standard) is an encryption algorithm used for encrypting Geometry Dash Save files on Mac and iOS + +- Geometry Dash uses `ECB` mode and a 256-bit key for encrypting and descrypting data + +## AES Key + +- The key used by Geometry Dash is: + + +- **Hexadecimal** + ```md + 69 70 75 39 54 55 76 35 34 79 76 5d 69 73 46 4d 68 35 40 3b 74 2e 35 77 33 34 45 32 52 79 40 7b + ``` + +- **Plaintext** + ``` + ipu9TUv54yv]isFMh5@;t.5w34E2Ry@{ + ``` + +## Padding + +- The plaintext data sometimes may not perfectly align and therefore, some padding is implemented + + + +### **Implement Padding** + +```js +// Pseudocode +pad_offset = data.length % 16; + +if (pad_offset != 0) { + pad_total = 16 - pad_offset; + + for (i = 0; i < pad_total; i++) { + data += pad_total.toByte(); + } +} +``` + +### **Remove Padding** + +```js +// Pseudocode +total_padding = data.last_byte(); + +if (total_padding < 16) { + data = data.slice(0, data.length - total_padding); +} +``` + + + +## Implementation + +- Decrypting and Encrypting data with AES is pretty simple + + + +### **Encrypt** + +```js +// Pseudocode +data = addPadding(data); + +cipher = AES.new( + "\x69\x70\x75\x39\x54\x55\x76\x35\x34\x79\x76\x5d\x69\x73\x46\x4d + \x68\x35\x40\x3b\x74\x2e\x35\x77\x33\x34\x45\x32\x52\x79\x40\x7b", + AES.MODE_ECB +); + +encrypted_data = cipher.encrypt(data); +``` + +### **Decrypt** + +```js +// Pseudocode +cipher = AES.new( + "\x69\x70\x75\x39\x54\x55\x76\x35\x34\x79\x76\x5d\x69\x73\x46\x4d + \x68\x35\x40\x3b\x74\x2e\x35\x77\x33\x34\x45\x32\x52\x79\x40\x7b", + AES.MODE_ECB +); + +decrypted = cipher.decrypt(data); + +decrypted = removePadding(decrypted); +``` + + diff --git a/docs/topics/encryption/base64.md b/docs/topics/encryption/base64.md index 7de27ae92..e8c3f891b 100644 --- a/docs/topics/encryption/base64.md +++ b/docs/topics/encryption/base64.md @@ -1,33 +1,48 @@ # Base64 -Base64 encoding is widely used amongst different endpoints in Geometry Dash. - -It is used to encode fields like level data, level descriptions, comments, etc. - -GD uses *URL-safe* Base64 encoding, which uses A-Z and a-z letters, along with `_` and `-` as special characters. (Main Base64 uses `+` and `/` special characters) - -Here is an example of using Base64 decoding and encoding: - - - -### **Python** - -```py -import base64 - -# For unknown reasons, people tend to use base64.b64(decode|encode) functions -# and replace "+" with "-" and "/" with "_" manually, -# even though "base64" module implements base64.urlsafe_b64(decode|encode). - -# encode and decode functions return "bytes" type in python so we might want to use -# bytes.decode() to convert them to "str" type. - -def base64_encode(string: str) -> str: - return base64.urlsafe_b64encode(string.encode()).decode() - - -def base64_decode(string: str) -> str: - return base64.urlsafe_b64decode(string.encode()).decode() -``` - - +> Base64 is a method of encoding data which is widely used in the context of Geometry Dash. + +**GD uses a variant of Base64 called url safe Base64 which replaces characters that may unintentionally break requests sent to the servers** + +## Table of characters + +> This table covers the characters url safe base64 use + +| Index | Binary | Char | | Index | Binary | Char | +| :---- | :------- | :--- | :-- | :---- | -------- | ---- | +| `0` | `000000` | A | | `32` | `100000` | g | +| `1` | `000001` | B | | `33` | `100001` | h | +| `2` | `000010` | C | | `34` | `100010` | i | +| `3` | `000011` | D | | `35` | `100011` | j | +| `4` | `000100` | E | | `36` | `100100` | k | +| `5` | `000101` | F | | `37` | `100101` | l | +| `6` | `000110` | G | | `38` | `100110` | m | +| `7` | `000111` | H | | `39` | `100111` | n | +| `8` | `001000` | I | | `40` | `101000` | o | +| `9` | `001001` | J | | `41` | `101001` | p | +| `10` | `001010` | K | | `42` | `101010` | q | +| `11` | `001011` | L | | `43` | `101011` | r | +| `12` | `001100` | M | | `44` | `101100` | s | +| `13` | `001101` | N | | `45` | `101101` | t | +| `14` | `001110` | O | | `46` | `101110` | u | +| `15` | `001111` | P | | `47` | `101111` | v | +| `16` | `010000` | Q | | `48` | `110000` | w | +| `17` | `010001` | R | | `49` | `110001` | x | +| `18` | `010010` | S | | `50` | `110010` | y | +| `19` | `010011` | T | | `51` | `110011` | z | +| `20` | `010100` | U | | `52` | `110100` | 0 | +| `21` | `010101` | V | | `53` | `110101` | 1 | +| `22` | `010110` | W | | `54` | `110110` | 2 | +| `23` | `010111` | X | | `55` | `110111` | 3 | +| `24` | `011000` | Y | | `56` | `111000` | 4 | +| `25` | `011001` | Z | | `57` | `111001` | 5 | +| `26` | `011010` | a | | `58` | `111010` | 6 | +| `27` | `011011` | b | | `59` | `111011` | 7 | +| `28` | `011100` | c | | `60` | `111100` | 8 | +| `29` | `011101` | d | | `61` | `111101` | 9 | +| `30` | `011110` | e | | `62` | `111110` | \_ | +| `31` | `011111` | f | | `63` | `111111` | - | + +## Padding + +> The padding character is `=` however, it is optional as the Geometry Dash servers will automatically correct strings that do not have any padding \ No newline at end of file diff --git a/docs/topics/encryption/chk.md b/docs/topics/encryption/chk.md index d2184bcb2..6236de250 100644 --- a/docs/topics/encryption/chk.md +++ b/docs/topics/encryption/chk.md @@ -84,6 +84,12 @@ def generate_upload_seed(data: str, chars: int = 50) -> str: +## Upload list + +- seed + +Seed is generated by taking the listLevel and obtaining 50 characters the same way as for uploading a level (see above), then appending your account ID to it, then taking a SHA-1 hash of that, then XOR encrypting with the randomly generated `seed2` value and Base64 URL-Safe encoding it. + ## Comment - Username @@ -113,7 +119,16 @@ Random number consisting of *5* digits. - [RS](topics/encryption/id.md?id=rs) - AccountID - [UDID](topics/encryption/id.md?id=udid) -- [UUID](topics/encryption/id.md?id=uuid) +- "UUID" (player/user ID, different from the account ID) + +## Rate + +- LevelID +- Stars +- [RS](topics/encryption/id.md?id=rs) +- AccountID +- [UDID](topics/encryption/id.md?id=udid) +- "UUID" (player/user ID, different from the account ID) ## User Profile @@ -134,6 +149,12 @@ Random number consisting of *5* digits. - Glow (0 = disabled, 1 = enabled) - SpiderID - ExplosionID +- Length of dinfo +- dinfow +- dinfog +- sinfo +- sinfod +- sinfog ## Level Leaderboard @@ -144,14 +165,14 @@ Random number consisting of *5* digits. - Attempts - Seed -Seed can be generated like this: +Seed for classic levels can be generated like this: ### **Python** ```py -def generate_leaderboard_seed( +def generate_classic_leaderboard_seed( jumps: int, percentage: int, seconds: int, has_played: bool = True ) -> int: @@ -164,6 +185,20 @@ def generate_leaderboard_seed( +For platformer levels: + + + +### **Python** + +```py +def generatePlatformerHash(bestTime, bestPoints): + number = (((bestTime + 7890) % 34567) * 601 + ((abs(bestPoints) + 3456) % 78901) * 967 + 94819) % 94433 + return ((number ^ number >> 16) * 829) % 77849 +``` + + + - Best Differences For example, `0%` - `13%` - `100%` -> `(13 - 0), (100 - 13)` -> `13,87` @@ -174,6 +209,13 @@ For example, `0%` - `13%` - `100%` -> `(13 - 0), (100 - 13)` -> `13,87` - TimelyID - [RS](topics/encryption/id.md?id=rs) +## Upload Multiplayer Comment + +- AccountID +- Comment +- GameID +- Extra + ## Salts | Value | Type | diff --git a/docs/topics/encryption/id.md b/docs/topics/encryption/id.md index c8bf5fcd8..9ed04f302 100644 --- a/docs/topics/encryption/id.md +++ b/docs/topics/encryption/id.md @@ -31,7 +31,7 @@ def generate_rs(n: int) -> str: ## UUID [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) stands for Universally Unique IDentifier. -It has format of `aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee` and is sent as `uuid` in requests. +It has format of `aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee` and used to be sent as `uuid` in requests, but now the player ID (not to be confused with the account ID) is sent in its place instead. It can be randomly generated using our `generate_rs()` function: @@ -50,11 +50,10 @@ def generate_uuid(parts: [int] = (8, 4, 4, 4, 10)) -> str: ## UDID [UDID](https://en.wikipedia.org/wiki/UDID) is an abbreviation for Unique Device IDentifier that is sent as `udid` in requests. -It does not really have a defined format, -but frequently has structure like `Sxxx...` where `x` are digits, -or it is the same as PlayerID of a user. -Generating UDID should be simpler because we can just generate a random integer: +On Android, your UDID is generated by making a UUIDv4 from your SSAID (`Settings.Secure.ANDROID_ID`). However, there's a common bug with the SSAID returning the value `9774d56d682e549c` on multiple devices. If the SSAID matches that string, a random UUIDv4 is generated instead. What's interesting is that the UDID on Android is one of the few parts of the Java code, so it's easily decompilable. + +On Windows, the game tries to obtain either the thread or the process [SID](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers) and removes all the dashes from it, which is a really long number starting with `S`. If it fails, the UDID is `NoUDID_`, followed by a random value from 0 to 1,000,000,000. @@ -65,7 +64,7 @@ import random def generate_udid(start: int = 100_000, end: int = 100_000_000) -> str: - return "S" + str(random.randint(start, end)) + return "S15" + str(random.randint(start, end)) + str(random.randint(start, end)) + str(random.randint(start, end)) + str(random.randint(start, end)) ``` diff --git a/docs/topics/encryption/robtop-cipher.md b/docs/topics/encryption/robtop-cipher.md new file mode 100644 index 000000000..1e966b51b --- /dev/null +++ b/docs/topics/encryption/robtop-cipher.md @@ -0,0 +1,58 @@ +# The RobTop Cipher +The RobTop Cipher refers to the combination of Base64 (url-safe) + XOR used frequently within Geometry Dash as a form of encryption. +It is used within places such as vault codes, passwords before 2.2 (GJP) or save data on Windows. +The cipher may utilise either the cycled or static variant of the XOR cipher depending on where the ciphertext originates from (such as save data or the +encoded password). To find out which variant is used, you may consult the [key reference material](/reference/keys.md#xor-keys). + +## Encryption and Decryption +### Encryption +To encrypt something using the RobTop Cipher, you do the following: + +```js +plaintext = "some text" + +// Cyclic XOR RobTop Cipher +cyclic_key = "..." // The XOR Key from the key reference material + +cxor_text = cyclic_xor(plaintext, cyclic_key) +cresult = base64_urlsafe_encode(cxor_text) // Our result + +// Static XOR RobTop Cipher +static_key = 0xFF // The XOR Key from the key reference material + +sxor_text = static_xor(plaintext, static_key) +sresult = base64_urlsafe_encode(sxor_text) // Our result. +``` + +### Decryption +To decrypt something using the RobTop cipher, you do the following: + +```js +ciphertext = "..." + +// Cyclic XOR RobTop Cipher +cyclic_key = "..." // The XOR Key from the key reference material + +cxor_text = base64_urlsafe_decode(ciphertext) +cresult = cyclic_xor(cxor_text, cyclic_key) // Our result. + +// Static XOR RobTop Cipher +static_key = 0xFF // The XOR Key from the key reference material + +sxor_text = base64_urlsafe_decode(ciphertext) +sresult = static_xor(sxor_text, static_key) // Our result. +``` + +## Types of XOR Used +### Static XOR +Within the static XOR RobTop Cipher, the key should be treated as a byte. Then, all bytes of the string must be iterated over, XORing each unicode code of a +character by the static key to form a new string. + +By the very nature of the XOR cipher, decoding involves the same process as encoding. + +### Cyclic XOR +Within the cyclic XOR RobTop Cipher (the most common variant), the key should be treated as a string or a sequence of UTF-8 character codes. +The differentiating factor between cyclic and static XOR is that cyclic XOR uses a key longer than one byte. This means that the key has to be +repeatedly looped over during the string iteration. + +Please refer to the [XOR](/topics/encryption/xor.md) section for details on the implementation diff --git a/docs/topics/encryption/xor.md b/docs/topics/encryption/xor.md index 96d4d141e..79e6b8caa 100644 --- a/docs/topics/encryption/xor.md +++ b/docs/topics/encryption/xor.md @@ -1,104 +1,58 @@ # XOR -[XOR](https://en.wikipedia.org/wiki/Bitwise_operation#XOR) is a _bit-wise_ binary operation that is commonly written as `^` in programming languages. +> XOR is a bitwise operation used by Geometry Dash to encrypt data. It is commonly denoted as a `^` in many programming languages -Each character in a string is essentially represented by a number, -so-called _codepoint_, to which XOR operation can be applied. +- **Geometry Dash has 2 methods to encrypting data using XOR** + - Using a singular key + - Cycling through a 5 digit key - - -### **Python** - -In python, you can use `ord()` function to get the codepoint of a character, -and `chr()` to convert the codepoint to a character. - -```py ->>> ord("N") -78 ->>> chr(78) -"N" -``` - - - -## XOR Cipher - -**XOR-Cipher** elaborates on the idea of applying _XOR_ to each to characters, -one in the string and one in the key. - -Here is our **XOR-Cipher** stub (empty) function: - - - -### **Python** - -```py -def xor_cipher(string: str, key: str) -> str: ... -``` +When needed, Geometry Dash iterates through every byte of the data and applies the XOR operation using its key - - -Suppose we have a string `"GD"` (how original), and key `"42069"` (rather original as well). +## Examples -Here is what will happen to our string if we apply **XOR-Cipher**: +Below are pseudocode examples of the algorithms used -### **Python** +### **Singular** -```py ->>> chr(ord("G") ^ ord("4")) + chr(ord("D") ^ ord("2")) -"sv" +```js +result = ""; +for (i = 0; i < input.length; i++) { + byte = input[i].toByte(); + result += (byte ^ key).toChar(); +} ``` - - -**XOR-Cipher** connects each character in given string with character in key (key is cycled), then applies _XOR_ operation on each pair. - -Returning back to the function: - - - -### **Python** +### **Cycle** -```py -import itertools - -# we are going to use itertools.cycle() on our key, which will basically -# repeatedly yield "1234512345..." for key "12345" - -def xor_cipher(string: str, key: str) -> str: - result = "" - for string_char, key_char in zip(string, itertools.cycle(key)): - result += chr(ord(string_char) ^ ord(key_char)) - return result -``` - -This function is quite good, but adding new characters to strings in python is quite slow. -Here is a better function that implements **XOR-Cipher** (and hey, it is written in one line!): - -```py -def xor_cipher(string: str, key: str) -> str: - return ("").join(chr(ord(x) ^ ord(y)) for x, y in zip(string, itertools.cycle(key))) +```js +result = ""; +for (i = 0; i < input.length; i++) { + byte = input[i].toByte(); + xKey = key[i % key.length].toByte(); + result += (byte ^ xKey).toChar(); +} ``` -## XOR Keys - -Here is a list of XOR keys currently used in GD: - -| Key | Usage | -| ----- | ----------------- | -| 14251 | Messages | -| 26364 | Level Password | -| 37526 | Account Password | -| 39673 | Level Leaderboard | -| 41274 | Level Seed | -| 29481 | Comment CHK | -| 19847 | Challenges | -| 59182 | Rewards | -| 58281 | Like and Rate | -| 85271 | User Profile | -| 19283 | Vault Codes | -| 48291 | Load data | +### Keys + +| Key | Usage | XOR Type | +| :------ | :-------------------------- | :------- | +| `11` | Player Save Data | Singular | +| `14251` | Player Messages | Cycled | +| `19283` | Vault Codes | Cycled | +| `19847` | Daily Challenges | Cycled | +| `26364` | Level Password | Cycled | +| `29481` | Comment Integrity | Cycled | +| `37526` | Account Password | Cycled | +| `39673` | Level Leaderboard Integrity | Cycled | +| `41274` | Level Integrity | Cycled | +| `48291` | Load Data | Cycled | +| `52832` | Multiplayer | Cycled | +| `57709` | Music/SFX Library Secret | Cycled | +| `58281` | Rating Integrity | Cycled | +| `59182` | Chest Rewards | Cycled | +| `85271` | Stat Submission Integrity | Cycled | diff --git a/docs/topics/gjp.md b/docs/topics/gjp.md index 47aab3bc6..e683c64c8 100644 --- a/docs/topics/gjp.md +++ b/docs/topics/gjp.md @@ -1,8 +1,28 @@ # GJP GJP is a parameter commonly sent as `gjp` in requests. It is used for account authentication, and commonly sent with the `accountID` parameter. +It was changed to `gjp2` in 2.2. -## Encoding/Decoding +## Generating GJP2 + +The GJP is your password, salted with "mI29fmAnxgTs" and hashed with SHA-1. + + + +### **Python** +```py +import base64 +import hashlib # sha1() lives there + + +def generate_gjp2(password: str = "", salt: str = "mI29fmAnxgTs") -> str: + password += salt + hash = hashlib.sha1(password.encode()).hexdigest() + + return hash +``` + +## Old GJP Encoding/Decoding The GJP is your account password XOR-encoded with key "37526", and then encoded with base64. @@ -22,8 +42,8 @@ def encode_gjp(password: str) -> str: encoded = xor_cipher(password, "37526") # encode the password to base64 encoded_base64 = base64.b64encode(encoded.encode()).decode() - encoded_base64 = encoded_base64.replace("+", "-") - encoded_base64 = encoded_base64.replace("/", "_") + encoded_base64 = encoded_base64.replace("+", "-") + encoded_base64 = encoded_base64.replace("/", "_") return encoded_base64 diff --git a/docs/topics/localfiles_encrypt_decrypt.md b/docs/topics/localfiles_encrypt_decrypt.md index b3c7a3223..7b3a38a4e 100644 --- a/docs/topics/localfiles_encrypt_decrypt.md +++ b/docs/topics/localfiles_encrypt_decrypt.md @@ -54,11 +54,16 @@ On MacOS, decryption is quite simpler. Saves are encrypted with -### **Plain** +### **Hexadecimal** + ```md + 69 70 75 39 54 55 76 35 34 79 76 5d 69 73 46 4d 68 35 40 3b 74 2e 35 77 33 34 45 32 52 79 40 7b + ``` + +### **Plaintext** + ``` + ipu9TUv54yv]isFMh5@;t.5w34E2Ry@{ + ``` -```plain -69 70 75 39 54 55 76 35 34 79 76 5d 69 73 46 4d 68 35 40 3b 74 2e 35 77 33 34 45 32 52 79 40 7b -``` ### **Python** @@ -97,6 +102,8 @@ def mac_decrypt(data: bytes) -> str: ## Encryption +Note that you don't actually need to reencrypt the save file in order for the game to accept it. + ### Windows Encryption is done pretty much the same way but with opposite operations and order. So the sequence for encrypting can be defined as: [gzip](https://zlib.net) compress/deflate -> [Base64](topics/encryption/base64) encode -> XOR using `0xb` (`11`) as a key. diff --git a/docs/topics/tags.md b/docs/topics/tags.md index f5669ec32..5530ecc85 100644 --- a/docs/topics/tags.md +++ b/docs/topics/tags.md @@ -1,33 +1,70 @@ # Tags -Throughout Geometry Dash there are various interfaces in which the player an see specialised text. Specialised text is created using a custom tag which Robtop has created. As of Geometry Dash 2.11, there are 3 different types of tags. +Various text interfaces within the Geometry Dash client can be manipulated using special tags similar to markup languages such as `HTML`. Geometry Dash has 4 primary types of tags: -> - `` tags assign a specific colour depending on the letter you give you give it. All `` tags must be closed with `` otherwise the game will crash.

-> - `` tags manipulate the delay before a piece of text is sent within a dialog box. The speed you can set is within a range of `000` to `999` ms.

-> - `` tags are the simplest tags out of everything and all they do is make text appear immediately. `` tags must be closed with `` otherwise the game will crash. +- Colour Tags +- Fade Tags +- Delay Tags +- Shake Tags -## \ Tags +## Colour Tags -There are `9` different colour tags that are usable in Geometry Dash. below is a table of them all + + -**Note:** The `\` in the examples are **not** a part of a tag! It has been placed there to show the colour of the tag with the game still showing the tag as plain text. +Colour Tags are used to style areas of text with colour. They contain both a start and an end tag - the start tag defining which colour should be rendered on screen and the end tag denoting when to stop reading. -| tag | Colour | Hex Code | -|:----|:-------|:---------| -| `` | ![Orange](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/orange.png) | `0xFFA54B` | -| `` | ![Yellow](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/yellow.png) | `0xFFFF48` -| `` | ![Green](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/green.png) | `0x40E348` | -| `` | ![Light Blue](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/lightBlue.png) | `0x32C8FF` | -| `` | ![Blue](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/blue.png) | `0x4A52E1` | -| `` | ![Purple](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/purple.png) | `0xFF00FF` -| `` | ![Very Light Blue](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/veryLightBlue.png) | `0x60ABEF` | -| `` | ![Red](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/red.png) | `0xFFA548` | -| `` | ![dark Red](https://raw.githubusercontent.com/Wyliemaster/gddocs/client/assets/examples/tags/any.png) | `0xFF0000` | +**Usage:** `Coloured Text!` -## \ Tags + -`` tags are used to create a delay before a specific string in dialog boxes. The game detects a `Delay Tag` if the string contains a `` | `0x4A52E1` | Sample | +| `` | `0x40E348` | Sample | +| `` | `0x60ABEF` | Sample | +| `` | `0x32C8FF` | Sample | +| `` | `0xFFFF00` | Sample | +| `` | `0xFF5A4B` | Sample | +| `` | `0xFF5A5A` | Sample | +| `` | `0xFF00FF` | Sample | +| `` | `0x9632FF` | Sample | +| `` | `0xFF96FF` | Sample | +| `` | `0xFFFF96` | Sample | +| `` | `0x96FFFF` | Sample | +| `` | `0xFFDC41` | Sample | +| `` | `0xFF0000` | Sample | -## \ Tags +## Instant/Fade Tags -`` tags are the complete opposite to `Delay Tags` as they display text instantly rather than after a delay. `` tags also require a closing tag which is `` so the game does not crash when parsing the string. An example of an `` tag can be found [here](https://github.com/Wyliemaster/gddocs/blob/client/assets/examples/tags/I%20tags%20example.mp4?raw=true) \ No newline at end of file +Fade Tags are used to fade in a block of text on screen instead of making it appear character by character. Similarly to colour tags, Fade tags have a start and end tag to denote which piece of text should appear instantly. The number is specified in centiseconds, which is 1/100th of a second. + +**Usage:** `Hello, world!` + +![fade-in preview](../assets/images/fadein_tag.gif) + +## Delay Tags + +Delay tags are used to create a delay before a specific string in dialog boxes. The game detects a Delay Tag if the string contains a `...` + +![delay preview](../assets/images/delay_tag.gif) + +## Shake Tags + +Shake Tags are used to render shaky text on screen. The number denotes the intensity of the shake. + +**Usage:** `CHOPPER!` + +![shake preview](../assets/images/shake_tag.gif) + +## Notes + +- In 2.1, failing to add an end tag for Colour tags and Instant tags would result in the game crashing, but this was fixed in 2.2 + +- In 2.1, the Fade tags actually made the block of text appear instantly, and so no number was required. To emulate the old behavior, you can use `` + +- Only Colour tags are usable without modifying the client - via level descriptions and comment bans + +- The tags are defined within the `MultilineBitmapFont` class and are sometimes disabled within the create method using a bool diff --git a/docs/topics/vault_codes.md b/docs/topics/vault_codes.md index 5375d8fc2..f5878e25e 100644 --- a/docs/topics/vault_codes.md +++ b/docs/topics/vault_codes.md @@ -1,6 +1,6 @@ # Vault Codes -In Geometry Dash 2.1 there are three different vaults you can access. The Vault, Vault of Secrets and the Chamber of Time. Each vault has a select number of passwords that you can enter to unlock icons and colors. +In Geometry Dash 2.2 there are four different vaults you can access. The Vault, Vault of Secrets, Chamber of Time and The Wraith. Each vault has a select number of passwords that you can enter to unlock icons and colors. The Wraith is the first vault that is server-sided, meaning the rewards are checked on the server, they can change over time and can not be found by reverse engineering. ## Vault Code Encryption @@ -23,16 +23,21 @@ Vault of Secrets and Chamber of time: [VaultCode] + ask2fpcaqCQ2 -> Xor with a k | The Vault | robotop | robot | The Vault | gandalfpotter | trail | The Vault | sparky | coin +| The Vault | finalboss | swing | Vault of Secrets | your star count | cube -| Vault of Secrets | CodeBreaker | cube | you are given a sequence of numbers you have to subtract the numbers from each other and the code is all the numbers subtracted combined if the numbers were 1,2,4,8,16,32 then you would do 2-1, 4-2, 8-4, 16-8, 32-16 and the code would then be 124816 +| Vault of Secrets | cod3breaker | cube | you are given a sequence of numbers you have to subtract the numbers from each other and the code is all the numbers subtracted combined if the numbers were 1,2,4,8,16,32 then you would do 2-1, 4-2, 8-4, 16-8, 32-16 and the code would then be 124816 | Vault of Secrets | brainpower | cube | Vault of Secrets | octocube | cube | Vault of Secrets | seven | cube | Vault of Secrets | thechickenisonfire | colour2 | Vault of Secrets | gimmethecolor | color1 | Vault of Secrets | glubflub | coin +| Vault of Secrets | d4shg30me7ry | cube +| Vault of Secrets | thechickenisready | ship | Chamber Of Time | silence | cube | Chamber Of Time | hunger | cube | Chamber Of Time | darkness | cube | Chamber Of Time | volcano | wave | 'a volcano' also works | Chamber Of Time | river | color2 | 'a river' also works +| Chamber Of Time | backontrack | spider +| Chamber Of Time | givemehelper | robot diff --git a/generator.js b/generator.js index d166c0e1a..79c446c45 100644 --- a/generator.js +++ b/generator.js @@ -1,19 +1,26 @@ -/* eslint no-console: ["error", { allow: ["log", "warn", "error"] }] */ -const childProcess = require("child_process"); -const chalk = require("chalk"); -const os = require("os"); +// eslint-disable-next-line no-console +import { exec } from 'child_process'; +import os from 'os'; -require("./scripts/installPackages.js"); // install packages +// Import local script as an ES module +import './scripts/installPackages.js'; -// serve data +// Define the command to serve documentation let command = "node ./node_modules/docsify-cli/bin/docsify serve ./docs --port 9505"; if (os.platform() === "win32") { + // Adjust command for Windows command = "node \"./node_modules/docsify-cli/bin/docsify\" serve ./docs --port 9505"; } -console.log(`${chalk.hex("#66d9ff")("Running Command: ")} ${command}\n`); +console.log(`${("Running Command: ")} ${command}\n`); -const docsProcess = childProcess.exec(command); +// Execute the command +const docsProcess = exec(command); -docsProcess.on("exit", () => process.exit(0)); \ No newline at end of file +if(docsProcess.exitCode === null) { + console.log("http://localhost:9505") +} +docsProcess.on("exit", () => process.exit(0)); + +// Handle process exit diff --git a/package.json b/package.json index 3039af308..7be6ea7c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "gddocs", "version": "0.0.1", + "type": "module", "description": "GD Documentation", "main": "generator.js", "scripts": { @@ -20,7 +21,6 @@ }, "homepage": "https://github.com/gd-programming/gddocs#readme", "dependencies": { - "chalk": "^4.0.0", - "docsify-cli": "^4.4.0" + "docsify-cli": "^4.4.4" } } diff --git a/scripts/installPackages.js b/scripts/installPackages.js index abd31377f..12afaba26 100644 --- a/scripts/installPackages.js +++ b/scripts/installPackages.js @@ -1,14 +1,17 @@ // install_packages.js // made by Homura -const chalk = require("chalk"); -const fs = require("fs"); -const childProcess = require("child_process"); +import fs from 'fs'; +import { exec } from 'child_process'; -const package = require("../package.json"); -const missing = Object.keys(package.dependencies) - .filter((package) => !fs.existsSync(`../node_modules/${package}`)); +// Read package.json synchronously +const packageJsonContent = fs.readFileSync(new URL('../package.json', import.meta.url)); +const packageJson = JSON.parse(packageJsonContent); + +// Filter out packages that are not installed +const missing = Object.keys(packageJson.dependencies) + .filter((packageName) => !fs.existsSync(`../node_modules/${packageName}`)); if (missing.length > 0) { - console.log(chalk.hex("#79b0fc")("Installing Packages: ") + missing.join(", ")); - childProcess.exec(`npm i --save ${missing.join(" ")}`); + console.log(("Installing Packages: ") + missing.join(", ")); + exec(`npm i --save ${missing.join(" ")}`); } \ No newline at end of file