commit 9dfc56eb4a8bcc927ee48269eb691be07359529c Author: Azure Date: Sun Mar 15 18:36:05 2026 +0100 First commit diff --git a/.css b/.css new file mode 100644 index 0000000..c33c38e --- /dev/null +++ b/.css @@ -0,0 +1,3 @@ +body { + font-style: ; +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/bin/fennec.js b/bin/fennec.js new file mode 100644 index 0000000..c37bcc7 --- /dev/null +++ b/bin/fennec.js @@ -0,0 +1,321 @@ +const BackgroundTypes = { + IMAGE: 0, + SOLID: 1, + GRADIENT: 2 +} + +const BorderTypes = { + NONE: 0, + SOLID: 1 +} + +const Colors = { + WHITE: new N3DSColorRGBA(255, 255, 255, 1), + BLACK: new N3DSColorRGBA(0, 0, 0, 1) +} + +const FontStyles = { + NORMAL: 0, + ITALIC: 1, + OBLIQUE: 2 +} + +const Screens = { + TOP: 0, + BOTTOM: 1 +} + +const Languages = { + DEFAULT: "FR" +} + +class Fennec { + constructor() { + this.screens = { + Top: new N3DSScreen(Screens.TOP), + Bottom: new N3DSScreen(Screens.Bottom) + } + } + + /** + * @param {Function} callback + */ + onGameLoop(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onControlClicked(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onTouched(callback) { + callback() + } + + invokeKeyboard() { + return null + } + + getSystemLanguage() { + return Languages.DEFAULT + } +} + +class N3DSAsset { + constructor(location) { + this.location = location + } +} + +class Background { + /** + * @param {BackgroundTypes.IMAGE | BackgroundTypes.SOLID | BackgroundTypes} type + * @param {N3DSColorHEX | N3DSColorRGB | N3DSColorRGBA | N3DSAsset} source + */ + constructor(type, source) { + this.type = type + this.source = source + } + + draw(x, y, z, width, height) { + + } +} + +class N3DSColorRGBA { + /** + * @param {Number} red + * @param {Number} green + * @param {Number} blue + * @param {Number} alpha + */ + constructor(red, green, blue, alpha) { + this.red = red + this.green = green + this.blue = blue + this.alpha = alpha + } +} + +class N3DSColorRGB { + /** + * + * @param {Number} red + * @param {Number} green + * @param {Number} blue + */ + constructor(red, green, blue) { + this.red = red + this.green = green + this.blue = blue + this.alpha = 1 + } +} + +class N3DSColorHEX { + /** + * @param {string} hexCode + */ + constructor(hexCode) { + this.hexCode = hexCode + } + + /** + * @returns {N3DSColorRGB} + */ + toRGB() { + const convertedColor = ColorsHelper.fromHexToRGB(this.hexCode) + return convertedColor + } + + toRGBA() { + const convertedColor = ColorsHelper.fromHexToRGBA(this.hexCode) + return convertedColor + } +} + + +class Border { + /** + * @param {Number} top + * @param {Number} left + * @param {Number} right + * @param {Number} bottom + * @param {BorderTypes.DOTTED | BorderTypes.DASHED | BorderTypes.SOLID | BorderTypes.DOUBLE | BorderTypes.RIDGE | BorderTypes.INSET | BorderTypes.OUTSET | BorderTypes.NONE | BorderTypes.MIXED} type + * @param {Background} background + */ + constructor(top, left, right, bottom, type, background, radius) { + this.top = top + this.left = left + this.right = right + this.bottom = bottom + this.type = type || BorderTypes.SOLID + this.background = background || new Background(BackgroundTypes.SOLID, Colors.BLACK) + } +} + +class Font { + /** + * @param {N3DSAsset} source + * @param {string} name + */ + constructor(source, name) { + this.source = source + this.name = name + } +} + +class FontSetting { + /** + * @param {Font} font + * @param {Number} weight + * @param {FontStyles.NORMAL | FontStyles.ITALIC | FontStyles.OBLIQUE} style + */ + constructor(font, weight, style) { + this.font = font + this.weight = weight + this.style = style + } +} + +class N3DSText { + /** + * @param {string} value + * @param {FontSetting} fontSpec + */ + constructor(value, fontSpec) { + this.value = value + this.fontSpec = fontSpec + } +} + +class Placeholder { + /** + * @param {Text} title + * @param {N3DSColorHEX | N3DSColorRGB | N3DSColorRGBA} color + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {Background | null} background + * @param {Border} border + */ + constructor(title, color, background, border) { + this.title = title || new N3DSText() + this.color = color || Colors.BLACK + this.background = background || new Background(BackgroundTypes.SOLID, Colors.WHITE) + this.border = border || new Border(10, ) + } + + draw(x, y, z, width, height) { + + } +} + +class InputField { + /** + * @param {Placeholder} placeholder + */ + constructor(placeholder) { + this.placeholder = placeholder || new Placeholder("Enter your text here", Colors.BLACK, 0, null, 0, new Background(BackgroundTypes.SOLID, Colors.WHITE)) + } + + /** + * @param {Function} callback + */ + onChanged(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onKeypressed(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onClicked(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onCoordinatesChanges(callback) { + callback() + } + + /** + * @param {Function} callback + */ + onReset(callback) { + callback() + } + + + draw(x, y, z, width, height) { + this.placeholder.draw(x, y, z, width, height) + } +} + +class Graphics2D { + /** + * @param {N3DSText} text + */ + drawText(text) { + return text + } + + /** + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {Number} width + * @param {Number} height + * @param {Background} background + * @param {Border} border + */ + drawRect(x, y, z, width, height, background, border) { + return { x, y, z, width, height, background, border } + } +} + +class N3DSScreen { + /** + * @param {Screens.TOP | Screens.BOTTOM} position + */ + constructor(position) { + this.position = position + this.graphics2d = new Graphics2D() + } +} + +module.exports = { + Background, + BackgroundTypes, + Border, + BorderTypes, + Colors, + Fennec, + Font, + FontSetting, + FontStyles, + Graphics2D, + InputField, + Languages, + N3DSAsset, + N3DSColorHEX, + N3DSColorRGB, + N3DSColorRGBA, + N3DSScreen, + N3DSText, + Placeholder +} \ No newline at end of file diff --git a/build/metadata.smdh b/build/metadata.smdh new file mode 100644 index 0000000..4c741b4 Binary files /dev/null and b/build/metadata.smdh differ diff --git a/lib/transcompiler.js b/lib/transcompiler.js new file mode 100644 index 0000000..2aec5b2 --- /dev/null +++ b/lib/transcompiler.js @@ -0,0 +1,83 @@ +const fs = require("node:fs") +const path = require("node:path") +const acorn = require("acorn") + +const source_file = fs.readFileSync(path.join(process.cwd(), "source", "main.js")) +const parsedCode = acorn.parse(source_file) + +for (const node of parsedCode.body) { + for (const index in node.declarations) { + const declaration = node.declarations[index] + visitNode(declaration) + } +} + +function visitNode(node) { + switch (node.type) { + case "VariableDeclarator": + tranlateVariable(node) + break; + case "CallExpression": + + break; + case "Literal": + + break; + case "Identifier": + + break; + case "ArrowFunctionExpression": + + break; + case "FunctionExpression": + + break; + case "BinaryExpression": + + break; + case "Program": + + break; + case "ExpressionStatement": + + break; + case "BlockStatement": + + break; + + default: + console.error("Unsupported declaration type (too complex): " + node.type) + break + } +} + +function tranlateVariable(node) { + if (node.init.arguments && node.init.arguments[0] && node.init.arguments[0].value.includes("fennec")) { + return + } + if (!node.init) { + return + } + switch (node.init.type) { + case "NewExpression": + translateNewExpression(node) + break + default: + console.error("Unsupported declaration type (too complex): " + node.type) + break + } +} + +function translateNewExpression(nodeInitializer) { + if (nodeInitializer.init.callee) { + switch (nodeInitializer.init.callee.name) { + case "Fennec": + + break; + + default: + console.error("Unsupported declaration type (too complex): " + node.type) + break; + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b76457a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "fennec", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "fennec", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e0a4be8 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "fennec", + "version": "0.0.1", + "description": "a simple transpiler project to write 3DS homebrew in js style", + "repository": { + "type": "git", + "url": "https://gitea.azures.fr/ChibiEditor/Fennec" + }, + "license": "MIT", + "author": { + "name": "azures04", + "email": "gilleslazure04@gmail.com", + "url": "https://chibieditor.fr" + }, + "type": "commonjs", + "main": "index.js", + "scripts": { + "cpp:build": "cd runtime && make", + "cpp:clean": "cd runtime && make clean" + }, + "dependencies": { + "acorn": "^8.16.0" + } +} diff --git a/runtime/Makefile b/runtime/Makefile new file mode 100644 index 0000000..0289d67 --- /dev/null +++ b/runtime/Makefile @@ -0,0 +1,39 @@ +DEVKITPRO := C:/Users/Usuario/Documents/softwares/devkitPro +DEVKITARM := $(DEVKITPRO)/devkitARM +LIBCTRU := $(DEVKITPRO)/libctru + +3DSXTOOL := $(DEVKITPRO)/tools/bin/3dsxtool.exe +CXX := $(DEVKITARM)/bin/arm-none-eabi-g++.exe + +include $(DEVKITARM)/3ds_rules + +TARGET := FennecApp +INCLUDES := -Ilib -Igen -I$(LIBCTRU)/include \ + -I$(DEVKITPRO)/libcitro2d/include \ + -I$(DEVKITPRO)/libcitro3d/include + +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mfpu=vfp +CFLAGS := -g -Wall -O2 -mword-relocations $(ARCH) +CXXFLAGS := $(CFLAGS) -fno-exceptions -fno-rtti $(INCLUDES) + +LDFLAGS := $(ARCH) -specs=3dsx.specs -g +LIBS := -lcitro2d -lcitro3d -lctru -lm + +all: $(TARGET).3dsx + +$(TARGET).3dsx: $(TARGET).elf + "$(3DSXTOOL)" $< $@ \ + --smdh="../build/metadata.smdh" + @echo "🦊 Fennec : Build terminé avec succès !" + +$(TARGET).elf: lib/fennec_core.o gen/out.o + $(CXX) $(LDFLAGS) -o $@ $^ -L$(LIBCTRU)/lib $(LIBS) + +lib/fennec_core.o: lib/fennec_core.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +gen/out.o: gen/out.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -f lib/*.o gen/*.o *.elf *.3dsx \ No newline at end of file diff --git a/runtime/gen/out.cpp b/runtime/gen/out.cpp new file mode 100644 index 0000000..dde9285 --- /dev/null +++ b/runtime/gen/out.cpp @@ -0,0 +1,29 @@ +#include "fennec_core.h" + +int main() { + Fennec::init(); + + u32 white = Fennec::color(255, 255, 255); + u32 red = Fennec::color(255, 0, 0); + u32 blue = Fennec::color(0, 0, 255); + + while (aptMainLoop()) { + hidScanInput(); + if (hidKeysDown() & KEY_START) break; + + Fennec::beginFrame(); + + Fennec::selectScreen(GFX_TOP); + Fennec::drawRect(10, 10, 380, 220, blue); // Un fond bleu + Fennec::drawText(50, 100, "BIENVENUE SUR FENNEC", white); + + Fennec::selectScreen(GFX_BOTTOM); + Fennec::drawRect(50, 50, 100, 100, red); + Fennec::drawText(60, 160, "Carré rouge :)", white); + + Fennec::endFrame(); + } + + Fennec::exit(); + return 0; +} \ No newline at end of file diff --git a/runtime/lib/fennec_core.cpp b/runtime/lib/fennec_core.cpp new file mode 100644 index 0000000..51a0b41 --- /dev/null +++ b/runtime/lib/fennec_core.cpp @@ -0,0 +1,64 @@ +#include "fennec_core.h" + +namespace Fennec { + static C3D_RenderTarget* topTarget; + static C3D_RenderTarget* bottomTarget; + static C3D_RenderTarget* currentTarget; + + static C2D_TextBuf staticTextBuf; + + void init() { + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); + + staticTextBuf = C2D_TextBufNew(4096); + + topTarget = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); + bottomTarget = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); + + currentTarget = topTarget; + } + + void drawRect(float x, float y, float z, float w, float h, u32 color) { + C2D_DrawRectSolid(x, y, 0.5f, w, h, color); + } + + void drawText(float x, float y, std::string content, u32 color) { + if (!staticTextBuf) return; + C2D_Text textObj; + C2D_TextParse(&textObj, staticTextBuf, content.c_str()); + C2D_TextOptimize(&textObj); + C2D_DrawText(&textObj, C2D_WithColor, x, y, 0.5f, 0.1f, 0.1f, color); + } + + void clearTextBuffer() { + C2D_TextBufClear(staticTextBuf) + } + + void exit() { + C2D_TextBufDelete(staticTextBuf); + C2D_Fini(); + C3D_Fini(); + gfxExit(); + } + + void beginFrame() { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TextBufClear(staticTextBuf); + } + + void endFrame() { + C3D_FrameEnd(0); + } + + void selectScreen(gfxScreen_t screen) { + currentTarget = (screen == GFX_TOP) ? topTarget : bottomTarget; + C2D_SceneBegin(currentTarget); + } + + u32 color(u8 r, u8 g, u8 b, u8 a) { + return C2D_Color32(r, g, b, a); + } +} \ No newline at end of file diff --git a/runtime/lib/fennec_core.h b/runtime/lib/fennec_core.h new file mode 100644 index 0000000..28eca45 --- /dev/null +++ b/runtime/lib/fennec_core.h @@ -0,0 +1,21 @@ +#ifndef FENNEC_CORE_H +#define FENNEC_CORE_H + +#include <3ds.h> +#include +#include + +namespace Fennec { + void init(); + void exit(); + void beginFrame(); + void endFrame(); + + void drawRect(float x, float y, float w, float h, u32 color); + void drawText(float x, float y, std::string content, u32 color); + + void selectScreen(gfxScreen_t screen); + u32 color(u8 r, u8 g, u8 b, u8 a = 255); +} + +#endif \ No newline at end of file diff --git a/source/main.js b/source/main.js new file mode 100644 index 0000000..ad5ac57 --- /dev/null +++ b/source/main.js @@ -0,0 +1,11 @@ +const { Fennec, N3DSText } = require("../bin/fennec") + +const game = new Fennec() +const topScreenGraph = game.screens.Top.graphics2d +const bottomScreenGraph = game.screens.Bottom.graphics2d +const loremIpsum = new N3DSText("Hello world") + +game.onGameLoop(() => { + topScreenGraph.drawRect(0, 0, 0.5, 100, 20, null, null) + bottomScreenGraph.drawText(loremIpsum) +}) \ No newline at end of file