Add files via upload

This commit is contained in:
Rekushi authored and GitHub committed 2026-10-29 11:56:15 +00:00
commit 92f296ddb2
18 files changed
+12656

No files matched your search

+348
View File
@@ -0,0 +1,348 @@
(function(){
var loader=document.getElementById('flipnote-editor-loader');
var statusEl=loader&&loader.querySelector('[data-flipnote-loader-status]');
var progressEl=loader&&loader.querySelector('.flipnote-editor-loader-progress');
var progressFill=loader&&loader.querySelector('[data-flipnote-loader-progress]');
var copyEl=loader&&loader.querySelector('.flipnote-editor-loader-copy');
var titleEl=loader&&loader.querySelector('.flipnote-editor-loader-title');
var drawingEngineScript='./assets/flipnote-editor/drawing-engine.js?v=2.47-advanced-eraser-paint-memory';
var textToolScript='./assets/flipnote-editor/text-tool.js?v=3.26.2';
var editorScript='./assets/flipnote-editor/editor.js?v=3.26.2-canvas';
// The server enumerates every image in web/static/flipnote-editor on each request.
// This means newly-added button states (including manually-added tab art) join the
// preload automatically without needing another JavaScript manifest update.
// Keep a small explicit list for image assets used by the editor but stored elsewhere.
var imageUrls=[];
var imageCacheBank=document.getElementById('flipnote-editor-image-cache');
var decodedImageCache=window.__flipnoteEditorImageCache=window.__flipnoteEditorImageCache||new Map();
document.querySelectorAll('img[src]').forEach(function(img){imageUrls.push(img.getAttribute('src'));});
document.querySelectorAll('*').forEach(function(node){
Array.from(node.attributes||[]).forEach(function(attr){
if(/^data-.*src$/i.test(attr.name)&&attr.value)imageUrls.push(attr.value);
});
});
imageUrls=Array.from(new Set(imageUrls.concat(["./assets/flipnote-editor/base.png","./assets/flipnote-editor/checker.png","./assets/flipnote-editor/clear.png","./assets/flipnote-editor/clone-placeholder.png","./assets/flipnote-editor/closepages.png","./assets/flipnote-editor/colour.png","./assets/flipnote-editor/copy.png","./assets/flipnote-editor/copy_pressed.png","./assets/flipnote-editor/cut.png","./assets/flipnote-editor/delete-frame.png","./assets/flipnote-editor/downloadwebm.png","./assets/flipnote-editor/Draw.png","./assets/flipnote-editor/Draw_selected.png","./assets/flipnote-editor/earsHurt.png","./assets/flipnote-editor/editpattern.png","./assets/flipnote-editor/Erase.png","./assets/flipnote-editor/erase1.png","./assets/flipnote-editor/erase1_Adv.png","./assets/flipnote-editor/erase2.png","./assets/flipnote-editor/erase2_Adv.png","./assets/flipnote-editor/erase3.png","./assets/flipnote-editor/erase3_Adv.png","./assets/flipnote-editor/erase4.png","./assets/flipnote-editor/erase4_Adv.png","./assets/flipnote-editor/erase5.png","./assets/flipnote-editor/erase5_Adv.png","./assets/flipnote-editor/erase6.png","./assets/flipnote-editor/erase6_Adv.png","./assets/flipnote-editor/erase7.png","./assets/flipnote-editor/erase7_Adv.png","./assets/flipnote-editor/erase8.png","./assets/flipnote-editor/erase8_Adv.png","./assets/flipnote-editor/eraseaudio.png","./assets/flipnote-editor/erasebig.png","./assets/flipnote-editor/eraser.png","./assets/flipnote-editor/Erase_selected.png","./assets/flipnote-editor/fill.png","./assets/flipnote-editor/fill1.png","./assets/flipnote-editor/fill2.png","./assets/flipnote-editor/fill3.png","./assets/flipnote-editor/fill4.png","./assets/flipnote-editor/fill5.png","./assets/flipnote-editor/fill6.png","./assets/flipnote-editor/fill7.png","./assets/flipnote-editor/fill8.png","./assets/flipnote-editor/fill_selected.png","./assets/flipnote-editor/filmstrip-dots.png","./assets/flipnote-editor/first-frame.png","./assets/flipnote-editor/flip_x.png","./assets/flipnote-editor/flip_y.png","./assets/flipnote-editor/goodVolume.png","./assets/flipnote-editor/ImportImage.png","./assets/flipnote-editor/invert.png","./assets/flipnote-editor/last-frame.png","./assets/flipnote-editor/layer1.png","./assets/flipnote-editor/layer1_inactive.png","./assets/flipnote-editor/layer2.png","./assets/flipnote-editor/layer2_inactive.png","./assets/flipnote-editor/lightbox-placeholder.png","./assets/flipnote-editor/lightbox.png","./assets/flipnote-editor/lightbox_pressed.png","./assets/flipnote-editor/loop_disabled.png","./assets/flipnote-editor/loop_enabled.png","./assets/flipnote-editor/musicBarEmpty.png","./assets/flipnote-editor/musicImportalt.png","./assets/flipnote-editor/musicPlay.png","./assets/flipnote-editor/musicRec.png","./assets/flipnote-editor/musicStop.png","./assets/flipnote-editor/new-frame.png","./assets/flipnote-editor/next-frame.png","./assets/flipnote-editor/openpages.png","./assets/flipnote-editor/openppm.png","./assets/flipnote-editor/pageCopy.png","./assets/flipnote-editor/pageErase.png","./assets/flipnote-editor/pageInsert.png","./assets/flipnote-editor/pagePaste.png","./assets/flipnote-editor/Paint.png","./assets/flipnote-editor/paint1.png","./assets/flipnote-editor/paint1_Adv.png","./assets/flipnote-editor/paint2.png","./assets/flipnote-editor/paint2_Adv.png","./assets/flipnote-editor/paint3.png","./assets/flipnote-editor/paint3_Adv.png","./assets/flipnote-editor/paint4.png","./assets/flipnote-editor/paint4_Adv.png","./assets/flipnote-editor/paint5.png","./assets/flipnote-editor/paint5_Adv.png","./assets/flipnote-editor/paint6.png","./assets/flipnote-editor/paint6_Adv.png","./assets/flipnote-editor/paint7.png","./assets/flipnote-editor/paint7_Adv.png","./assets/flipnote-editor/paint8.png","./assets/flipnote-editor/paint8_Adv.png","./assets/flipnote-editor/Paint_selected.png","./assets/flipnote-editor/paper-placeholder.png","./assets/flipnote-editor/paperB.png","./assets/flipnote-editor/paperW.png","./assets/flipnote-editor/paste.png","./assets/flipnote-editor/pen.png","./assets/flipnote-editor/pen1.png","./assets/flipnote-editor/pen1_Adv.png","./assets/flipnote-editor/pen2.png","./assets/flipnote-editor/pen2_Adv.png","./assets/flipnote-editor/pen3.png","./assets/flipnote-editor/pen3_Adv.png","./assets/flipnote-editor/pen4.png","./assets/flipnote-editor/pen4_Adv.png","./assets/flipnote-editor/pen5.png","./assets/flipnote-editor/pen5_Adv.png","./assets/flipnote-editor/pen6.png","./assets/flipnote-editor/pen6_Adv.png","./assets/flipnote-editor/pen7.png","./assets/flipnote-editor/pen7_Adv.png","./assets/flipnote-editor/pen8.png","./assets/flipnote-editor/pen8_Adv.png","./assets/flipnote-editor/play.png","./assets/flipnote-editor/previous-frame.png","./assets/flipnote-editor/redo.png","./assets/flipnote-editor/redo_grey.png","./assets/flipnote-editor/resize_larger.png","./assets/flipnote-editor/resize_smaller.png","./assets/flipnote-editor/rotate_anticlockwise.png","./assets/flipnLine truncated
var fontJobs=[{css:'16px "NDS BIOS"',sample:'Preloading assets 00 / 00'}];
var completed=0;
var total=imageUrls.length+fontJobs.length+5;
function updateProgress(){
var safeTotal=Math.max(total,1);
var percent=Math.max(0,Math.min(100,Math.round((completed/safeTotal)*100)));
if(statusEl)statusEl.textContent='Preloading assets '+Math.min(completed,total)+' / '+total;
if(progressFill)progressFill.style.width=percent+'%';
if(progressEl)progressEl.setAttribute('aria-valuenow',String(percent));
}
function doneOne(){completed+=1;updateProgress();}
function preloadImage(url){
var cached=decodedImageCache.get(url);
if(cached&&cached.promise)return cached.promise.then(function(){doneOne();});
var image=new Image();
image.alt='';
image.decoding='sync';
image.loading='eager';
image.setAttribute('aria-hidden','true');
if(imageCacheBank)imageCacheBank.appendChild(image);
var promise=new Promise(function(resolve,reject){
var finished=false;
var decoding=false;
var finish=function(ok){
if(finished)return;
finished=true;
if(!ok){reject(new Error('asset '+url));return;}
resolve(image);
};
var decodeLoaded=function(){
if(finished||decoding)return;
decoding=true;
if(typeof image.decode==='function'){
image.decode().then(function(){finish(true);}).catch(function(){
finish(image.complete&&image.naturalWidth>0);
});
}else{
finish(image.complete&&image.naturalWidth>0);
}
};
image.onload=decodeLoaded;
image.onerror=function(){finish(false);};
image.src=url;
if(image.complete&&image.naturalWidth>0)decodeLoaded();
});
decodedImageCache.set(url,{image:image,promise:promise});
return promise.then(function(){doneOne();});
}
function preloadFont(job){
if(!document.fonts||!document.fonts.load)return Promise.reject(new Error('font loading unsupported'));
return document.fonts.load(job.css,job.sample).then(function(faces){
if(!faces||!faces.length)throw new Error('font '+job.css);
doneOne();
});
}
function loadFonts(){
return Promise.all(fontJobs.map(preloadFont)).then(function(){
if(loader)loader.classList.add('fonts-ready');
if(copyEl)copyEl.setAttribute('aria-hidden','false');
});
}
function loadScript(url,label){
return new Promise(function(resolve,reject){
var script=document.createElement('script');
script.src=url;
script.onload=function(){doneOne();resolve();};
script.onerror=function(){reject(new Error(label||'editor script'));};
document.body.appendChild(script);
});
}
function loadEditor(){return loadScript('./assets/vendor/flipnote.js','PPM reader').then(function(){return loadScript('./assets/ppm-import.js','PPM importer');}).then(function(){return loadScript(drawingEngineScript,'drawing engine');}).then(function(){return loadScript(textToolScript,'text tool');}).then(function(){return loadScript(editorScript,'editor');});}
function installEditorInputFixes(){
var titleField=document.getElementById('filename');
if(titleField&&!titleField.dataset.titleLimitFix){
titleField.dataset.titleLimitFix='1';
var titleLimit=80;
function titleText(){return String(titleField.textContent||'').replace(/ /g,' ');}
function selectionLength(){
var selection=window.getSelection&&window.getSelection();
if(!selection||!selection.rangeCount)return 0;
var range=selection.getRangeAt(0);
if(!titleField.contains(range.commonAncestorContainer))return 0;
return range.toString().length;
}
function putCaretAtEnd(){
var selection=window.getSelection&&window.getSelection();
if(!selection)return;
var range=document.createRange();
range.selectNodeContents(titleField);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
titleField.addEventListener('keydown',function(event){
if(event.defaultPrevented||event.ctrlKey||event.metaKey||event.altKey)return;
if(event.key&&event.key.length===1&&titleText().length-selectionLength()>=titleLimit){
event.preventDefault();
event.stopImmediatePropagation();
}
},true);
titleField.addEventListener('beforeinput',function(event){
if(event.defaultPrevented||!event.inputType||event.inputType.indexOf('insert')!==0)return;
var incoming=typeof event.data==='string'?event.data:'';
if(event.inputType==='insertLineBreak'||event.inputType==='insertParagraph'){
event.preventDefault();
return;
}
if(titleText().length-selectionLength()+(incoming?incoming.length:1)>titleLimit){
event.preventDefault();
}
},true);
titleField.addEventListener('paste',function(event){
var clipboard=event.clipboardData||window.clipboardData;
if(!clipboard)return;
var incoming=String(clipboard.getData('text')||'').replace(/[\r\n]+/g,' ');
var room=Math.max(0,titleLimit-(titleText().length-selectionLength()));
if(incoming.length<=room)return;
event.preventDefault();
if(room>0){
var partial=incoming.slice(0,room);
if(document.execCommand)document.execCommand('insertText',false,partial);
}
},true);
titleField.addEventListener('input',function(){
var value=titleText();
if(value.length<=titleLimit)return;
titleField.textContent=value.slice(0,titleLimit);
putCaretAtEnd();
},true);
}
var paperTool=document.getElementById('paper-tool');
function pageUsesBlackPaper(){
if(!paperTool)return false;
if(paperTool.getAttribute('aria-pressed')==='true')return true;
var image=paperTool.querySelector('img');
return !!(image&&/paperB\.png(?:[?#]|$)/i.test(image.getAttribute('src')||''));
}
function syncMonochromeInkLabel(){
var label=pageUsesBlackPaper()?'White':'Black';
['flipnote-image-import-colour','flipnote-image-import-second-colour'].forEach(function(id){
var select=document.getElementById(id);
var option=select&&select.querySelector('option[value="black"]');
if(option)option.textContent=label;
});
document.querySelectorAll('.flipnote-editor-layer-color[data-layer-color="black"]').forEach(function(button){
var layer=Number(button.getAttribute('data-layer'))+1;
button.title=label;
button.setAttribute('aria-label','Layer '+layer+' '+label.toLowerCase());
});
}
syncMonochromeInkLabel();
if(paperTool&&!paperTool.dataset.rexiPaperInkLabelFix){
paperTool.dataset.rexiPaperInkLabelFix='1';
paperTool.addEventListener('click',function(){setTimeout(syncMonochromeInkLabel,0);});
var paperObserver=new MutationObserver(syncMonochromeInkLabel);
paperObserver.observe(paperTool,{attributes:true,attributeFilter:['aria-pressed','title'],subtree:true});
var paperImage=paperTool.querySelector('img');
if(paperImage)paperObserver.observe(paperImage,{attributes:true,attributeFilter:['src']});
}
var imageImportButton=document.getElementById('flipnote-import-image-button');
if(imageImportButton&&!imageImportButton.dataset.rexiInkLabelFix){
imageImportButton.dataset.rexiInkLabelFix='1';
imageImportButton.addEventListener('click',function(){setTimeout(syncMonochromeInkLabel,0);});
}
var imageApply=document.getElementById('flipnote-image-import-apply');
if(imageApply&&!imageApply.dataset.rexiImageColourFix){
imageApply.dataset.rexiImageColourFix='1';
imageApply.addEventListener('click',function(){
var mode=document.getElementById('flipnote-image-import-mode');
var first=document.getElementById('flipnote-image-import-colour');
var second=document.getElementById('flipnote-image-import-second-colour');
var layer=document.getElementById('flipnote-image-import-layer');
var chosenMode=mode?mode.value:'single';
var firstColour=first?first.value:'black';
var secondColour=second?second.value:'red';
var chosenLayer=Math.max(0,Math.min(1,Number(layer&&layer.value)||0));
function applyLayerColour(index,colour){
var button=document.querySelector('.flipnote-editor-layer-color[data-layer="'+index+'"][data-layer-color="'+colour+'"]');
if(button)button.click();
}
setTimeout(function(){
if(chosenMode==='dual'){
applyLayerColour(0,firstColour);
applyLayerColour(1,secondColour);
}else{
applyLayerColour(chosenLayer,firstColour);
}
syncMonochromeInkLabel();
},0);
},true);
}
}
function installMobileShiftToggle(){
var button=document.getElementById('flipnote-mobile-shift-toggle');
if(!button)return;
var ua=String(navigator.userAgent||'');
var uaMobile=!!(navigator.userAgentData&&navigator.userAgentData.mobile);
var ipadLike=navigator.platform==='MacIntel'&&Number(navigator.maxTouchPoints||0)>1;
var mobileOrTablet=uaMobile||ipadLike||/(Android|iPad|iPhone|iPod|Mobile|Tablet|Silk|Kindle)/i.test(ua);
var desktopSetting=document.getElementById('flipnote-editor-desktop-shift');
var desktopSettingKey='flipnote-editor.flipnote-editor.desktop-shift.v1';
var desktopEnabled=false;
try{desktopEnabled=window.localStorage.getItem(desktopSettingKey)==='1';}catch(_error){}
if(desktopSetting)desktopSetting.checked=desktopEnabled;
if(!mobileOrTablet&&desktopSetting)desktopSetting.addEventListener('change',function(){
desktopEnabled=desktopSetting.checked;
try{window.localStorage.setItem(desktopSettingKey,desktopEnabled?'1':'0');}catch(_error){}
if(!desktopEnabled)setVirtualShift(false);
button.hidden=!desktopEnabled;
});
var image=button.querySelector('img');
var normalSrc=image&&image.getAttribute('data-normal-src')||'./assets/flipnote-editor/shiftl.png';
var activeSrc=image&&image.getAttribute('data-active-src')||'./assets/flipnote-editor/shiftl_active.png';
var active=false;
function makeShiftEvent(type,down){
var event;
try{
event=new KeyboardEvent(type,{key:'Shift',code:'ShiftLeft',location:1,shiftKey:!!down,bubbles:true,cancelable:true});
}catch(_error){
event=document.createEvent('Event');
event.initEvent(type,true,true);
}
try{Object.defineProperty(event,'key',{configurable:true,get:function(){return 'Shift';}});}catch(_error){}
try{Object.defineProperty(event,'code',{configurable:true,get:function(){return 'ShiftLeft';}});}catch(_error){}
try{Object.defineProperty(event,'keyCode',{configurable:true,get:function(){return 16;}});}catch(_error){}
try{Object.defineProperty(event,'which',{configurable:true,get:function(){return 16;}});}catch(_error){}
try{Object.defineProperty(event,'shiftKey',{configurable:true,get:function(){return !!down;}});}catch(_error){}
var target=document.body||document.documentElement||document;
target.dispatchEvent(event);
}
function setVirtualShift(next){
next=!!next;
if(active===next)return;
active=next;
button.setAttribute('aria-pressed',active?'true':'false');
button.title=active?'Shift enabled':'Shift';
if(image)image.src=active?activeSrc:normalSrc;
makeShiftEvent(active?'keydown':'keyup',active);
}
function forceShiftModifier(event){
if(!active||!event)return;
try{Object.defineProperty(event,'shiftKey',{configurable:true,get:function(){return true;}});}catch(_error){}
if(typeof event.getModifierState==='function'){
var original=event.getModifierState.bind(event);
try{Object.defineProperty(event,'getModifierState',{configurable:true,value:function(key){return String(key||'').toLowerCase()==='shift'?true:original(key);}});}catch(_error){}
}
}
['pointerdown','pointerup','mousedown','mouseup','click','dblclick'].forEach(function(type){
document.addEventListener(type,forceShiftModifier,true);
});
function consumesOneShotShift(target){
if(!target||!target.closest)return false;
var slot=target.closest('[data-tool-slot]');
if(slot){
var slotImage=slot.querySelector('img');
var slotSrc=slotImage&&String(slotImage.getAttribute('src')||'');
return slotSrc.toLowerCase().indexOf('_adv.png')!==-1;
}
return !!target.closest('#copy-tool,.flipnote-editor-more-tool,[data-audio-control]');
}
button.addEventListener('click',function(){setVirtualShift(!active);});
document.addEventListener('click',function(event){
if(!active||button.contains(event.target)||!consumesOneShotShift(event.target))return;
window.setTimeout(function(){setVirtualShift(false);},0);
},true);
document.addEventListener('keyup',function(event){
if(active&&event.key==='Shift')window.setTimeout(function(){if(active)makeShiftEvent('keydown',true);},0);
});
window.addEventListener('focus',function(){if(active)makeShiftEvent('keydown',true);});
window.addEventListener('pagehide',function(){if(active)makeShiftEvent('keyup',false);});
button.hidden=!(mobileOrTablet||desktopEnabled);
}
function reveal(){
updateProgress();
document.body.classList.remove('flipnote-editor-loading');
if(!loader)return;
loader.classList.add('is-exiting');
var removed=false;
var removeLoader=function(){
if(removed)return;
removed=true;
loader.remove();
};
loader.addEventListener('transitionend',function(event){
if(event.target===loader&&event.propertyName==='transform')removeLoader();
});
window.setTimeout(removeLoader,900);
}
function fail(error){ if(loader)loader.classList.add('load-failed'); if(titleEl)titleEl.textContent='Flipnote Editor could not load'; if(statusEl)statusEl.textContent=error&&error.message?error.message:'Try refreshing the page.'; }
updateProgress();
var imageJobs=imageUrls.map(preloadImage);
var fontPromise=loadFonts();
function settleDecodedImages(){
return new Promise(function(resolve){
requestAnimationFrame(function(){requestAnimationFrame(resolve);});
});
}
Promise.all(imageJobs.concat([fontPromise])).then(settleDecodedImages).then(loadEditor).then(function(){installEditorInputFixes();installMobileShiftToggle();return settleDecodedImages();}).then(reveal).catch(fail);
})();
+2462
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 240 240">
<path fill="#AE301F" fill-rule="evenodd" d="M64,48 L88,48 C96.836556,48 104,55.163444 104,64 L104,176 C104,184.836556 96.836556,192 88,192 L64,192 C55.163444,192 48,184.836556 48,176 L48,64 C48,55.163444 55.163444,48 64,48 Z M152,48 L176,48 C184.836556,48 192,55.163444 192,64 L192,176 C192,184.836556 184.836556,192 176,192 L152,192 C143.163444,192 136,184.836556 136,176 L136,64 C136,55.163444 143.163444,48 152,48 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

+117
View File
@@ -0,0 +1,117 @@
(function (global) {
'use strict';
const WIDTH = 256;
const HEIGHT = 192;
const MAX_BYTES = 1024 * 1024;
const MAX_PAGES = 999;
const SAMPLE_RATE = 32768;
function checkFile(buffer) {
if (!(buffer instanceof ArrayBuffer) || buffer.byteLength < 0x6C0 || buffer.byteLength > MAX_BYTES) {
throw new Error('Choose a complete Flipnote Studio PPM, no larger than 1,024 KB.');
}
const view = new DataView(buffer);
if (view.getUint32(0, false) !== 0x50415241) throw new Error('That file is not a Flipnote Studio PPM.');
const pages = view.getUint16(12, true) + 1;
const animationEnd = 0x6A0 + view.getUint32(4, true);
if (pages > MAX_PAGES || animationEnd < 0x6A8 || animationEnd + pages > buffer.byteLength) {
throw new Error('The PPM page data is incomplete or exceeds 999 pages.');
}
const tableBytes = view.getUint16(0x6A0, true);
const firstFrame = 0x6A8 + tableBytes;
if (tableBytes < pages * 4 || firstFrame >= animationEnd) throw new Error('The PPM page table is invalid.');
for (let index = 0; index < pages; index++) {
if (firstFrame + view.getUint32(0x6A8 + index * 4, true) >= animationEnd) {
throw new Error('A PPM page points outside the animation data.');
}
}
const soundStart = Math.ceil((animationEnd + pages) / 4) * 4;
if (soundStart + 32 + view.getUint32(8, true) > buffer.byteLength) {
throw new Error('The PPM audio data is incomplete.');
}
let audioBytes = 0;
for (let index = 0; index < 4; index++) audioBytes += view.getUint32(soundStart + index * 4, true);
if (audioBytes > view.getUint32(8, true)) throw new Error('The PPM audio track lengths are invalid.');
return pages;
}
function base64(bytes) {
let text = '';
for (let offset = 0; offset < bytes.length; offset += 8192) {
text += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
}
return btoa(text);
}
function layerData(pixels) {
const bytes = new Uint8Array(WIDTH * HEIGHT / 8);
for (let index = 0; index < pixels.length; index++) {
if (pixels[index]) bytes[index >> 3] |= 1 << (index & 7);
}
return base64(bytes);
}
function pcmWav(pcm) {
const bytes = new Uint8Array(44 + pcm.length * 2);
const view = new DataView(bytes.buffer);
const text = (offset, value) => { for (let i = 0; i < value.length; i++) bytes[offset + i] = value.charCodeAt(i); };
text(0, 'RIFF'); view.setUint32(4, bytes.length - 8, true); text(8, 'WAVE');
text(12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true);
view.setUint16(22, 1, true); view.setUint32(24, SAMPLE_RATE, true);
view.setUint32(28, SAMPLE_RATE * 2, true); view.setUint16(32, 2, true); view.setUint16(34, 16, true);
text(36, 'data'); view.setUint32(40, pcm.length * 2, true);
for (let i = 0; i < pcm.length; i++) view.setInt16(44 + i * 2, pcm[i], true);
return bytes;
}
function decode(buffer, filename = 'Flipnote.ppm') {
checkFile(buffer);
const parser = new global.flipnote.PpmParser(buffer);
const flags = parser.decodeSoundFlags();
const ink = index => index === 2 ? 'red' : index === 3 ? 'blue' : 'black';
const frames = [];
for (let index = 0; index < parser.frameCount; index++) {
const layers = parser.decodeFrame(index);
const palette = parser.getFramePaletteIndices(index);
frames.push({
layer1: layerData(layers[0]), layer2: layerData(layers[1]),
colours: [ink(palette[1]), ink(palette[2])],
paper: palette[0] === 1 ? 'black' : 'white',
sounds: {sound1: flags[index][0], sound2: flags[index][1], sound3: flags[index][2]},
});
}
const audio = {};
const tracks = [['music', global.flipnote.FlipnoteAudioTrack.BGM],
['sound1', global.flipnote.FlipnoteAudioTrack.SE1],
['sound2', global.flipnote.FlipnoteAudioTrack.SE2],
['sound3', global.flipnote.FlipnoteAudioTrack.SE3]];
for (const [key, track] of tracks) {
if (!parser.hasAudioTrack(track)) continue;
const pcm = parser.getAudioTrackPcm(track, SAMPLE_RATE);
if (!pcm.length) continue;
audio[key] = {data: base64(pcmWav(pcm)), mime:'audio/wav', duration:pcm.length / SAMPLE_RATE,
reference_fps:parser.framerate, source_name:filename, source_kind:'ppm_import'};
}
return {
project: {
title:String(filename).replace(/\.ppm$/i, '').slice(0, 32) || 'Imported Flipnote',
speed_level:parser.frameSpeed, loop:parser.meta.loop,
thumbnail_frame:Math.max(0, Math.min(frames.length - 1, parser.thumbFrameIndex)),
layer_visibility:[parser.layerVisibility[1], parser.layerVisibility[2]], frames, audio,
},
source:{filename, original_author_name:parser.meta.current.username},
};
}
async function read(file) {
if (!file || !/\.ppm$/i.test(file.name || '')) throw new Error('Choose a Flipnote Studio .PPM file.');
if (file.size > MAX_BYTES) throw new Error('That PPM is larger than the 1,024 KB limit.');
try { return decode(await file.arrayBuffer(), file.name); }
catch (error) { throw new Error('Could not open this PPM: ' + (error.message || 'invalid file data')); }
}
const api = Object.freeze({read, decode});
global.FlipnotePpmImport = api;
if (typeof module === 'object' && module.exports) module.exports = api;
})(typeof window === 'object' ? window : globalThis);
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 James Daniel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+8232
View File
File diff suppressed because it is too large. Load diff