Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » Terrario, a Terraria rewrite for the calculator
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Terrario, a Terraria rewrite for the calculator

Posté le 10/07/2020 16:05

Hi. I noticed a while ago there weren't any games like Terraria or Minecraft available for Casio calculators. For the past while I've been working on rewriting Terraria in C for the SH4 calculators using gint. I'm not sure when if ever I'll finish it, since it is a fairly big project, so I've decided to put it here for now.

Here are a few screenshots of the progress so far (some may be out-of-date):
Main menu


Gameplay


Inventory


Crafting


Equipment


A visualisation of a generated world (click for full detail)



The game runs at 30FPS. Worlds are 1000x250 tiles large (640x250 on the 35+E II / GIII).

The control scheme and a crafting guide can be found in the game's About menu.

This forum page is updated regularly with the latest release of the game, as well as a changelog in the comments.

If you aren't sure what an item does, feel free to search it up on the official Terraria wiki.

Most recent update:
Water and lakes/beaches.

Up next:
NPCs.

The attached file contains the latest build of the game, as well as instructions and a screenshot compiling script and map tool.

The source code repository as well as early builds of the game can be found at this GitHub repo and its Gitea mirror. Obviously, expect bugs in these early builds, though I take care to remove the major ones I find before releasing.

Due to the very large world, the save files for this game are big. Make sure you have at least 450kB of storage space before installing the addin (300kB on Graph 35+E II), and try to keep at least 300kB free afterwards. Tampering with the files in the TERRARIO folder will corrupt the save, so don't do that. The game will warn you if you have low storage space available, so that you can optimise your storage.

NOTE: You must have a Graph 35+ E, Graph 35+E II, fx9860GII, or fx9750GIII model calculator to run this game.

Fichier joint


Précédente 1, 2, 3, 4, 5, 6, 7 Suivante
Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 12/07/2020 08:54 | #


These .src files from SimLo's documentation are actually written for Renesas' original SuperH assembler, which is part of the tool chain shipped with the fx-9860G SDK. We usually consider this toolchain to be of lower quality than GCC, so I haven't used it in years. You can absolutely use assembler with the fxSDK, it calls sh-elf-as for the job, but the syntax is slightly different.

Here are a couple instructions that should get you through:
.SECTION P,CODE,ALIGN=4 becomes .text followed by .align 4
.export becomes .global
• Instead of the SYSCALL macro, you can use the C preprocessor as in gint's core/syscalls.S (save your file as *.S instead of *.s for GCC to run the preprocessor)
• Don't use ? in symbol names. "." is often used for local symbols (eg ?try becomes .try). Also a colon is missing after ?exit
• Instead of mov.l #0x80010070, r2 do mov.l .syscall_addr and later on, after the code, .syscall_addr: .long 0x80010070
.POOL and .END can be removed, .DATA.L becomes .long
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 12/07/2020 09:07 | #


Cheers, I might try implementing it after multi-file saves.

Ajouté le 13/07/2020 à 12:15 :
Does BFile_FindFirst work with folders? I'm giving it a path to a folder (\\fls0\TERRARIO) and it appears to crash the addin if the folder exists.
Yatis Hors ligne Membre Points: 547 Défis: 0 Message

Citer : Posté le 13/07/2020 12:43 | #


The BFile_FindFirst() work fine but it's a bit weird to use.
Without any code we can not help you

But there is a piece of code that I use in Vhex (my kernel) to "mount" the SMEM.
Basically, I just dump the File Hierarchy of the SMEM to avoid interaction with Casio's OS (for many raisons) and I use BFile_Find*() for this

(NOTE: look the void dump_smem_level() function)


#include <kernel/fs/smemfs.h>
#include <kernel/util/atomic.h>
#include <kernel/util/casio.h>
#include <kernel/devices/earlyterm.h>
#include <kernel/driver.h>
#include <string.h>

/*******************************************/
/**                                       **/
/** USB Power Graphic III SMEM driver part **/
/**                                       **/
/** (extract from <kernel/fs/smemfs.h>)   **/
/*******************************************/
// Internal superblock use by the USB3 abstractions
struct smemfs_USB3_superblock
{
    struct smemfs_USB3_inode *root_inode;
    struct smemfs_USB3_inode *fake_root_inode;
};

