Friday, May 12, 2023

using between

 

Bon, imaginer un gameplay sympa avec des ponts, c'est une chose. Mais comment programmer le comportement de ce genre de ponts au fait ? hein ? La bonne nouvelle, c'est qu'on a re-fait un passage dans une autre pleine de jeu avec encore plus de ponts, de cordes, et j'en passerelles et des meilleures. Une image comme ci-dessus, par exemple, suggère que Bilou est nettement plus massif que le reste des rondins, sans quoi il ne perturberait pas autant la répartition des charges sur la corde.

So, I now have fun ideas of how to use bridges in Bilou: Dreamland. Sounds like it could be a good idea to also figure out how I'm going to implement such things with my game engine... One thing is almost obvious: the bridge will be made of a set of independent-but-interacting game objects. I'm not going to try and make it one big animated object. If I did, I'd have to manually adjust collision boxes for segments for every frame. That's not what it is for. Same observation applied to the Green Zone boss: Big Caterpillar. And much like the bridge, Big Caterpillar is made of segments that must be able to interact to give the illusion of a single object.

Mais revenons un cran en arrière, voulez-vous ? Alors que J.L.N était face à son premier mini-boss dans Minish Cap, je me suis mis à prendre quelques notes sur le type de contrôleurs qu'il faudrait ajouter à mon moteur de jeu pour pouvoir faire quelque-chose de ce genre. Puis, sur la lancée, à ce que je pouvais en tirer comme conclusions pour la réalisation de Big Caterpillar, le boss de la Green Zone.

Il y a déjà une chose que le moteur de jeu de School Rush peut faire: c'est attacher un Gob (objet mobile fait de sprite(s)) à un autre, et utiliser sa position dans certains contrôleurs. C'est ainsi p.ex. que la chauve-souris-baie se dirige vers Bilou. ou que SpongeBop sait se balancer au bout d'un clou. C'est encore comme ça qu'on peut transporter un dumblador et que ses pieds le suivent à la trace. Mais dans tous ces exemples, on a un seul objet de référence et un objet référent. Quand il y a plusieurs référents (3 BerryBats) pour un même objet de référence (Bilou), ils s'ignorent les uns les autres. Or, pour permettre de positionner les segments internes de Big Caterpillar entre sa tête et sa queue, il faut les coordonnées des deux autres. C'est forcé :P

So since I've just been studying a bit how BC could be implemented, let's see what it can teach us. The game engine can attach one Gob to another one. That's how SpongeBop swings and how BerryBat aim at their target. It is quite clear that those "attach" links will play a role in animating BC, but the question is "who should be attach with whom, and why". When BC will be crawling, for instance, it is easy to make his head move forward during the "expanse" phase and then make the tail move forward (while the head is stopped) in the "contract" phase. As the distance between head and tail shrinks or grows, the location of other segments should adjust.

It is quite easy to find math expression for those locations based on head and tail coordinates. Unfortunately, that'd require *two* attach links per segment, and the game engine only handle one (including in expression opcodes to attach and detach things when collisions occur). I either have to revise the whole engine and break the assumption that One Attach Link Shall Be Enough For Everybody, or I have to come up with some clever way out. That way out could be how a between(...) controller could use not only the attached gob as a reference, but also *the one to which that is attached*. I.e. segments are all attached to the tail, tail is attached to the head and when segment is processed, it reads segment.attach == tail and tail.attach == head to get the data it needs.

