added old 3ds stuff

This commit is contained in:
Mrrp 2025-01-01 14:17:16 -08:00
parent 526bdc91cc
commit 61e90aef4f
13 changed files with 853 additions and 5574 deletions

View file

@ -1,5 +1,12 @@
{ {
"posts": [ "posts": [
{
"metadata": {
"description": "A guide to using RomFS on the 3DS. (Old)", "date": "2025-01-01", "tags": ["3ds", "programming", "c", "devkitpro", "old"], "previous": "old3ds_helloworld.md", "next": "old3ds_touchscreen.md"
},
"id": "old3ds_romfs",
"url": "/blog/old3ds_romfs.md"
},
{ {
"metadata": { "metadata": {
"description": "A curated list of awesome stuff I like", "date": "2024-11-26", "tags": ["awesome", "curated"] "description": "A curated list of awesome stuff I like", "date": "2024-11-26", "tags": ["awesome", "curated"]
@ -16,10 +23,24 @@
}, },
{ {
"metadata": { "metadata": {
"description": "A test post to see how the site styling looks", "date": "2024-12-31", "tags": ["meta", "web"] "description": "A guide to using the touchscreen on the 3DS. (Old)", "date": "2025-01-01", "tags": ["3ds", "programming", "c", "devkitpro", "old"], "previous": "old3ds_romfs.md"
},
"id": "old3ds_touchscreen",
"url": "/blog/old3ds_touchscreen.md"
},
{
"metadata": {
"description": "A test post to see how the site styling looks", "date": "2025-01-01", "tags": ["meta", "web"]
}, },
"id": "styling_test", "id": "styling_test",
"url": "/blog/styling_test.md" "url": "/blog/styling_test.md"
},
{
"metadata": {
"description": "A guide to creating a simple Hello, World program for the 3DS. (Old)", "date": "2025-01-01", "tags": ["3ds", "programming", "c", "devkitpro", "old"], "next": "old3ds_romfs.md"
},
"id": "old3ds_helloworld",
"url": "/blog/old3ds_helloworld.md"
} }
] ]
} }

View file

@ -31,17 +31,8 @@ export default function configured_markdown(): MarkdownIt {
"</code></pre>"; "</code></pre>";
}, },
}); });
// .use(figure, {
// mathFence: true,
// render: (content: any, displayMode: any) => {
// // render tex here and return svg
// console.log("figure render");
// console.log(content);
// return TeXToSVG(content);
// },
// });
md = md.use(tab) md = md
.use(tasklist) .use(tasklist)
.use(mark) .use(mark)
.use(footnote) .use(footnote)
@ -51,6 +42,8 @@ export default function configured_markdown(): MarkdownIt {
"important", "success", "caution", "question", "done", "important", "success", "caution", "question", "done",
"quote", "deprecated", "example" "quote", "deprecated", "example"
], ],
}).use(tab, {
name: "tabs"
}); });
md.renderer.rules.text = function (tokens, idx, options, env, self) { md.renderer.rules.text = function (tokens, idx, options, env, self) {

View file

@ -301,5 +301,4 @@ Hosted
@apply border-2 border-purple-600; @apply border-2 border-purple-600;
box-shadow: 0 0 7px #A020F0; box-shadow: 0 0 7px #A020F0;
} }
</style> </style>

View file

