Giter Site home page Giter Site logo

kabili207 / zora-sharp Goto Github PK

View Code? Open in Web Editor NEW
45.0 9.0 22.0 1.76 MB

A decoder for the password system used in the Legend of Zelda Oracle of Ages and Oracle of Seasons games.

License: GNU Lesser General Public License v3.0

C# 100.00%
zelda c-sharp

zora-sharp's Introduction

ZoraSharp Build Status Build status codecov.io documentation FOSSA Status

A library for working with the password system used in the Legend of Zelda Oracle of Ages and Oracle of Seasons games. Inspired by the original password generator written by Paul D. Shoener III a.k.a. Paulygon back in 2001.

The name comes from a contraction of Zelda Oracle and from the Zora, one of the games' races and enemies.

User Interfaces

ZoraSharp is only a library for manipulating the codes from the Oracle series games, meant for use by developers. General users should instead use of the the following user interfaces:

Features

  • Decodes game and ring secrets
  • Generates game, ring, and memory secrets
  • Allows import and export of game data using a json-based .zora file

Using the library

Full documentation for the current stable release of ZoraSharp can be found at http://kabili207.github.io/zora-sharp/api-doc/. Below are functions to handle the most common scenarios.

Regions

There are two supported regions, US and JP. (The PAL region is nearly identical to the US region for the purpose of secrets.)

Getting the raw secret

ZoraSharp uses byte arrays for most operations with the secrets. Most people don't go passing byte values around, however, opting for a more readable text representation. These secret strings can be parsed like so:

string gameSecret = "H~2:@ left 2 diamond yq GB3 circle ( 6 heart ? up 6";
byte[] rawGameSecret = SecretParser.ParseSecret(gameSecret, GameRegion.US);

The ParseSecret method is fairly flexible in what it will accept. Possible formats include 6●sW↑, 6 circle s W up, and 6{circle}sW{up}. You can even combine the formats like so: 6cIrClesW↑. Brackets ({ and }) also ignored, so long as they are next to a symbol word such as circle or left.

Whitespace is also ignored. This does not cause any issues with the symbol words because the list of valid characters does not include any vowels.

Getting a secret string

It's also possible to take the raw bytes and convert them back into a readable string value.

byte[] rawSecret = new byte[]
{
     4, 37, 51, 36, 63,
    61, 51, 10, 44, 39,
     3,  0, 52, 21, 48,
    55,  9, 45, 59, 55
};
string secret = SecretParser.CreateString(rawSecret, GameRegion.US);
// H~2:@ ←2♦yq GB3●( 6♥?↑6

The CreateString method is far less flexible than it's counter-part, and will only return a UTF-8 string, as shown above.

Loading a secret

Secrets can be loaded from a string...

string gameSecret = "H~2:@ left 2 diamond yq GB3 circle ( 6 heart ? up 6";
Secret secret = new GameSecret();
secret.Load(gameSecret, GameRegion.US);

...or from a byte array

// H~2:@ ←2♦yq GB3●( 6♥?↑6
byte[] rawSecret = new byte[]
{
     4, 37, 51, 36, 63,
    61, 51, 10, 44, 39,
     3,  0, 52, 21, 48,
    55,  9, 45, 59, 55
};
Secret secret = new GameSecret();
secret.Load(rawSecret, GameRegion.US);

Loading a ring secret

// L~2:N @bB↑& hmRh=
byte[] rawSecret = new byte[]
{
     6, 37, 51, 36, 13,
    63, 26,  0, 59, 47,
    30, 32, 15, 30, 49
};
Secret secret = new RingSecret();
secret.Load(rawSecret, GameRegion.US);

Creating a game secret

GameSecret secret = new GameSecret()
{
    GameID = 14129,
    TargetGame = Game.Ages,
    Hero = "Link",
    Child = "Pip",
    Animal = Animal.Dimitri,
    Behavior = 4,
    IsLinkedGame = true,
    IsHeroQuest = false,
    WasGivenFreeRing = true
};
string secretString = secret.ToString();
// H~2:@ ←2♦yq GB3●( 6♥?↑6
byte[] data = secret.ToBytes();

Creating a ring secret

RingSecret secret = new RingSecret()
{
    GameID = 14129,
    Rings = Rings.PowerRingL1 | Rings.DoubleEdgeRing | Rings.ProtectionRing
};
string ringSecret = secret.ToString();
// L~2:N @bB↑& hmRh=
byte[] data = secret.ToBytes();

Creating a memory secret

MemorySecret secret = new MemorySecret()
{
    GameID = 14129,
    TargetGame = Game.Ages,
    Memory = Memory.ClockShopKingZora,
    IsReturnSecret = true
};
string secret = secret.ToString();
// 6●sW↑
byte[] data = secret.ToBytes();

The .zora save file