Mais gérer deux pointeurs (un "avant" et un "arrière"), ça compliquerait méchamment les choses. Entre autres, ça rendrait difficile le choix de 'qui doit bouger le premier", et ça demanderait une série de bytecodes supplémentaires pour les opérations "attache, détache" des expressions de la machine d'état (que j'aurais cru plus nombreuses. [ ] à vérifier).

Let's see whether that can be enough. I also long for the opportunity to have swinging vines in some Bilou game. They could also be made of chained Gob through attach links. At least when they're hanging free. But as soon as Bilou is using them, I'd like the rules to change. Now Bilou's position dictates how one part of the vine behaves because of tension. If we can use collision to re-write attachments when that happens, that turns into a job for between(...) again.

Retournons vers le pont en faisant une halte à une autre mécanique que je tiens à faire apparaître dans Dreamlands: pouvoir s'accrocher à des trucs qui pendent. J'aimerais depuis longtemps que ce genre d'objet dans Bilou soit au moins aussi bien animé que dans le remake de Pharao's Curse par Lazycow. ce qui suppose

  • un mouvement ondulant lorsque la liane est livrée à elle-même (pas de balancé excessif à la DKC)
  • une certaine raideur quand Bilou s'y accroche (pas question de passer en mode 'corde ninja')
  • Si Bilou n'est pas au bout de la corde, l'extrémité libre continue d'agir librement alors que la moitié tendue est entièrement contrainte par la position de Bilou et celle du point d'attache.

Si j'essaie de dessiner des petites flèches rouges sur ce schéma pour représenter "qui a besoin de connaître la position de qui", on voit que la corde devra réorganiser ces liens quand elle passe d'un "état" à l'autre. Normalement, c'est jouable avec des collision. Et ça confirme mon intuition construite avec Big Caterpillar: le jeu, c'est d'avoir les deux points de référence (début et fin) qui sont directement liés l'un à l'autre (fin->attach == debut) et un contrôleur between() qui suit segment->attach pour trouver fin puis fin->attach pour avoir début et détermine la position-cible à partir des positions de fin et début.

Et pour le pont ? Va-t-il forcément falloir deux liens par bûche ? Je crois que non, mais ça supposera de faire apparaître à la volée deux objets "coincés" par collision sous la bûche qui a reçu un poids, chacun étant la fin d'une chaîne qui remonte jusqu'à un des points d'attache du pont. Et donc que la largeur maximale pour les zones de collision déterminera la largeur maximale du pont.

In Theory, it should be possible to do something similar with the bridge, at least to get a nice alignment of the logs when the force applied is so strong that logs' weights becomes neglectible in comparison. Yet, it's obvious there will be two chains here, both using the same "origin" location (the log on which the force is applied). I thought it could be fixed by creating temporarily two GOBs (red dashes) that are pushed by a collision box of the weighted log (red hatching), each being attached to either side of the bridge (red circles). "slave" logs that must just align would then attach to the dashed-GOB that's on their side (green arrows). But maybe we could even do it without the dashed-GOBs if sides attached to the weighted log and not the other way round.

Saturday, May 06, 2023

Funky 2.1 / A bridge to fun


Il y a bien longtemps (wow. 11 ans!), j'envisageais de faire un niveau vertical plein de Funky Funghis mais je voulais aussi quelque-chose qui soit plus original que la caverne aux champis de Commander Keen... Peut-être en utilisant un levier comme dans la BD.

Sauf que ... j'essaie que la forêt donne la sensation d'un environnement pas trop artificiel. Quelques statues, ok. Un puit, passe encore. Mais des planches-levier-d'acrobates ? J'aimerais autant pas. Bilou qui transporte des cailloux et des planches pour fabriquer lui-même le levier ? Je vais perdre les joueurs en route.

Back in '93, my brother had drawn a jumping mushroom, directly inspired from the Shadowlands of Commander Keen, and placed a few in the Green Zone. Back in October 2000, it became "Toadstool", the first ambiguous character, that is not 100% a baddy, but still not 100% friendly either. In February 2009, it became "Funky Funghi", and in 2012, as I tried to make it an area with plenty of them, again drawing inspiration from the Cave of Descendent of CK4, where we'd sometimes have one appearing from the top of the screen, sometimes have one catching our feet from below, and so on. There was also some places with something acting like a teeterboard, where Funky Funghi's siblings would (unvoluntarily?) help Bilou by propelling him upwards as they bop around.

J'ai tenté d'autres approches, inspirés par l'une ou l'autre vidéo let's play (mais j'ai oublié lesquelles ^^" de l'excellent Tiny Thor à venir), comme cette gelée/résine dans les branches d'arbres ... d'un point de vue purement mécanique, ça pourrait faire l'affaire. D'un point de vue intégration dans l'univers ... euh ... bof bof.

There was such teeterboard in the webcomic, and it is unclear where it came from. Bilou uses it for revenge. That could have been something Bilou made up on purpose.. who knows. But it would feel odd to see some in a green zone level, says my 23-years-older brain. I tried to come up with alternative recently, like this sort of jelly which could possibly work in another environment by which I can't convince myself truly works within a giant hollow tree.

Puis mes gamins sont passés à nouveau sur une plaine de jeu, et dans les plaines de jeux, il y a ces ponts suspendus sur lesquels les uns passent en courant pour faire tout trembler tandis que d'autres râlent, cramponnés aux rambardes, mais tentent de passer quand-même. Puis il y a ceux qui sautent pour tout secouer encore plus. Et dans ma green zone aussi, il y a des ponts (même si je n'ai encore presqu'aucun post qui en montre ^^"), bien qu'ils étaient assez peu marquant dans la version BASIC (en gros, certains morceaux pouvaient tomber. Voilà).

Mais pour "dreamland", ce serait assurément assez sympa que ce côté "un pont, ça tangue" soit exploité, et en particulier, que Funky Funghi puisse faire "voler" Bilou vers le haut si on a le bon timing.

So I'm glad my kids were on a playground last week-end, one with those bridges you run across to shake them more than the lad before did. There *are* bridges in the green zone already (I just haven't shown them to you yet). No sane level designer making a game a few years after Sonic the Hedgehog would craft a platformer without bridges. And I had thought of using them as trampolines, where you slowly get higher and higher by jumping again and again. Why not have Funky Funghi "use" them to propel you ?