// Internal struct used to store SMEM dump
struct smemfs_USB3_inode
{
    // File name
    char name[32];

    // Internal file's informations
    int type;
    size_t fsize;
    size_t dsize;

    // Internal abstraction informations
    struct smemfs_USB3_inode *child;
    struct smemfs_USB3_inode *sibling;
    struct smemfs_USB3_inode *parent;
};


// convert wide character string  (16bits) into ASCII string
static size_t wide_char_convert(char *pathname, uint16_t *pathname_wc)
{
    size_t i;

    i = -1;
    while (pathname_wc[++i] != 0x0000 && pathname_wc[i] != 0xffff)
        pathname[i] = pathname_wc[i] & 0x00ff;
    pathname[i] = '\0';
    return (i);
}

// Dump one level of the SMEM Filesystem Hierarchy
// @note: send buffer to avoid recursif definition
static void dump_smem_level(struct smemfs_USB3_inode *parent,
        struct smemfs_USB3_inode **sibling, uint16_t *buffer)
{
    struct casio_file_info file_info;
    struct smemfs_USB3_inode *inode;
    int handle;
    int i;

    // Generate search path
    i = 7;
    memcpy(buffer, u"\\\\fls0\\", 14);
    if (parent != NULL) {
        for (int j = 0 ; parent->name[j] != '\0' ; ) {
            buffer[i] = (uint16_t)(parent->name[j]);
            i = i + 1;
            j = j + 1;
        }
        buffer[i++] = '\\';
    }
    buffer[i + 0] = '*';
    buffer[i + 1] = 0x0000;

    // Find the first file
    // @note: the search buffer and the buffer which will content
    // the file name is the same. But it's not used at the same time, so
    // we can use this tricky way to save some stack
    if (casio_Bfile_FindFirst(buffer, &handle, buffer, &file_info) != 0)
        return;

    // Get all inode stored in this level
    i = 0;
    do {
        // Try to alloc new inode
        // TODO: return error code !
        *sibling = smemfs_USB3_alloc_inode();
        if (*sibling == NULL)
            break;

        // Get first inode
        if (i == 0)
            inode = *sibling;
        i = 1;

        // Convert wide char into char
        wide_char_convert((*sibling)->name, buffer);

        // Dump file informations
        (*sibling)->type = file_info.type;
        (*sibling)->fsize = file_info.size.file;
        (*sibling)->dsize = file_info.size.data;

        // Link node and get next sibling
        (*sibling)->parent = parent;
        sibling = &(*sibling)->sibling;

    } while (casio_Bfile_FindNext(handle, buffer, &file_info) == 0);

    // Close casio BfileFind* handle
    casio_Bfile_FindClose(handle);

    // Now let's check all file to find directories
    while (inode != NULL)
    {
        // Check directory type
        if (inode->type == DT_DIRECTORY)
            dump_smem_level(inode, &inode->child, buffer);

        // Get next inode
        inode = inode->sibling;
    }
}

// DEBUG function
// TODO: remove ?
static void proto_ls(struct smemfs_USB3_inode *inode, int level)
{
    if (inode == NULL)
        return;
    for (int i = 0 ; i < level ; ++i)
        earlyterm_write("  ");
    earlyterm_write("%s\n", inode->name);
    if (inode->child != NULL)
        proto_ls(inode->child, level + 1);
    proto_ls(inode->sibling, level);
}

//
// smemfs_USB3_mount() - Mount the file system (sync)
// @note:
// We don't known how the file system work, so we should use
// Casio's "Bfile_*" sycalls to dump all internal informations
// to avoid OS switch (Vhex -> Casio -> Vhex)
//
void *smemfs_USB3_mount(void)
{
    extern struct smemfs_USB3_superblock smemfs_USB3_superblock;
    uint16_t buffer[64];
    void *root_inode;

    // Get current root inode
    atomic_start();
    root_inode = smemfs_USB3_superblock.root_inode;
    atomic_stop();

    // Check useless mount
    if (root_inode != NULL)
        return (root_inode);

    // We should use internal Casio's `Bfile_*` syscall
    // to dump SMEM content
    drivers_uninstall(0);

    // Generate fake root inode
    smemfs_USB3_superblock.fake_root_inode = (void*)0xdeadbeff;

    // Dump SMEM files organisation
    smemfs_USB3_superblock.root_inode = NULL;
    dump_smem_level(smemfs_USB3_superblock.root_inode,
            &smemfs_USB3_superblock.root_inode, buffer);

    // Get the "fake" root inode
    root_inode = smemfs_USB3_superblock.fake_root_inode;

    //DEBUG
    //proto_ls(smemfs_USB3_superblock.root_inode, 0);
    //earlyterm_write("g@m3rz\n");
    //while (1);

    // Restore all drivers
    drivers_install(0);

    // Return the sector table to simulate the root inode.
    return (root_inode);
}

Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 13/07/2020 12:47 | #