The .zora file contains all relevent information to recreate a player's game and ring secrets. Data is saved as a JSON object, with the intention that it can be used with other implementations of the password system.

{
    "Region": "US",
    "Hero": "Link",
    "GameID": 14129,
    "Game": "Ages",
    "Child": "Pip",
    "Animal": "Dimitri",
    "Behavior": 4,
    "IsLinkedGame": true,
    "IsHeroQuest": false,
    "WasGivenFreeRing": true,
    "Rings": -9222246136947933182
}

Valid values for Game are Ages or Seasons. This value refers to the target game.

Valid values for Animal are Ricky, Dimitri, or Moosh.

The rings are saved as a 64 bit signed integer. A signed integer was chosen to maintain compatibility with languages that don't support unsigned integers.

None of the fields are required; the ZoraSharp library will load whatever is present, however the same cannot be guaranteed for other libraries that implement the .zora save file.

Special Thanks

License

FOSSA Status

zora-sharp's People

Contributors

carys-the-weed-cloud avatar catobat avatar fossabot avatar friendlyanon avatar henrygab avatar kabili207 avatar stewmath avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zora-sharp's Issues

Unknown bit flags

There are currently four unknown bit values used when creating or parsing a game secret.

  • Bit 58: default 0
  • Bit 59: default 0
  • Bit 76: default 1 0 (flag indicating if Vasu has given you the free Friendship Ring)
  • Bit 88: default 1

Considering 58 and 59 are located right next to each other, it's possible that they hold a 2-bit integer value (0, 1, 2, or 3). There is also the possibility that all four bits form an integer value (0 through 15). Bits 58 and 59 are part of the child's behavior. What the values actually represent is still unknown.

As @M1CR0H4CK3R pointed out in issue #1, one or more of these values is likely used to store whether or not Maple has the vacuum cleaner.

How do the secrets work ?

I am making an Oracle fangame and intend to make my game compatible with every Game and Ring Secret available in the original games, but I need to know how a secret is generated to make my game understand the passwords. Can you help me ?

Perform validation of values entered

There are various combinations of values that could result in unexpected behavior when a secret is used. Functions to validate a secret should be created to check for these cases.

  • Child values should be constrained to a maximum value for linked secrets. See this analysis.
  • Function should also be created to allow users to create a valid value from rules used by the game. (added with 10e1b7d)
  • The free ring given value should be checked for each secret type.
  • None should not be an allowed animal friend value when creating a secret for Seasons. (See bug #7)
  • Verify that users haven't inserted invalid values into enums
    • Animal friend
    • Memory values
  • Allow loading of rings from mismatched game IDs

Remove "None" for the animal companion

I thought leaving it in would be ok, but I was mistaken. In Ages, it checks for "None" (0) and gives you Moosh in that case. In Seasons, though, it doesn't check at all. It stays at "0" and you get no companion. A linked-game secret made for Seasons in this way will not be completable!

Blazor WebUI

This library now has a Blazor implementation in a form of an installable PWA.
You can find said implementation here.

Memory Secret Load

Hello, thanks for following my repo. I noticed in your MemorySecret.cs load function you loop through all 4 combinations to find a match (and commented "it's ugly but it works"). I believe IsReturnSecret should always be true for a 1 bit immediately after the memory ID, as decodedSecret[24] in your code. The TargetGame can then be deduced from that and the following bit. I found that the target game could be either Seasons or Ages depending on which game was started. The other pair are the hero secret, so in my own code I xor against the initial game in the sequence of 4 to derive the memory event names in the GUI.

I've done some work on conversion code here, which you might be able to adapt and hopefully our secrets should match.

https://bitbucket.org/alexbramham/zelda-oracles-api/src/master/src/main/kotlin/zelda/oracles/parser/MemorySecretParser.kt

If you have a moment, could you try the Java editor I put together? There's a download link here, only tested on Mac but should display with icons on the tabs and automatically repopulate the secrets when one is entered on the furthest tab or values changed in the first one. I've tested a lot of secret combinations and had success with them so far. (There were quite a few mistakes in the old Java implementation I hacked together). Do you have any suggestions? Thanks!

https://bitbucket.org/alexbramham/zelda-oracles-api/downloads/zelda-oracles.jar

Wrong Cipher

This is the correct one:

private static readonly byte[] Cipher =
{ 
    21, 35, 46,  4, 13, 63, 26, 16,
    58, 47, 30, 32, 15, 62, 54, 55,
     9, 41, 59, 49,  2, 22, 61, 56, 
    40, 19, 52, 50,  1, 11, 10, 53,
    14, 27, 18, 44, 33, 45, 37, 48,
    25, 42,  6, 57, 60, 23, 51, 24
};

As you can see, it ends with 24, which is needed to decode the checksum byte when the cipher key is 7. Also, it starts with 21 instead of 7, this corrects the alternating 0s and 1s when decoding some secrets.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.