Evidemment, laisson à Funky Funghi son côté "utile, oui, mais attention". Par exemple, si Bilou se contente de passer sur le pont, on peut très bien imaginer que chaque fois que Funghi atterrit, il secoue assez le pont pour que Bilou perde pied. S'il retombe d'un grand saut, il pourrait faire décoller Bilou -- plus ou moins l'équivalent d'un saut normal, mais pas forcément vertical.

Now, it's still Funghi. Even when it is useful, it remains dangerous. we could imagine that if Bilou doesn't "prepare" the bridge for being propelled, he would just be bounced away towards Funghi, but not much higher than a regular jump would have. We can also imagine that when Funghi makes just a small bop, it just makes the bridge shake and be somewhat stunned by that.

Bon, un truc pareil, par contre, il faudra l'introduire pour que le joueur puisse l'exploiter. Par exemple quand c'est Bilou qui joue le rôle de poids lourd et un autre personnage (au hasard, Appleman) se fasse propulser. Ça ne sert pas à grand chose, mais c'est rigolo. Ne pas oublier de mettre des trucs rigolos dans un jeu vidéo: on est là avant tout pour s'amuser.

It will take some training if we want the player to figure out things like that. And training requires some sandbox to experiment. So why not make sure that the player will have encountered easy-to-handle baddies (e.g. applemen) on bridges before and had the opportunity to realize that stomping on a bridge has side-effects. It shouldn't be too hard to make it fun and it's always good to have a few things that are just fun in a game.

Tuesday, May 02, 2023

A bit of shadow

Once upon a tweet, was a blog post about making dsgametools widget more readable and nice to use by adding a shadow to the default font. It was the youngest of the Todo family and its sibling would often make fun of it because it had the lowest possible priority for the project.

But then, on an idle day were the coder so tired he couldn't read a screen, though he had the Todo category open on his boox. "Well, why not" he thought, and he let our little Thumby Todo post jump over a blank page of his notebook.

It started sketching one letter, with its right and bottom empty lines of pixels, and split it in 3: Darkness, where light normally never comes; Highlights, where light might strike harder. And it turns out that if Highlights were to fall and the darkness picked up its power, then the letter would look all flat. And then the color that never changes, regardless of whether Highlights or Darkness is winning.

Bon, quelque part entre 2 scorpions, j'ai eu envie de creuser un peu cette histoire d'ombre pour les caractères dans les outils DS. Le mockup donne tellement mieux que mes captures d'écran ... en plus, ça se prête à merveille à un peu de programmation sur papier. Un petit schéma d'abord... puis quelques routines comme à l'époque du cours d'algo. Et puis j'ai fini par me rendre compte que chaque fois que je passais devant cette page-là dans mon livre-ordinateur cahier-agenda, ça réchauffait un peu mon coeur de codeur de penser qu'un jour, peut-être, j'aurais le temps d'essayer ça.

Very well, thought the coder, but I do not feel like hand-painting every 128 characters of the default font. Can we find a simple set of rules that, given a regular 1-bit-per-pixel font would deduce the 2-bit-per pixel font with highlights ? Yes, of course. And that's a perfect fit for bringing back paper coding.

Well, as usual, it then took a good deal of checks and debugging to find where that code should actually be invoked, what prevents it from working and so on. 

Et puis zut. On est dans un projet perso pour le plaisir, pas dans un projet-pro pour le client, donc au diables le Canban, les priorités, les milestones et tout ça. hg branch et voyons ce que ça donne... eh bien, ça donne bien. J'adore. Juste un hic: ça verrouille presque 40 couleurs de la palette utilisée par SEDS. Ou plutôt, dès qu'on charge un .spr qui utilise plus de 216 couleurs, l'effet est ruiné par un texte qui devient illisible. Parce que oui, j'utilise les palettes étendues de la NDS pour le moteur de jeu et pour presque toute l'interface graphique sauf pour le texte et ... pour le widget "palette" :-P

But there it is. Working. And even on emulator, the feel of it is just wonderful. And all it takes is some NES-glory palette swapping, where the text is moved from the 'interactive' palette with black shadow to the 'active' palette where 'shadow' is actually white and highlight has background color, as if the text had been pushed down by one pixel. Unfortunately, there's a price to pay: instead of 4 system colors, I need 40 of them (2 x 16-color palette + 4 on the 3rd palette). And so far, in SEDS, that means we can only use 216 colors instead of 250.

edit: but there is hope: SEDS is currently *not* using the multi-palettes mode (unlike LEDS) It likely wouldn't suffer from some extra system colors if it did. Converting AnimEDS sounds less appealing, because that has all its interactive windows on the 'main' screen rather than on sub-screen like older tools ^^"

Thursday, April 20, 2023

Retour chez les Minish

 

J'ai *enfin* remis la main sur ma cartouche de Minish Cap! C'était bien à la S-Team que je l'avais confiée, mais à Mlle A, et non pas à M. N (oui, ils ont grandi, maintenant). Du coup, pas besoin de prendre tout de suite un abonnement Nintendo+ avec ma switch et J.L.N a donc commencé un 4eme Zelda en parallèle. Il a pas mal avancé tout seul: tout le premier donjon. Là, il coince dans le village et insiste sur le fait qu'il *faut* qu'il puisse y devenir tout petit pour aller dans le Mont Gongle poursuivre sa quête.

J'ai donc relancé une partie moi aussi, parce qu'il y a une chose dont je me souviens bien dans ce jeu: les 'clés/portes' sont parfois loin d'être intuitives. Prenez ce tonneau, par exemple, qui renferme un item indispensable à notre progression. Lors de votre premier passage, il y a de fortes chances que des minish montent la garde devant chacune de ses entrées (et pas de chance, vous ne savez pas leur parler). Trouvez le minish qui connait la langue des humains, il vous dira d'aller dans le tonneau prendre ce qu'il faut pour vous faire comprendre. 

Oui, mais bon, c'est pas cohérent, là. "Comment j'explique au minish qui ne me comprend pas qu'il doit me laisser entrer dans la réserve pour prendre un truc pour qu'il me comprenne, hein, M. le Sage ?" que je voudrais lui dire, moi, au NPC. Logique non? Bin, ne te tracasse pas tant, mon grand: une fois ce dialogue passé avec le Sage, pouf, tout d'un coup, les minish qui montaient la garde sont partis casser la graine ou je ne sais quoi. Champ libre. Et des comme ça, il y en aura des tonnes (oh!).

Wednesday, April 19, 2023

Fury 2 ?


Je dois reconnaître que celle-là je ne m'y attendais pas, mais figurez-vous qu'une équipe s'est mise en tête de nous faire une reprise de Fury of the Furries! Eh oui. J'en veux pour preuve une vidéo "playthrough" du nouveau château. Bon, je suivais à distance le projet depuis un moment, et je ne peux évidemment pas m'empècher (après Giana et Bubsy) de juger "ce qui marche et ce qui ne marche pas" dans cette adaptation.

Tout bon: le pixel art. Sur les décors principalement, bien que les sprites ne soient pas en reste. Les graphistes semblent avoir bien capturé le côté "di-chromie" du premier opus et s'en servent judicieusement (mur du fond dans les tons mauves et boiseries qui ressortent bien) ainsi que les "textures" variées des briques et autres éléments de décors.