@ -1,10 +1,17 @@
<script setup> <script setup>
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import fm from 'front-matter'; import fm from 'front-matter';
import { auto } from '@popperjs/core';
const props = defineProps({ const props = defineProps({
url: String, url: String,
tagFilter: Array tagFilter: Array,
// Allow sm, md, and lg
size: {
type: String,
default: 'full',
validator: (value) => ['xs', 'sm', 'md', 'full'].includes(value)
}
}); });
const url = ref(props.url) const url = ref(props.url)
@ -19,6 +26,7 @@ const description = ref(null);
const date = ref(null); const date = ref(null);
const tags = ref(null); const tags = ref(null);
// watch the params of the route to fetch the data again // watch the params of the route to fetch the data again
watch(url, async () => { watch(url, async () => {
await fetchData() await fetchData()
@ -38,6 +46,8 @@ async function fetchData() {
description.value = processed.attributes.description description.value = processed.attributes.description
date.value = processed.attributes.date date.value = processed.attributes.date
tags.value = processed.attributes.tags tags.value = processed.attributes.tags
date.value = new Date(date.value).toLocaleDateString()
} catch (err) { } catch (err) {
error.value = err.toString() error.value = err.toString()
loading.value = false loading.value = false
@ -49,6 +59,8 @@ async function fetchData() {
<template> <template>
<NuxtLink :href="'/blog?post=' +url"> <NuxtLink :href="'/blog?post=' +url">
<!-- Large -->
<div v-if = "size === 'full'">
<div class="m-4 min-h-30 min-width-90 text-white transition hover:bg-purple-600 bg-opacity-50 hover:bg-opacity-70"> <div class="m-4 min-h-30 min-width-90 text-white transition hover:bg-purple-600 bg-opacity-50 hover:bg-opacity-70">
<Card> <Card>
<div v-if="loading" class="text-center animate-pulse"> <div v-if="loading" class="text-center animate-pulse">
@ -61,6 +73,7 @@ async function fetchData() {
<div class="grid"> <div class="grid">
<div class="justify-center"> <div class="justify-center">
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<small>{{ date }}</small>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<div v-for="tag in tags" :key="tag" class="m-1 text-center"> <div v-for="tag in tags" :key="tag" class="m-1 text-center">
@ -77,5 +90,88 @@ async function fetchData() {
</div> </div>
</Card> </Card>
</div> </div>
</div>
<!-- Medium -->
<div v-else-if = "size === 'md'">
<div class="m-4 min-h-30 min-width-50 text-white transition hover:bg-purple-600 bg-opacity-50 hover:bg-opacity-70">
<Card>
<div v-if="loading" class="text-center animate-pulse">
</div>
<div v-else-if="error" class="text-center">
<h2>Error: {{ error }}</h2>
<button @click="fetchData">Retry</button>
</div>
<div v-else>
<div class="grid">
<div class="justify-center">
<h2>{{ title }}</h2>
<small>{{ date }}</small>
</div>
<div class="flex justify-center">
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
<div v-if="props.tagFilter.includes(tag)">
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
</div>
<div v-else>
<div class="flex justify-center">
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
<div v-if="props.tagFilter.includes(tag)">
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
</div>
<div v-else>
<span class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{ tag }}</span>
</div>
</div>
</div>
<span class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{ tag }}</span>
</div>
</div>
</div>
</div>
</div>
</Card>
</div>
</div>
<!-- Small -->
<div v-else-if = "size === 'sm'">
<div class="m-4 min-h-30 min-width-30 text-white transition hover:bg-purple-600 bg-opacity-50 hover:bg-opacity-70">
<Card>
<div v-if="loading" class="text-center animate-pulse">
</div>
<div v-else-if="error" class="text-center">
<h2>Error: {{ error }}</h2>
<button @click="fetchData">Retry</button>
</div>
<div v-else>
<div class="grid">
<div class="justify-center">
<h3>{{ title }}</h3>
<small>{{ date }}</small>
</div>
</div>
</div>
</Card>
</div>
</div>
<!-- Extra Small -->
<div v-else-if = "size === 'xs'">
<div class="m-4 min-h-30 min-width-20 text-white transition hover:bg-purple-600 bg-opacity-50 hover:bg-opacity-70">
<Card>
<div v-if="loading" class="text-center animate-pulse">
</div>
<div v-else-if="error" class="text-center">
<h3>Error: {{ error }}</h3>
<button @click="fetchData">Retry</button>
</div>
<div v-else>
<div class="grid">
<div class="justify-center">
<h5>{{ title }}</h5>
</div>
</div>
</div>
</Card>
</div>
</div>
</NuxtLink> </NuxtLink>
</template> </template>

5812
deno.lock generated

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,8 @@ const title = ref(null);
const description = ref(null); const description = ref(null);
const date = ref(null); const date = ref(null);
const tags = ref(null); const tags = ref(null);
const previous = ref(null);
const next = ref(null);
// watch the params of the route to fetch the data again // watch the params of the route to fetch the data again
@ -62,8 +64,17 @@ async function fetchData(url) {
background.value = processed.attributes.background background.value = processed.attributes.background
title.value = processed.attributes.title title.value = processed.attributes.title
description.value = processed.attributes.description description.value = processed.attributes.description
date.value = processed.attributes.date date.value = processed.attributes.date.toLocaleDateString()
tags.value = processed.attributes.tags tags.value = processed.attributes.tags
if (processed.attributes.previous)
previous.value = "/blog/?post=/blog/" + processed.attributes.previous
else
previous.value = null
if (processed.attributes.next)
next.value = "/blog/?post=/blog/" + processed.attributes.next
else
next.value = null
} catch (err) { } catch (err) {
error.value = err.toString() error.value = err.toString()
console.error(err) console.error(err)
@ -111,9 +122,15 @@ async function fetchList() {
// Extract the tags // Extract the tags
tagList.value = blog_list.posts.flatMap(post => post.metadata.tags).filter((tag, index, self) => self.indexOf(tag) === index) tagList.value = blog_list.posts.flatMap(post => post.metadata.tags).filter((tag, index, self) => self.indexOf(tag) === index)
// Sort the posts by date, most recent first
list.value.sort((a, b) => new Date(b.metadata.date) - new Date(a.metadata.date))
} }
fetchList() fetchList()
function resetReadingPosition() {
window.scrollTo(0, 0)
}
// Hook and check if the URL gets changed in the query params // Hook and check if the URL gets changed in the query params
watch(url, async () => { watch(url, async () => {
await fetchData(url.value) await fetchData(url.value)
@ -150,14 +167,35 @@ watch(url, async () => {
<Transition name="list"> <Transition name="list">
<div class="flex flex-col" :key="url"> <div class="flex flex-col" :key="url">
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<small>{{ date }}</small>
<div class="max-w-50 flex flex-row justify-center"> <div class="max-w-50 flex flex-row justify-center">
<div v-for="tag in tags" :key="tag" class="m-1 text-center"> <div v-for="tag in tags" :key="tag" class="m-1 text-center">
<span class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{ <span class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{
tag }}</span> tag }}</span>
</div> </div>
</div> </div>
<div>
<!-- Next/Prev controls, on the left and right side using PostCards -->
<div class="flex max-w-4xl max-md:w-screen">
<div class="justify-start">
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous" class="m-2 text-white">Previous</NuxtLink>
</div>
<div class="justify-end">
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next" class="m-2 text-white">Next</NuxtLink>
</div>
</div>
</div>
<Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left"> <Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left">
<Markdown :text="text"></Markdown> <Markdown :text="text"></Markdown>
<!-- Aligned next/prev controls -->
<div class="flex">
<div class="justify-start">
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous" class="m-2 text-white">Previous</NuxtLink>
</div>
<div class="justify-end">
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next" class="m-2 text-white">Next</NuxtLink>
</div>
</div>
</Card> </Card>
</div> </div>
</Transition> </Transition>

View file

@ -0,0 +1,129 @@
---
title: 3DS Programming - Hello World
description: A guide to creating a simple Hello, World program for the 3DS. (Old)
date: 2025-01-01
tags: ['3ds', 'programming', 'c', 'devkitpro', 'old']
next: old3ds_romfs.md
---
# Hello, World!
> [!warning]
> This guide of mine is old - Like 3 years old at the time of posting. I prefer to keep it up, but I don't think it very highly - It should still work though.
> I might make a new one in the future.
> I'm doing some alterations at least to make it more readable.
> It doesn't teach why things are done very well is my primary issue with it.
'Hello, World!' is a very simple program used for checking a program's build system, and showing its basic syntax.
We should do both before going forward. You should have your project set up for this.
And if you are using VSCode, I would recommend you get the include paths set up.
## Begin coding
Start by including 'stdio.h' (or 'cstdio'!) and '3ds.h'.
A quick explanation of '3ds.h'
'3ds.h' is the file containing references to all the VITAL things we can use to program on the system.
It does not include many features, such as (And certainly not limited to) rendering capabilities, aside from running the console)
## Main loop
Start by setting up a main function as usual for C/C++.
To initialize the graphics, we need to run gfxInitDefault().
To initialize the console, we need to run consoleInit([SCREEN HERE], NULL).
Replace '[SCREEN HERE]' with either GFX_TOP or GFX_BOTTOM, depending on what screen you want.
At this point, the code should look something like this:
<!-- Note: I originally used images for these, but... Why did I do that? -->
```c
#include <stdio.h>
#include <3ds.h>
int main() {
// Initialize the console
gfxInitDefault();
consoleInit(GFX_BOTTOM, NULL);
return 0;
}
```
As the console is now ready, you can now print to the screen. You can use puts(), printf(), std::cout, etc.
If we try and run it now, it should immediately close itself, as there is no main loop.
To create a main loop, we create a while loop with aptMainLoop() as its argument:
```c
// Initialize the console
// ...
/// Main loop
while(aptMainLoop()) {
// Code here
}
```
This will loop infinitely until the user closes the program.
## Input
Okay, so we now have a main loop... How do we exit back to the Homebrew Launcher?
Simple. We can scan for input, and break if it detects a button, in this case START, being pressed.
We can scan for input with hidScanInput(), and get which keys are down with hidKeysDown() like so:
```c
// Main loop
while(aptMainLoop()) {
// Scan for input
hidScanInput();
u32 kDown = hidKeysDown();
}
```
Every button is mapped to a different bit on the unsigned 32-bit integer.
We can get specific keys via an AND operation between the integer and the desired key:
```c
// Main loop
while(aptMainLoop()) {
// Scan for input
hidScanInput();
u32 kDown = hidKeysDown();
// Break if START is pressed
if(kDown & KEY_START) break;
}
```
## Wrapping up
We can add V-Sync with gspWaitForVBlank(), which may help if you want that and don't want to draw anything aside from the console. You can disregard this if you want:
```c
// Main loop
while(aptMainLoop()) {
// Scan for input
// ...
// Break if START is pressed
// ...
// Wait for V-Blank
gspWaitForVBlank();
}
```
And we are done. You can run the program, and get the words 'Hello, World!' printed on the screen:
![Failed to load image](/files/old3ds/helloworld/dkp_progress0.png)

161
public/blog/old3ds_romfs.md Normal file
View file

@ -0,0 +1,161 @@
---
title: 3DS Programming - Using RomFS
description: A guide to using RomFS on the 3DS. (Old)
date: 2025-01-01
tags: ['3ds', 'programming', 'c', 'devkitpro', 'old']
previous: old3ds_helloworld.md
next: old3ds_touchscreen.md
---
# Accessing ROM files with RomFS
> [!warning]
> This guide of mine is old - Like 3 years old at the time of posting. I prefer to keep it up, but I don't think it very highly - It should still work though.
> I might make a new one in the future.
> I'm doing some alterations at least to make it more readable.
> It doesn't teach why things are done very well is my primary issue with it.
RomFS allows us to access files stored on the ROM file, cartridge, or application.
It can be used to read things such as graphics assets, audio data, and other data.
## RomFS folder
You may have noticed while building that there is a folder named 'romfs' in your project:
```
- Project
- build
- **romfs**
- source
- Makefile
- Project.3dsx
```
(If it is not already present, create it now.)
This is where all the program files are stored. These files are mounted on 'romfs:/' when the app is running.
If you have any sprites, they will appear in the 'gfx' directory inside of 'romfs:/', and are accessed accordingly.
Create a text file inside of 'romfs', name it something like 'sample.txt' (or 'sample' if you have file extensions off):
```
- Project
- build
- **romfs**
- sample.txt
- source
- Makefile
- Project.3dsx
```
This file will appear as 'romfs:/sample.txt' in the program.
## Using RomFS
We will initialize RomFS, check if a file exists, and then deinitialize.
### Initializing
Initializing RomFS is trivial - It only takes one simple function to do it:
```c
// Initialize the console
// ...
// Initialize RomFS
romfsInit();
// Main loop
// ...
```
### Using
To access a file, we do basically the exact same thing we could do on a home computer in C or C++, just with a different drive.
We will check if the file exists, first by trying to open it, and checking if it failed to open. This is what you would normally do in C:
```c
// Check if the file exists
FILE* file = fopen("romfs:/sample.txt", "r");
if (file == NULL) {
// File does not exist
puts("File does not exist.");
} else {
// File exists
puts("File exists.");
}
// Close the file
fclose(file);
// Main loop
```
You can play with this by adding or removing sample.txt from the 'romfs' folder.
### De-initializing
Like initializing RomFS, it only takes another simple function to de-initialize:
```c
// Main loop
// ...
// De-initialize RomFS
romfsExit();
// Return...
```
And that is all you need to access files on the ROM.
## Wrapping up
Our code should now look like this:
```c
#include <stdio.h>
#include <3ds.h>
int main() {
// Initialize the console
gfxInitDefault();
consoleInit(GFX_BOTTOM, NULL);
// Initialize RomFS
romfsInit();
// Check if the file exists
FILE* file = fopen("romfs:/sample.txt", "r");
if (file == NULL) {
// File does not exist
puts("File does not exist.");
} else {
// File exists
puts("File exists.");
}
// Close the file
fclose(file);
// Main loop
while(aptMainLoop()) {
// Scan for input
hidScanInput();
u32 kDown = hidKeysDown();
// Close the program if START is pressed
if (kDown & KEY_START) break;
}
// De-initialize RomFS
romfsExit();
return 0;
}
```

View file

@ -0,0 +1,98 @@
---
title: 3DS Programming - Touchscreen Input
description: A guide to using the touchscreen on the 3DS. (Old)
date: 2025-01-01
tags: ['3ds', 'programming', 'c', 'devkitpro', 'old']
previous: old3ds_romfs.md
---
# Touchscreen Input
> [!warning]
> This guide of mine is old - Like 3 years old at the time of posting. I prefer to keep it up, but I don't think it very highly - It should still work though.
> I might make a new one in the future.
> I'm doing some alterations at least to make it more readable.
> It doesn't teach why things are done very well is my primary issue with it.
Here we will find the position of a touch on the touchscreen.
It is very simple to do this.
## Getting the touch position
To find the touch position, we need to request the touchPosition type from the OS, and read from that.
We must first create a touchPosition like so:
```c
// Check for input
// ...
// Will contain the position of the touch
touchPosition touch;
// Will obtain that information
hidTouchRead(&touch);
// Exit program if START is pressed
// ...
```
Then, we must read from it like so:
The touch position is stored in our touchPosition as 'px' and 'py'.
## Printing the information to the screen
To print the position so we may see it, we should, instead of spamming the console with it, overwrite the X and Y position output by setting the position of the output first.
This would make it much easier to read, although we would need some whitespace after it to prevent anything getting printed without overwriting:
```c
// Print the touch screen coordinates
// Keep the whitespace if you do not want issues
printf("\x1b[1;0H X=%u Y=%u ", touch.px, touch.py);
// Exit program if START is pressed
// ...
```
The position should default to [0, 0] when no touch is found:
![Failed to load image](/files/old3ds/touchscreen/dkp_touch0.png)
## Wrapping up
Our code should now look like this:
```c
#include <stdio.h>
#include <3ds.h>
int main() {
// Initialize the console
gfxInitDefault();
consoleInit(GFX_BOTTOM, NULL);
// Will contain the position of the touch
// This can either be inside or outside the main loop
touchPosition touch;
while (aptMainLoop()) {
// Check for input
hidScanInput();
// Will obtain that information
hidTouchRead(&touch);
// Print the touch screen coordinates
// Keep the whitespace if you do not want issues
printf("\x1b[1;0H X=%u Y=%u ", touch.px, touch.py);
// Exit program if START is pressed
if (hidKeysDown() & KEY_START)
break;
}
return 0;
}
```

View file

@ -1,7 +1,7 @@
--- ---
title: Styling Test title: Styling Test
description: A test post to see how the site styling looks description: A test post to see how the site styling looks
date: 2024-12-31 date: 2025-01-01
tags: ['meta', 'web'] tags: ['meta', 'web']
--- ---

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -46,7 +46,9 @@ extract_front_matter() {
# use awk # use awk
local front_matter=$(echo "$data" | awk '/---/ && !f {f=1; next} f; /---/ {exit}') local front_matter=$(echo "$data" | awk '/---/ && !f {f=1; next} f; /---/ {exit}')
echo "$front_matter" | sed '1d;$d' | sed 's/^/ "/' | sed 's/: /": "/' | sed 's/$/"/' | tr '\n' ',' | sed 's/,$//' | sed 's/"tags": "\[\(.*\)\]"/"tags": \[\1\]/g' | sed "s/'/\"/g" local processed=$(echo "$front_matter" | sed '1d;$d' | sed 's/^/ "/' | sed 's/: /": "/' | sed 's/$/"/' | tr '\n' ',' | sed 's/,$//' | sed 's/"tags": "\[\(.*\)\]"/"tags": \[\1\]/g' | sed "s/'/\"/g")
echo "$processed"
} }
# Find files via Regex # Find files via Regex