Le monde des portages PC de jeux rétro s’enrichit d’une nouvelle mise à jour majeure avec The Legend of Zelda: Majora’s Mask Recomp version 1.2. Cette dernière version apporte des fonctionnalités attendues par la communauté et transforme l’expérience de ce classique de Nintendo 64. Passons en revue ensemble les nouveautés et les possibilités offertes par ce projet non officiel qui permet de profiter du chef-d’Å“uvre de Nintendo sur ordinateur.
Majora’s Mask Recomp 1.2 : support des mods et personnalisation
L’équipe derrière Zelda 64: Recompiled vient de franchir une étape décisive avec cette version 1.2 qui métamorphose l’expérience utilisateur. La grande nouveauté réside dans l’intégration complète du support des mods, permettant aux joueurs de personnaliser leur aventure comme jamais auparavant.
L’installation des modifications est désormais d’une simplicité remarquable. Deux méthodes s’offrent aux utilisateurs :
- Glisser-déposer les mods directement sur la fenêtre du jeu
- Utiliser le bouton « Install Mods » dans le menu dédié avant de lancer une partie
- Accéder à la plateforme Thunderstore pour télécharger une variété de mods
- Combiner plusieurs mods pour une expérience totalement personnalisée
Cette fonctionnalité ouvre la porte à une créativité sans limite pour la communauté des moddeurs. Les joueurs peuvent maintenant transformer radicalement leur expérience de jeu, des ajustements mineurs aux refont Specifying the Namespace Schema
Before starting to write the namespace schema, I first needed to specify what data it should contain. Recalling the state information in the file system, here are the name and data constraints for the namespace:
* All type names must be valid .NET type names, that is, a basic identifier (starting with a letter or underscore and containing only letters, digits, and underscores), optionally preceded by a namespace (which is a sequence of such identifiers separated by periods).
* Every type must have a key, and each namespace must have a unique collection of types, identifiable by their keys.
* Each type must have a source (a file system path), methods, and fields.
* Each method must have a name (following the same rules as type names), a return type (which can be void or a type name), zero or more parameters, and a body (which is the source code enclosed in braces).
* Each parameter must have a type and a name, and each method must have a unique collection of parameters.
* Each field must have a type and a name, and each type must have a unique collection of fields.
* Each method’s body may be a block of code with embedded statements, or it might be a skeleton to be augmented by other code-generation activites.
This seems like a straightforward problem on the surface, given the constraints. However, it’s actually a bit tricky to enforce the uniqueness of methods. One issue is that methods can be overloaded, and the parameter list distinguishes one method from another with the same name. Consider these methods:
« `
class C
{
void M() { }
void M(int i) { } // overloaded based on parameters
void M(string s) { }
}
« `
The uniqueness of these methods lies both in the method name and in the signature of its parameters. If I try to add a method « void M(int i) » to class C, I should get a uniqueness constraint violation. This means I need a computed key for methods that combines the method name and its parameter signature.
Another issue is the body of the method, which needs to be able to produce syntax appropriate for the context (for example, for a field initializer method must be different from what in the general method context). I’ll approach the problem by declaring a class for the method body, but the default implementation will just be the braces and source nested inside.
Parameterization will be useful in the field updates. For example, I don’t think I need to declare a special notion of the constructor initializer list; rather, I can parameterize the field methods. That is, I’ll need methods like:
* GetFields to walk through a collection of field expressions
* GetConstructorInitializerList to create the initializer list
This suggests using a visitor pattern to gather and process specialized lists.
Let’s start by mapping out the schema. Here’s what I’ve got so far:
« `XML
« `
Let’s refine this design to support my specific goals:
1. The model is an in-memory representation of the namespace, types, methods, and fields.
2. I need to be able to read the source code for types from files.
3. I need to be able to update source files with new methods and fields.
4. I need to be able to update existing methods with new code.
5. I need to be able to modify field and method declarations without completely regenerating the file.
Given these requirements, I might need more specialized types in the schema. Let’s look at expanding the Method and Field entites to capture more of the semantic variety.
« `XML
« `
On second thought, I think the key design parameters are:
1. Capture the structure of the namespace, types, methods, and fields.
2. Support the uniqueness constraints.
3. Support the basic syntax of the language.
I don’t need to design a full compiler or language model; I just need enough to support the code generation. So, let’s keep it simple and focus on the basic structure. Maybe I’ll need to extend the model in the future. Maybe I’ll need a Visitor pattern to traverse the structure, but I can add that later.
So far, the schema I have looks fine for the specific goals in mind. It’s a good starting point. I’ll keep the schema simple and enhance it if needed. With this schema, I can represent the namespace, read and write code, and support the basic code generation tasks I’ve outlined.
Is there anything specific from my goals that isn’t covered by this schema?
For the Type, I might want to include more information about the type, like whether it’s a class, interface, struct, etc. Let’s add a « Kind » field to the Type entity. But I’ll add more information as needed based on experience with the schema, initially using a generic string field for flexibility and extending to an enum if that seems helpful.
« `XML
« `
Actually, let’s also add a « Modifiers » field to both methods and fields to handle visiblity, static, readonly, etc. modifiers.
« `XML
« `
There, I think we now have a good starting schema for our namespace model. I’ll probably need to extend it, but this is a good starting point for our C# code generation needs.
Actually, I realize I need to remove the hardcoding of the Key field in Type and Method and instead use a Computed attribute for keys. This makes things cleaner. Let’s fix that:
« `XML
« `
Actually, I wasn’t planning to include the namespace information in the Type’s key, but that’s not needed; the Type’s name is unique within a Namepace, so that’s fine.
Let’s finalize this version and move forward with it:
« `XML
« `
Now that we have a basic schema, we can use the code generator to create the C# classes for our namespace model.
Whoops! I notice the ComputedKey syntax isn’t exactly what I wanted. In C# code, I’m simply going to need to calculate a string key. I don’t think the metadata system I’ve designed supports a ComputedKey with a general expression. Let’s see what I can use.
Based on my description, here’s what I think I can use:
« `XML
« `
Actually, I just realized for Computed fields, I’ll need to actually calcluate the value in the code generator. This is a simple expression, but I’m not sure the generator can handle it.
Let’s try this instead:
« `XML
« `
Wait, I’m overthinking this. I don’t need a Formula or other construct. I can just declare a rule where the implementation must enforce that the Key field is generated from other fields. I’ll revert to my key model, but I’ll need to make sure the code that creates Method objects puts a unique method signature in the Key property.
« `XML
« `
On further reflection, I’m going to revisit how I’m constructing the key. For Type, I’m going to use the Name as the key.
For Method, I could use the signature as the key, but that’s a bit complex. The signatures are potentially complex strings, and I want to make sure I get the formatting consistent, especially with parameter type and names. Additionally, I’m now rethinking my need to support overloaded methods. The purpose of this schema is to support code generation, not to completely model the language. So, I’m going to simplify and either use a simple Name as key for methods (so overloaded methods are not supported), or I’ll provide a constructor that initializes the Key field.
Let’s try this:
« `XML
!– use the Name as the key; no support for overloaded methods –>
« `
However, I do want to add a constructor to all the entities to make it easier to create them. Also, the MethodBody class needs to have a property to get the source code.
Let’s make these changes:
« `XML
!– use the Name as the key; no support for overloaded methods –>
« `
I think I’m overthinking the schema, especially when it comes to the Method key. Let’s make an executive decision to keep it for now. The Type name is guaranteed to be unique in a Namespace, and the Method name is unique within a Type, regardless of parameter signatures (that is, no support for overloaded methods). Generally, for code generation, we don’t need to support overloaded methods.
Now, let’s make a final revision of the schema:
« `XML