A améliorer: la bande son. N'allez pas me faire dire que l'artiste en charge ne fait pas de belles choses, mais sincèrement, comparé au travail d'El Mobo sur l'original (remixé?), on se sent loin du compte. Ça manque de punch et de personnalité. J'aurais presque envie de dire à CJ "allez, je leur laisse Pikaboo Castle" (mais il est déjà dans Wolfling ^^")

Know what? One of the authors of the original Fury of the Furries game has started an indie Furry 2 project! From times to times, we get videos of in-progress game development - and recently, that was a full playthrough video of one of the environments: the castle. Great pixel art, improvable soundtrack and a mood that feels like keeping the genome of the original game. Let's see whether we can say more about game design...

Interpellant: la longueur des niveaux. Fury, c'était un 'puzzle platformer'. Pas que la dimension "casse-tête" soit majoritaire comme dans un "Toki Tori", mais enchaînant des niveaux plutôt courts les uns derrière les autres. ça donne la possibilité de se concentrer sur l'exploration/l'observation d'un des niveaux pour trouver les mécanismes à activer, les passages secrets, etc. Ici, on a une map plutôt grande concentrant tout ce qu'il y a a dire (?) sur le chateau, et dans laquelle on nous invite à trouver un bon millier de fruits et légumes cachés. Pas loin de 40 minutes pour une exploration exhaustive sans respawn, c'est long, comme session de jeu.

Levels are much *much* longer than in the original puzzle-platformer game. That somehow make sense since the 'puzzle' part seems to have been dropped in favour of a more regular "explore-platformer with some local puzzles to get extra things". When you're designing a puzzle, you want to expose as much of the items to your player's eyes, so they brains can solve the puzzle instead of trying to remember what they've seen elsewhere. You also need to ensure failing the puzzle (which often implies dying or retrying from the start in the original game) won't ask your player to redo too much again, and short levels help on that. Still, here we've got about 40 minutes of video for a collect-all-items playthrough.

Mitigé: les pouvoirs. Fury, c'est 4 pouvoirs différents et (le plus souvent), la possibilité de passer de l'un à l'autre. Jaune = attaque, Rouge = mange-mur, bleu = nageur, vert = acrobate à la corde ninja. Le principe est conservé ici, bien que si je ne me trompe pas, il est maintenant possible de changer de pouvoir instantanément, sans attendre d'avoir mis son personnage à l'arrêt (ce dont je ne me plaindrai probablement pas). J'avoue que le côté " vert se balance avec sa langue" a un côté dégueu-kid-paddle dont je me serais passé, mais je suis prêt à le concéder comme liberté artistique des nouveaux (?) auteurs au même titre que je passe l'éponge sur l'exagération graphique de Rayman Origins/Legends. 

Bleu peut maintenant cracher de l'eau, ce qui donne lieu à quelques interactions sympas (je ne vais pas appeler ça des puzzles) comme ici avec un canon qui perd toute sa vigueur si on noie sa mèche. Il faut toujours Jaune et ses kameah pour se débarasser de la plupart des ennemis, donc ça reste équilibré et ça atténue un peu l'effet "Bleu ne sert à rien, y'a pas d'eau" du jeu original.

Rouge a maintenant la possibilité de voler en pétant. C'est kid qui va être ravi. La mécanique est rigolote, le contrôle en vol plutôt correctement dosé (à vérifier manette en main) entre trop fort et pénible-à-la-Donkey-sur-un-tonneau-fusée, l'habillage (comprenez les prouts) bien en phase avec le personnage (dévore tout et n'importe quoi) et la fonction (mon filleul parlait déjà des prouts de Yoshi pour justifier sa faculté à voler un petit moment dans Yoshi's Island). J'ai un peu peur que ça ne rende les acrobaties de Vert fort anecdotiques, par contre. Pourquoi se prendre la tête à faire le ninja quand on peut léviter ?