It is supposed to. To briefly quote the documentation, "The Bfile_FindFirst examines subdirectory names as well as filenames." and there is a DT_DIRECTORY file type in the return structure.

This might be a gint bug in the sense that performing that search can change hardware state in a way that gint does not expect, causing problems when returning from the switch. If you can narrow it down to some kind of MWE, please file an issue, I will gladly look at it.
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 13/07/2020 12:47 | #


The current code for my save function (supposed to just create TERRARIO if it doesn't exist):
void saveGame()
{
    uint16_t filePath[30] = { 0 };
    int handle;
    uint16_t foundPath[30];
    struct BFile_FileInfo fileInfo;
    int error;

    makeFilePath(filePath, "\\\\fls0\\TERRARIO");
    error = BFile_FindFirst((const uint16_t*)filePath, &handle, foundPath, &fileInfo);
    BFile_FindClose(handle);
    if(error == -1) BFile_Create((const uint16_t*)filePath, BFile_Folder, NULL);
}

Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 13/07/2020 12:49 | #


As a quick aside, you can get UTF-16 string literals with the u prefix:

uint16_t const *filePath = u"\\\\fls0\\TERRARIO";
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 13/07/2020 12:51 | #


oh cool, didn't know that, thought that was only in C++

Ajouté le 13/07/2020 à 14:56 :
Turns out it might be an emulator problem? After the umpteenth failure I decided to put it on my calculator and it looks like it works, the folder gets created. The emulator's been nothing but trouble to be honest, it really doesn't play well with gint but it's convenient to quickly test stuff.
Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 13/07/2020 15:00 | #


And no crash if the folder exists? Interesting. I admit I haven't thoroughly tested applications on the fx-9860G Manager so there might be quirks that I'm not aware of.
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 13/07/2020 15:07 | #


I'm doing an error check to make sure I don't try creating the folder if it exists, but the emulator just doesn't like making folders - I get either a reboot and no folder or a hang and no folder after a manual reset. It doesn't have any problems with files, though.

Ajouté le 13/07/2020 à 16:06 :
I've managed to get world saves working, using 64x64 tile regions and saving in \\fls0\TERRARIO\regX.dat. Each .dat file is 8192 bytes, and I'm just padding them out if they overlap the edge of the world. I also tried it on the emulator and it worked after a few tries
Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 13/07/2020 16:46 | #


Good job! The crash definitely looks like a problem with gint's switch mechanism. Similar things happened before when writing to files, until I discovered that BFile was leaving the DMA running so I had to wait for it before leaving the switch. In another occurrence, writing to files causes TLB pages to be evicted, so I have to account for that as well. This is probably a similar case, I will look at it hopefully in the next few days.
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 11:58 | #


FINALLY finished with the update, pushed everything and made a new release. I haven't implemented the in-addin optimisation (right now you just get an error if space runs out) but I'll definitely think about in in the next update. I fixed all the bugs I saw but there might be a couple left with the save/load mechanic. I also discovered that grayscale means you can have rather good-looking antialiased text
Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 14/07/2020 12:04 | #


Nice! I would suggest leaving a direct link to the g1a file in your main post if you want people to try it out. You can make screenshots of the screen by writing the VRAM to a file (in mono use gint_vram, in gray use gvram(), or if you have the very latest commits, gray_getvram()).

I also discovered that grayscale means you can have rather good-looking antialiased text

Wait, seriously? I never really believed this would work and I eventually forgot about trying it. xD Do you have any photo of this?
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 12:10 | #


Right, added the g1a, I'll have to remember to update that too. I updated to the latest version today, didn't realise you could use the vram for screenshots! I'll see if I can make a thing to get one.
Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 14/07/2020 12:23 | #


Oh I forgot to mention that in gray mode the VRAM switches when you call dupdate(), so you have to save the VRAM between the time you finish drawing and the time you perform the dupdate(). In practice this means you need to save the frame before you show it. If you want to interactively save the frame that is currently visible on-screen while in gray mode, you can render the next frame and save the VRAM after calling dupdate() (because by this time the next frame has been sent to screen and the desired frame is back into the VRAM).
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 12:54 | #


I'm copying 1024 bytes from each VRAM pointer into a char buffer using memcpy, then into a file, but there isn't anything in either file when I look at their contents Am I copying the data the wrong way? I looked at Monochromelib's documentation for the 1024 bytes figure.

void takeScreenshot()
{
    uint32_t* light;
    uint32_t* dark;

    uint16_t* pathLight = u"\\\\fls0\\light.vram";
    uint16_t* pathDark = u"\\\\fls0\\dark.vram";
    int descriptor;
    int size = 1024;
    char bufLight[size];
    char bufDark[size];

    dgray_getvram(&light, &dark);

    memcpy(bufLight, light, size);
    memcpy(bufDark, dark, size);

    BFile_Remove(pathLight);
    BFile_Create(pathLight, BFile_File, &size);

    BFile_Remove(pathDark);
    BFile_Create(pathDark, BFile_File, &size);

    descriptor = BFile_Open(pathLight, BFile_WriteOnly);
    BFile_Write(descriptor, bufLight, size);
    BFile_Close(descriptor);

    descriptor = BFile_Open(pathDark, BFile_WriteOnly);
    BFile_Write(descriptor, bufDark, size);
    BFile_Close(descriptor);
}

Lephenixnoir Hors ligne Administrateur Points: 19546 Défis: 142 Message

Citer : Posté le 14/07/2020 13:00 | #


This seems correct. I thought files extensions were limited to three characters, though if they're created it's probably not a problem. The VRAMs are already in the heap or static RAM area, you don't need to copy them to a buffer.

Please check that you're taking the screenshot with the correct timing relative to the dupdate() calls (see my message above). Also, do any of the BFile functions return an error code?
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 13:09 | #


Ah, I was calling it right after dupdate. Moved it to before and it looks like it worked, I'll make a python script to combine the two.
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 13:38 | # | Fichier joint


Made it, here's a screenshot of the main menu I'll make the script enlarge the images a bit.


Dark storm Hors ligne Membre d'honneur Points: 11355 Défis: 176 Message

Citer : Posté le 14/07/2020 13:48 | #


You can post the image as it, and then ask to display it bigger: [img=<width>|pixelated]https://url-to-img[/img]

It's better if <width> is a multiple of the original width. Ex: [img=256|pixelated]https://url-to-img[/img]
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Kbd2 Hors ligne Membre Points: 239 Défis: 0 Message

Citer : Posté le 14/07/2020 13:56 | #


That's what I was doing with the photos, I just think it's always better to have them a good size already in case they get filtered when they're upscaled and look bad.

I've made it so that the [SHIFT] button takes a capture of the VRAMs, I'll put the script in with the source code for if people want to take screenshots of their worlds.

Ajouté le 18/07/2020 à 03:19 :
Small update - the player is now animated. I planned on releasing this with the next big one but after implementing it I thought it looked good enough for its own release.

Ajouté le 20/07/2020 à 10:41 :
I've managed to squeeze an even larger world into 250kB - 1000x250 tiles, just above 1/20th the size of a small Terraria world!

I managed this by removing the status byte from the tile struct and instead calculating it when the tile is rendered. I feel that a minimal performance decrease is worth having double the world size. I still have 2 bits free in the struct, possibly for stuff like hammered tiles.

Another benefit is that world generation is now much faster due to not precalculating the tile statuses anymore.
Dark storm Hors ligne Membre d'honneur Points: 11355 Défis: 176 Message

Citer : Posté le 20/07/2020 10:58 | #


Did you consider some compression or lazy loading from storage memory? It may improve a bit the world size, even if it can add some latencies when moving to another world part.
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Précédente 1, 2, 3, 4, 5, 6, 7 Suivante

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2021 | Il y a 39 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd