diff --git a/README.md b/README.md
index 91b6b20..b0619a3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# FSPDS
+
+
+# FSPDS V0.1
## Flipnote Studio Player for Nintendo DS
@@ -10,7 +12,7 @@ This simple ROM allows you to play flipnotes on Nintendo DS (Lite) using your fl
- Create a new folder named ```/flipnotes``` at the root of your SD card
- Copy your favorite flipnotes to ```/flipnotes``` directory
-- Copy ```FSPDS.nds``` anywhere on your SD card
+- Copy ```FSPDS.nds``` anywhere to your SD card
- Insert your flashcard into the DS, then turn on the console
- Launch ```FSPDS.nds```
- Select a file from the menu using the D-pad
@@ -18,14 +20,18 @@ This simple ROM allows you to play flipnotes on Nintendo DS (Lite) using your fl
- Use ```L```/```R``` buttons to navigate through bottom-screen tabs
## Screenshots
+
+
+
+ Thumbnail of one of the sample flipnotes (Cat on a skateboard) displayed by FSPDS
+
-![ROM in action: show thumbnail](https://github.com/NotImplementedLife/Resources/blob/master/FSPDS/images/ss01.png "Show thumbnail")
-
-*Thumbnail of one of the sample flipnotes (Cat on a skateboard) displayed by FSPDS*
-![ROM in action: playing a flipnote](https://github.com/NotImplementedLife/Resources/blob/master/FSPDS/images/ss02.png "Playing a flipnote")
-
-*Capture of "eBay Song" flipnote by RANDOM☆GUY played by FSPDS*
+
+
+
+ Capture of "eBay Song" flipnote by RANDOM☆GUY played by FSPDS
+
## Bugs/Limitations
@@ -33,6 +39,13 @@ This simple ROM allows you to play flipnotes on Nintendo DS (Lite) using your fl
- Don't place more than 1024 files in ```/flipnotes``` directory, otherwise FSPDS won't be capable to see them all
(also intentional behavior, probably will be changed in the future)
- No sound currently
-- It's possible to encounter some graphical glitches (they'll be fixed soon)
+- ~~It's possible to encounter some graphical glitches (they'll be fixed soon)~~ [*Hopefully not!*]
+
+### Tested on DeSmuMe emulator and R4(i) flashcards
+
+## Credits
-- Tested on R4(i) flashcards
+- Guys from [DSiBrew](https://dsibrew.org/wiki/Flipnote_Files/PPM "PPM Format") and [Flipnote Collective](https://github.com/Flipnote-Collective/flipnote-studio-docs/wiki/PPM-format "PPM Format") for their awesome documentation on .PPM file format.
+- [devkitPro](https://github.com/devkitPro) for their compiler, ndslib and examples
+- The authors behind [GodMode9i](https://github.com/DS-Homebrew/GodMode9i "GodMode9i") for the idea of overwriting character fonts
+- [Drenn1/GameYob](https://github.com/Drenn1/GameYob "Drenn1/GameYob") for helping me figure out how to work with makefiles.
\ No newline at end of file
diff --git a/README_Resources/ss01.png b/README_Resources/ss01.png
new file mode 100644
index 0000000..b20a0e5
Binary files /dev/null and b/README_Resources/ss01.png differ
diff --git a/README_Resources/ss02.png b/README_Resources/ss02.png
new file mode 100644
index 0000000..6ad453c
Binary files /dev/null and b/README_Resources/ss02.png differ
diff --git a/arm9/Makefile b/arm9/Makefile
index 3e8a477..2eab6bf 100644
--- a/arm9/Makefile
+++ b/arm9/Makefile
@@ -31,7 +31,7 @@ CFLAGS := -Wall -Wno-psabi -DNO_VIZ \
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math -O3 $(ARCH) $(DEBUG) \
-DVERSION_STRING=\"`git describe --always --abbrev=4`\"\
- -DC_IO_FUNCTIONS -DNIFI \
+ -DC_IO_FUNCTIONS \
-include "nds/ndstypes.h"
CFLAGS += $(INCLUDE) -DARM9 -DDS
diff --git a/arm9/include/console.h b/arm9/include/console.h
index e93c8a0..471aaf8 100644
--- a/arm9/include/console.h
+++ b/arm9/include/console.h
@@ -31,6 +31,8 @@ void c_writeFrame()
iprintf("\025");
for(int i=1;i<31;i++) iprintf("\004");
iprintf("\023");
+ c_goto(23,4);
+ iprintf("V0.1 - Created by N\032I\032L");
//for(i=32;i--;) iprintf("\x02");
}
@@ -98,7 +100,7 @@ static inline void initConsole()
dmaFillWords(0xFFFFF000, (void*)0x62002B4, 4);
dmaFillWords(0x00000000, (void*)0x62002B8, 8);
- // Char #26
+ // Char #26 - Play Triangle Up 1
void* address=(void*)0x62002C0;
u32 word=0x000000FF;
for(u8 i=3;i--;)
@@ -109,7 +111,7 @@ static inline void initConsole()
}
dmaFillWords(word, address,20);
- // Char #27
+ // Char #27 - Play Triangle Up 2
address=(void*)0x62002E0;
word=0x00000000;
dmaFillWords(word,address,16);
@@ -122,7 +124,7 @@ static inline void initConsole()
address+=0x4;
}
- // Char #28
+ // Char #28 - Play Triangle Bottom 1
address=(void*)0x6200300;
word=0xFFFFFFFF;
@@ -136,7 +138,7 @@ static inline void initConsole()
address+=0x4;
}
- // Char #29
+ // Char #29 - Play Triangle Bottom 2
address=(void*)0x6200320;
word=0xFFFFFFFF;
for(int i=4;i--;)
@@ -147,6 +149,12 @@ static inline void initConsole()
}
dmaFillWords(word,address,16);
+ // Char #30 - Bullet
+ dmaFillWords(0x00000000, (void*)0x6200340, 8);
+ dmaFillWords(0x000FF000, (void*)0x6200348, 4);
+ dmaFillWords(0x00FFFF00, (void*)0x620034C, 8);
+ dmaFillWords(0x000FF000, (void*)0x6200354, 4);
+ dmaFillWords(0x00000000, (void*)0x6200358, 8);
BG_PALETTE[ 0]=BG_PALETTE_SUB[ 0]=0x7FFF; // used when clear screen
BG_PALETTE[ 15]=BG_PALETTE_SUB[ 15]=0x7FFF; // used by \x1b[30m
@@ -183,7 +191,7 @@ void c_loadingBox()
for(int i=10;i<13;i++)
{
c_goto(i,10);
- for(int j=0;j<10;j++) iprintf(" ");
+ for(int j=0;j<12;j++) iprintf(" ");
}
consoleSelect(&consoleFG);
iprintf("\x1b[39m");
diff --git a/arm9/include/player.h b/arm9/include/player.h
index ddf6a04..99c7c74 100644
--- a/arm9/include/player.h
+++ b/arm9/include/player.h
@@ -52,16 +52,12 @@ void setPixelThumbnail(u8 x,u8 y,u16 c)
{
int xx=32+x, yy=48+2*y;
mainGFX[128*(yy++)+xx]=c+(c<<8);
- //mainGFX[128*(yy++)+xx]=c;
mainGFX[128*yy+xx]=c+(c<<8);
- //mainGFX[128*yy+xx]=c;
}
void playerClear()
{
- for(int x=0;x<256;x++)
- for(int y=0;y<192;y++)
- mainGFX[128*y+x]=0x1010;
+ memset(mainGFX,0x10,256*192);
}
void displayThumbnail()
@@ -88,13 +84,9 @@ void displayThumbnail()
}
}
}
- //bgSetScroll(bgMain, -50, -50);
- //bgUpdate();
playerSwapBuffers();
}
-//u8 *layer;
-
struct decodeType
{
int i, k;
@@ -103,90 +95,22 @@ struct decodeType
bool diffing;
};
-void buildLayer(struct decodeType* dt)
-{
- /*if(dt->diffing)
- {
- //memcpy(layerA,layer,LAYER_SIZE);
- dmaCopy(layer,layerA,LAYER_SIZE);
- }*/
- for(u16 y=0;y<192;y++)
- {
- u16 yy=y<<5;
- switch((ppm_AnimationData[dt->i+(y>>2)]>>((y&0x3)<<1)) & 0x3)
- {
- case 0:
- {
- //dmaFillWords((u32)0,(u8*)(layer+yy),8);
- //yy+=32;
- for(u8 _i=0;_i<32;_i++)
- layer1[yy++]=0;
- break;
- }
- case 3:
- {
- for(u16 c=32;c--;)
- {
- layer1[yy++] = ppm_AnimationData[dt->k++];
- }
- break;
- }
- case 2:
- {
- // Fill line
- for(int _i=0; _i<32; layer1[yy+(_i++)]=0xFF);
- goto __DECODETYPE_1_2__;
- }
-
- case 1:
- {
- // Clear line
- for(int _i=0; _i<32; layer1[yy+(_i++)]=0);
-
- __DECODETYPE_1_2__: ;
-
- u32 bytes=ppm_AnimationData[dt->k++]<<24;
- bytes|=ppm_AnimationData[dt->k++]<<16;
- bytes|=ppm_AnimationData[dt->k++]<<8;
- bytes|=ppm_AnimationData[dt->k++];
- for(;bytes;)
- {
- if(bytes & 0x80000000)
- {
- layer1[yy] = ppm_AnimationData[dt->k++];
- }
- bytes<<=1;
- yy++;
- }
- break;
- }
- }
- }
- /*if(dt->diffing)
- {
- for(u16 y=0;y<96;y++)
- {
- if(ytY) continue;
- if(y-dt->tY>=192) break;
- u16 BL=y<<5,BA=(y-dt->tY)<<5;
- for(u8 c=0;c<32;c++)
- for(u8 b=0;b<8;b++)
- {
- u8 x=8*c+b;
- if(xtX) continue;
- if(x-dt->tX>=256) break;
- u8 dx=(x-dt->tX);
- layer[BL+c]^= ((bool)(layerA[BA+(dx>>3)]&(1<<(dx&0x7))))< The maximum flipnote framerate is 30fps. */
+/** > 1/30 = 1/60 + 1/60, hence, in 1/30 seconds two vBlanks happen */
+/** > Frame rendering is too expensive to be executed in < 1/60 = 16.6ms */
+/** > So, I'll split the frame rendering into two separate subtasks, each */
+/** > to be executed in its own vBlank. */
+/** > */
+/** > The procedure is as follows: */
+/** > vBlank 0 - parse animation data and do frame diffing up to ~68% of */
+/** > the lines */
+/** > vBlank 1 - finish frame diffing and write graphics into memory */
+/** > */
+/** > If the playback speed is smaller (e.g. 12fps = 5 vBlanks), then the */
+/** > code does nothing during the additional vBlanks. */
+/** > The real rendering speed is adjusted using the {counter} in main() */
-/* [ vBlank Op ] Because maximum flipnote frame rate is 30 fps (so a frame each 2 vBlanks)
- I decided render each of the two layers on a separate vBlank
- Hope this will minimize the chances of missing vBlanks, which could cause
- synchronization troubles
-*/
void playerNextFrameVBlank0(struct decodeType* dt)
{
@@ -203,8 +127,6 @@ void playerNextFrameVBlank0(struct decodeType* dt)
if(dt->diffing)
{
- //dmaCopy(layer1,layerA,LAYER_SIZE);
- //dmaCopy(layer2,layerB,LAYER_SIZE);
memcpy(layerA,layer1,LAYER_SIZE);
memcpy(layerB,layer2,LAYER_SIZE);
}
@@ -221,10 +143,6 @@ void playerNextFrameVBlank0(struct decodeType* dt)
}
case 3:
{
- /*for(u16 c=32;c--;)
- {
- layer1[yy++] = ppm_AnimationData[dt->k++];
- }*/
memcpy(layer1+yy,ppm_AnimationData+dt->k,32);
yy+=32;
dt->k+=32;
@@ -234,15 +152,13 @@ void playerNextFrameVBlank0(struct decodeType* dt)
{
// Fill line
memset(layer1+yy,0xFF,32);
- //for(int _i=0; _i<32; layer1[yy+(_i++)]=0xFF);
goto __DECODETYPE_1_2__;
}
case 1:
{
// Clear line
- //memset(layer1+yy,0x00,32);
- for(int _i=0; _i<32; layer1[yy+(_i++)]=0);
+ memset(layer1+yy,0x00,32);
__DECODETYPE_1_2__: ;
@@ -271,10 +187,6 @@ void playerNextFrameVBlank0(struct decodeType* dt)
{
case 0:
{
- //dmaFillWords((u32)0,(u8*)(layer+yy),8);
- //yy+=32;
- //for(u8 _i=0;_i<32;_i++)
- // layer2[yy++]=0;
memset(layer2+yy,0,32);
yy+=32;
break;
@@ -290,7 +202,6 @@ void playerNextFrameVBlank0(struct decodeType* dt)
{
// Fill line
memset(layer2+yy,0xFF,32);
- //for(int _i=0; _i<32; layer2[yy+(_i++)]=0xFF);
goto __DECODETYPE_1_2__L2;
}
@@ -298,7 +209,6 @@ void playerNextFrameVBlank0(struct decodeType* dt)
{
// Clear line
memset(layer2+yy,0x00,32);
- //for(int _i=0; _i<32; layer2[yy+(_i++)]=0);
__DECODETYPE_1_2__L2: ;
@@ -323,15 +233,14 @@ void playerNextFrameVBlank0(struct decodeType* dt)
{
u16 ld0 = (dt->tX>=0) ? (dt->tX>>3) : 0;
u16 pi0 = (dt->tX>=0) ? 0 : (-dt->tX)>>3;
- u8 del = dt->tX>=0 ? (dt->tX & 7) : ((126+dt->tX)&7);
+ u8 del = dt->tX>=0 ? (dt->tX & 7) : (((u8)(dt->tX))&7);
+ u8 ndel=8-del;
u8 alpha = (1<<(8-del))-1;
u8 nalpha = ~alpha;
u16 pi=0,ld=0;
- //c_goto(5,5);
- //iprintf("%d %d %d %d ",del, ld0, pi0,nalpha);
if(dt->tX>=0)
{
- for(u16 y=0;y<120;y++)
+ for(u16 y=0;y<130;y++)
{
if(ytY) continue;
if(y-dt->tY>=192) break;
@@ -341,8 +250,8 @@ void playerNextFrameVBlank0(struct decodeType* dt)
layer2[ld++] ^= layerB[pi] & alpha;
while((ld&31)<31)
{
- layer1[ld]^=(layerA[pi]&nalpha) | (layerA[pi+1]&alpha);
- layer2[ld]^=(layerB[pi]&nalpha) | (layerB[pi+1]&alpha);
+ layer1[ld]^=((layerA[pi]&nalpha)>>ndel) | ((layerA[pi+1]&alpha)<>ndel) | ((layerB[pi+1]&alpha)<tY) continue;
if(y-dt->tY>=192) break;
@@ -359,8 +268,8 @@ void playerNextFrameVBlank0(struct decodeType* dt)
pi=((y-dt->tY)<<5)+pi0;
while((pi&31)<31)
{
- layer1[ld]^=(layerA[pi]&nalpha) | (layerA[pi+1]&alpha);
- layer2[ld]^=(layerB[pi]&nalpha) | (layerB[pi+1]&alpha);
+ layer1[ld]^=((layerA[pi]&nalpha)>>ndel) | ((layerA[pi+1]&alpha)<>ndel) | ((layerB[pi+1]&alpha)<diffing)
{
- //c_goto(1,1);
- //iprintf("%d ",frameTime[ppm_FramePlaybackSpeed]);
u16 ld0 = (dt->tX>=0) ? (dt->tX>>3) : 0;
- u16 pi0 = (dt->tX>=0) ? 0 : (-dt->tX)>>3;
- u8 del = dt->tX>=0 ? (dt->tX & 7) : ((126+dt->tX)&7);
+ u16 pi0 = (dt->tX>=0) ? 0 : (((u8)(-dt->tX))>>3);
+ u8 del = (dt->tX>=0) ? (dt->tX & 7) : (((u8)(dt->tX))&7);
+ u8 ndel=8-del;
u8 alpha = (1<<(8-del))-1;
u8 nalpha = ~alpha;
u16 pi=0,ld=0;
if(dt->tX>=0)
{
- for(u16 y=120;y<192;y++)
+ for(u16 y=130;y<192;y++)
{
if(ytY) continue;
if(y-dt->tY>=192) break;
ld=(y<<5)+ld0;
pi=((y-dt->tY)<<5)+pi0;
- layer1[ld] ^= layerA[pi] & alpha;
- layer2[ld++] ^= layerB[pi] & alpha;
+ layer1[ld] ^= (layerA[pi] & alpha)<>ndel) | ((layerA[pi+1]&alpha)<>ndel) | ((layerB[pi+1]&alpha)<>ndel) | ((layerA[pi+1]&alpha)<>ndel) | ((layerB[pi+1]&alpha)<tY) continue;
if(y-dt->tY>=192) break;
@@ -413,16 +321,18 @@ void playerNextFrameVBlank1(struct decodeType* dt)
pi=((y-dt->tY)<<5)+pi0;
while((pi&31)<31)
{
- layer1[ld]^=(layerA[pi]&nalpha) | (layerA[pi+1]&alpha);
- layer2[ld]^=(layerB[pi]&nalpha) | (layerB[pi+1]&alpha);
+ layer1[ld]^=((layerA[pi]&nalpha)>>ndel) | ((layerA[pi+1]&alpha)<>ndel) | ((layerB[pi+1]&alpha)<>ndel;
+ layer2[ld] ^= (layerB[pi] & nalpha)>>ndel;
}
}
}
+ // prepare to draw frame
+
u16 paperColor = (dt->first_byte_header & 1);
u16 layer1Color = (dt->first_byte_header>>1) & 0x3;
if(layer1Color<2)
@@ -438,12 +348,10 @@ void playerNextFrameVBlank1(struct decodeType* dt)
u16 p=0,val=0;
for(u16 y=0,yy=0;y<192;y++)
{
- //u16 yy=(y<<5);
for(u8 c=32;c--;yy++)
{
for(u8 b=0;b<8;b++)
{
- //u16 p=(yy<<2)+(b>>1);
if(layer1[yy]&(1<