The general idea of 4 characters with different abilities and a way to swap between them (you only ever control one avatar) has been kept and the overall ability scheme has been preserved: Yellow shoots, Red eats, Green swing and Blue swims. Well, Blue can also exhale water (solves some puzzles, and used as a weak weapon). And green can slurp bonuses at a distance (possibly swallow small baddies ?). The most modified one is Red. He can now fart-fly for significant distances. To what extent will that break platforming or rope-swinging, I'm unsure of. I understand this won't disappear because it was the core gameplay of a mobile game I've never played. It doesn't look very balanced on the video, but maybe that's because player has already collected some fart-more-power-ups or something similar (I see hints that Yellow has now limited shots despite the counter never goes down, either). Anyway, since there are already portals that forbid the use of one character for one area, there's a clear way to enforce player would use rope-swinging or jumping in some specific room if level designer decides so.

Enfin, et c'est sans doute lié au passage du puzzle à une approche plus "collect-platformer", les retraits de pouvoirs semblent plutôt rares. Le jeu d'origine nous mettait souvent dans un niveau avec seulement 2 pouvoirs, et un 3eme à réactiver dans une zone précise. Ici, c'est plutôt "les 4 font la fête" en permanence avec quelques passages "Regarde Maman! Sans les mains!" pour l'une ou l'autre salle bonus.

Très bien: le côté décalé. On a gardé les loups-garous qui se changent en lapinou, les parodies de Dracula, les clins d'oeil aux flippeurs et compagnie. Trop tôt pour dire si les cameos seront aussi nombreux que dans l'original (en particulier dans le niveau final. On dirait bien), mais on a ni renié ses origines façon préhistorik-supernes, ni sombré dans un excès qui perd toute saveur façon film de Jim Carrey.

A tester: la barre de vie. Fury était un die-and-retry. Tout comme Commander Keen et Rick Dangerous, tout contact y était fatal. C'était précieux pour certains puzzles mais ça pouvait devenir franchement gonflant quand le moindre pet de mouche dans la tronche vous éclate. Là, avec 6 points de vie en début de niveau et des ennemis qui explosent au moindre tir (le jeu d'origine vous demandait régulièrement de bourrinner le bouton de tir 5 à 10 secondes pour la moindre araignée) et distribuent généreusement des petits coeurs, j'aurais du mal à fixer un pronostic.

One last item that might be worth tuning is the amount of hitpoints for both baddies and furries. The original game had a die-and-retry aspect for the player while any baddies would require numerous shots to be defeated. Here this is the opposite: plenty of hitpoints for the player and most ennemies explode in one shot, often releasing a heart for the player to recover. I know I've been wondering whether some god mode was enabled while watching the video. But it wouldn't be too hard to make that customizable by the player, adjusting heart-drop-probabilities or baddies resistance to Yellow's shots through menu or collectibles. Don't let that prevent you from wishlisting the game!

Bref, vous l'aurez compris: Fury 2 n'est pas un remake de Fury 1, mais il reste fidèle à l'esprit et à l'ambiance de l'original. J'aurais envie de comparer l'évolution de gameplay à ce que l'on a pu voir entre Ori 1 et Ori 2. Je souhaite une bonne finalisation au projet et un grand merci au Cyrille qui bosse là-dessus de nous proposer un retour en Furries. Wishlistez bien ^_^

Tuesday, April 18, 2023

std::unique_ptr<>

Okay, it shouldn't be that hard to have a variable that gives access to an object RAII-style while allowing late initialization: I should use std::unique_ptr

RAII is neat. Despite having an impossible-to-use-and-remember-name (don't be surprised if I use 'Rabi' instead ;), it means that you spend less time catching/rethrowing exceptions, less time checking return codes (because you used exceptions) and can think of your software as something as reliable as a building instead of some Jenga game. Want to have some code dealing with some file, but don't want to forget closing the file handle if anything goes wrong ? Simple.

  • have a RabiFile class that encapsulate your file handler
  • have the ctor of RabiFile do *everything* that's needed to be working on the file
  • have the destructor always going to a clean state.

As soon as you've written something like RabiFile music("forest.xm", RabiFile::RO), you're good to go

  • either your RabiFile exists and all its methods are now valid
  • or you failed to create it and an exception has been thrown, taking you out

So with that approach, you'll never write any music->open("underwater.xm") nor any music->close() anywhere. That makes writing of constructors a *bit* more complicated, I admit, especially those for objects that contains some RabiObjects: they have to catch exceptions and rollback any 'personal' resource acquisition they performed because nothing invokes destructor on objects that haven't been fully constructed. If your constructor fails with an exception, it's up to your constructor to ensure it leaves no mines behind.

But that usually don't happens a lot. It does happen when I stretch the RAII fashion to long-lived objects like GameLevel that has LevelMap and SpriteSet(s), or to GobState that has GobTransitions, though. But Rabi* is mostly for temporary things that need us to hold something while something else.

There's one common drawback to both, though. Because my objects have to be allocated on the stack to benefit automatic cleanup on failure, it is tricky to benefit from polymorphism. Say I should either create an instance that reads the script from a file or an instance that reads it from a buffer received over WiFi by runME ... I can't just replace BufferReader reader(...) at the head of my function by


loaderCode() {
   if (fromFile) {
       FileReader reader(whichFile);
   } else {
       BufferReader reader(whichBuffer);
   }
   DoStuffWith(reader); // no such reader, dude.
}

because there, the condition-dependant objects are no longer valid when I then want to use them. And migrating the DoStuffWith into the condition will soon turn unpleasant as well because it is not DRY. So instead, I can have


loaderCode() {
   std::unique_ptr<InputReader> reader;
   if (fromFile) { 
      reader = std::unique_ptr<FileReader>(new FileReader(...));
   } else {
      reader = std::unique_ptr<BufferReader>(new BufferReader(...));
   }
   DoStuffWith(*reader);
}

Granted, that will not allocate the object on the stack but at least it guarantees that the created object gets deleted whatever happens during DoStuffWit(*reader). Given the size of the NDS stack, it might not be a bad move.
Maybe I could have done it with auto_ptr instead. It seems it was the way to do it before C++11 came, declared auto_ptr obsolete and unique_ptr as being the way to do this instead.

A few things to remember when working with unique_ptr

  • you must use std::move(reader) if you intend to return the unique pointer as function value
  • you must use std::move(aswell) if you want to capture some unique pointer you've received as a member of a new uniquely pointed thing

  •  

Wednesday, April 12, 2023

StyleHax on DSi

For (too?) long, I've staid aside of DSi homebrew. Partly because there's little the camera could offer to my projects and I don't really need more than 4MiB of RAM, but mostly because until recently, running homebrew on that device felt like an arm race with Nintendo. You need to break into the machine through exploits the same way you'd take control of someone else's server on Internet and that has a taste of dark side I'm not fan of. Plus, once you've managed to get into things, you then have to install more permanent modification if you want to be sure to be able to do the same the next day, since Nintendo could ban the vulnerable software you've used to gain access (if you were lucky enough to have it on your device on first place).

But one post on hackaday might well change all this. Nathan Farlow has found a way to use the web browser shipped with the DSi firmware to execute arbitrary code.

 He just had to get his code into the right spot. For this he employed what’s known as a NOP sled; basically a long list of commands that do nothing, which if jumped into, will slide into his exploit code. In modern browsers a good way to allocate a chunk of memory and fill it would be a Float32Array, but since this is a 2008 browser, a smattering of RGBA canvases will do.

Ultimately, that will execute a boot.nds file from the SD card, such as a homebrew launcher.

Octobre dernier, j'avais jeté un oeil un peu plus attentif à "Comment on peut faire tourner des homebrews sur DSi". S'en était suivi un graphe compliqué dans mon calepin indiquant quel menu dépend de quel installateur qui dépend de quel firmware custom. Mais au final, on finissait toujours par dépendre d'un "hax" pour prendre le contrôle de la console. Un vrai piratage en règle, quoi, et qui dépend en général de la disponibilité d'un logiciel vulnérable sur sa console.

Pendant toute la durée de vie de la bête, Nintendo a mené une chasse farouche à ce genre de vulnérabilité, supprimant jeux et applications du service DSiWare au fur et à mesure qu'elles étaient identifiées, étendant sa liste de cartouches "nds" interdites (dont les clés avaient été cassées et utilisées pour des linkers) lors de mises à jour système indispensables pour faire tourner les nouveaux jeux ou refaire un téléchargement sur le DSiWare. J'étais resté à l'écart de tout ça. Mais j'ai commencé à réviser ma position: un nouvel "exploit" (comprenez "mécanisme permettant de faire tourner son code à soi en exploitant une vulnérabilité) à été trouvée, et pour le navigateur intégré, tout simplement!

It should be trivial to try: install your favourite DSi-capable software as boot.nds on an SD card, direct your DSi browser to nathan's web page and voilà. Except nothing guarantees the contents of Nathan's page will stay the same over time, so I'd rather download that locally and study the stylehax source code before I start using that on my own device. 

At the start of the chain is an html document with some NDS binary embedded as "shellcode" in the html <script> part, plus some javascript to exploit the use-after-free bug of the browser. The payload.c featured in the stylehax repository contains actually just a little loop to inject the 'true payload' at some dedicated location (held as a blob into payload.h). And that true payload comes from another repository (well, actually, that second repository contains many exploits, but the one we'll use is the 'minitw' one, according to payload.h)

As soon as the 'payload.c' part is running, we already have access to most resources of the DSi. The goal of minitwlpayload is to setup the proper context for the default bootloader of the devkitpro project embedded into the .nds (yes, that comes from a third repository :P) The bootloader will mostly run on the ARM7 core, which is the only one to have access to SD card registers on the DSi. There will be many passme-loop-synchronization-points until we're ready to execute the NDS or DSi code contained in boot.nds, one for each ARM9 function we want to execute between two ARM7 functions.

Pour celui qui veut tenter sa chance, rien de plus simple! L'auteur de l'exploit a mis en ligne une page HTML (une seule) intégrant le code nécessaire pour aller exécuter le fichier boot.nds qu'on trouverait sur la carte SD de la DSi et le javascript qu'il faut pour faire bugger le navigateur au point qu'il se mette à exécuter un morceau de page web canvas HTML5 comme si c'était du code machine. Et surprise, c'est du code machine, cette fois-ci! Enfoncé, HTML Quest ;-)

One question remains, though: how does ARM9 exploit trigger execution of custom code on the ARM7 ? It looks like the answer lies within the following snippet of exploit.c:

    memcpy16(VRAM_C, decrbegin, decrsize); // writing custom code into VRAM at 0x6000000
	VRAM_C_CR = VRAM_ENABLE | VRAM_C_ARM7_0x06000000;
    // now inserting a jump at the start of ARM7-dedicated memory area.
	*(vu32*)0x2380000 = 0xE51FF004; // ldr pc, =0x06000000
	*(vu32*)0x2380004 = 0x06000000; // (constant for above)
	debug_color(0,15,0); // dark green
	IPC_SendSync(1); // MAGIC!

For some reason, when we trigger an IPC IRQ on the ARM7 with code 1 (out of 16 possible codes), it executes code from the start of the ARM7 area again, where we have installed our trampoline to VRAM. Why does it behave so ? I guess the reason lies in some line of code for the ARM7 code in the TWL sdk, sealed in a vault at Kyoto ...  I presume I'll have to accept it without further understanding.

Seulement moi, j'ai envie de comprendre un minimum ce qui se passe. Histoire entre-autre d'éviter que la page ne télécharge un jetaibieneu.nds au lieu de démarrer hbmenu.nds rebaptisé boot.nds, par exemple ?Et aussi pour enfin comprendre cette histoire de .twl et donner une meilleure chance à l'exécution de mes homebrews sur DSi. Donc je parcours les pages github, ma liseuse dans une main, mon calepin dans l'autre...

exomizer/exodecrunch

One notable action of exploit.c from minitwlpayload is to "decrunch" the embedded bootloader into VRAM where it will execute, that is, to undo the compression performed by the exomizer tool invoked by dsi/exploits/minitwlpayload/Makefile.

  • it packs the 'bootloader' from devkitpro github from 6KiB into a bit more than 4KiB. Unsure whether it was critical to use in this context :-P
  • ndsload.bin is just an 'objcopy' binary-extraction of the ndsload.elf
  • https://bitbucket.org/magli143/exomizer/src/master/src/Makefile could be the source for the exomizer tool, but I fail to get why it would need a 6502 emulator ... (I guess this is linked to the 'self-extracting for C64, Apple II and BBC micro' feature)

Possibly, such steps could make sense for larger payloads or for other exploits where the amount of data we can embed is limited (remember, there has even been exploits involving QR code readers)