Décoder une entrée de répertoire FAT Microsoft MS-DOS 5.0

27

Le système de fichiers Microsoft FAT possède une table de répertoires pour représenter les "fichiers" dans quels "dossiers" sur le disque. Pour le moment, ces entrées regroupaient de nombreuses informations en une petite quantité de bits. Il y a un tas de spécifications techniques sur Wiki pour les curieux, mais le défi ici va se concentrer sur un décodage "simple" d'une entrée.

Chaque entrée se compose d'un mot binaire de 32 octets, divisé en plusieurs sections. Pour assurer la cohérence de ce défi, nous utiliserons la version MS-DOS 5.0, les octets sont classés en gros caractères et nous appelons l'octet 0x00le plus à gauche et l'octet 0x1Fle plus à droite.

Vous trouverez ci-dessous un bref schéma des sections pertinentes et quel devrait être le résultat de chaque section (en gras ).

  • Les 11 premiers octets sont le nom du fichier au format ASCII (c'est de là que vient le fameux nom de fichier 8.3 - 8 octets pour le nom de fichier, 3 octets pour l'extension). Ce sont des encodages ASCII simples et doivent être sortis en ASCII avec un point (.) Entre .
    • Remarque: les parties 8 et 3 sont rembourrées avec des espaces pour faire une entrée pleine longueur. La sortie doit ignorer les espaces (c'est-à-dire ne pas les sortir).
    • L'extension de fichier peut être vide (c'est-à-dire tous les espaces), auquel cas la sortie ne doit pas sortir le point .
    • Comme ASCII n'utilise que les 7 bits inférieurs, les octets auront tous un interligne 0.
  • L'octet suivant (0x0b) est un masque de bits de ce qui suit:
    • 0x01 Lecture seule - sortie RO
    • 0x02 Caché - sortie H
    • 0x04 Système - sortie S
    • 0x08 Étiquette de volume - sortie VL . La taille du fichier (ci-dessous) doit être sortie en tant que 0 , quelle que soit son entrée réelle.
    • Sous-répertoire 0x10 - sortie SD . La taille du fichier (ci-dessous) doit être sortie en tant que 0 , quelle que soit son entrée réelle.
    • Archive 0x20 - sortie A
    • Périphérique 0x40 - ignoré pour ce défi.
    • 0x80 Réservé - ignoré pour ce défi.
    • Puisqu'il s'agit d'un masque de bits, plusieurs indicateurs sont possibles - toutes les sorties applicables doivent être concaténées ensemble dans n'importe quel ordre. Par exemple, 0xffpourrait être ROHSVLSDA(ou toute autre combinaison).
  • Les deux octets suivants (0x0c et 0x0d) ne sont pas utilisés sous MS-DOS 5.0.
  • Les deux octets suivants (0x0e et 0x0f) sont le temps de création comme suit:
    • Les bits 15 à 11 sont les heures au format 24 heures - sortie 00 à 23
    • Les bits 10 à 5 sont les minutes - sortie 00 à 59
    • Les bits 4 à 0 sont les secondes / 2 - sortie 00 à 58 (notez que les secondes ne sont qu'en résolution de deux secondes)
    • Pour plus de précision: hhhhhmmmmmmssssslorsqu'il est écrit en big-endian.
  • Les deux octets suivants (0x10 et 0x11) sont la date de création comme suit:
    • Les bits 15 à 9 sont l'année - sortie 1980 pour 0jusqu'à 2107 pour127
    • Les bits 8 à 5 sont les mois - sortie 1 à 12 (avec ou sans zéro devant)
    • Les bits 4 à 0 sont le jour - sortie 0 à 31 (avec ou sans zéro devant)
    • Pour plus de précision: yyyyyyymmmmdddddlorsqu'il est écrit en big-endian.
  • Les deux octets suivants (0x12 et 0x13) sont la dernière date d'accès. Bien qu'utilisé dans MS-DOS 5.0, nous ignorons cette partie pour ce défi.
  • Les deux octets suivants (0x14 et 0x15) ne sont pas utilisés par MS-DOS 5.0.
  • Les deux octets suivants (0x16 et 0x17) sont la dernière heure modifiée, suivant le même format que l'heure de création ci-dessus.
  • Les deux octets suivants (0x18 et 0x19) sont la dernière date modifiée, suivant le même format que la date de création ci-dessus.
  • Les deux octets suivants (0x1a et 0x1b) sont l'emplacement de cluster du fichier sur le disque. Nous ignorons cette partie pour ce défi.
  • Les quatre derniers octets (0x1c, 0x1d, 0x1e et 0x1f) sont la taille du fichier - sortie sous forme d' entier non signé , sauf si les indicateurs VL ou SD sont définis (ci-dessus), auquel cas la sortie 0.

Représentation visuelle

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
\______________________________FILENAME________________________________________________/\_ATTR_/\___NOTUSED____/\_CREATIONTIME_/\_CREATIONDATE_/\__LASTACCESS__/\___NOTUSED____/\_MODIFIEDTIME_/\_MODIFIEDDATE_/\___NOTUSED____/\___________FILESIZE___________/

Contribution

  • Un seul mot de 32 octets (c'est-à-dire 256 bits), dans le format qui convient.
    • Il peut s'agir d'une chaîne de 1et 0, comme plusieurs ints non signés , d'un tableau de valeurs booléennes, etc.
    • Veuillez spécifier dans votre réponse le format que vous utilisez pour la saisie.
    • Vous ne pouvez pas accepter plusieurs entrées (c'est-à-dire un tableau prédécoupé dans les tailles d'octets appropriées) à moins que ce soit le seul moyen pour votre langue de saisir des données. L'analyse de l'entrée fait partie du défi.
  • Vous pouvez supposer que l'entrée est valide (par exemple, vous n'avez pas besoin de vérifier la date pour vérifier que la date est valide).
  • Les octets non utilisés peuvent être tous 0, tous 1, etc., tant qu'ils sont présents. Dans les exemples ci-dessous, j'ai utilisé tout 0pour les octets inutilisés.

Sortie

Soit imprimé à l'écran ou retourné, ce qui suit:

  • Le nom de fichier sous forme de chaîne ASCII
  • Les attributs de fichier sous forme de chaîne ASCII
  • L'heure et la date de création, avec des séparateurs appropriés (deux-points, des barres obliques, quelque chose pour distinguer les composants)
  • L'heure et la date modifiées, toujours avec des séparateurs appropriés
  • La taille du fichier

La sortie peut être une chaîne unique séparée par des espaces ou une nouvelle ligne, des éléments séparés dans un tableau, etc. Veuillez spécifier dans votre réponse comment votre sortie est formatée.

Règles

  • Les formats d'E / S standard sont acceptables.
  • Un programme complet ou une fonction sont acceptables.
  • Les failles standard sont interdites.
  • Il s'agit de , donc toutes les règles de golf habituelles s'appliquent et le code le plus court l'emporte.
  • Les éléments intégrés qui remplissent exactement cette fonction sont interdits.

Exemples

0111000001110010011011110110011101110010011000010110110101101101011010010110111001100111000001100000000000000000101000100100010001001000110101000000000000000000000000000000000010100010010001000100100011010100000000000000000000000000000000001101000000000000

programm.ing HS 20:18:08 2016/06/20 20:18:08 2016/06/20 53248

0010000000100000001000000010000001110000011100000110001101100111001000000010000000100000000101000000000000000000010111010110110000111101100111110000000000000000000000000000000010100010010001000100100011010100000000000000000011110000000100111111001011100001

ppcg SDS 11:43:24 2010/12/31 20:18:08 2016/06/20 0
AdmBorkBork
la source
Est-ce correct s'il y a un excès d'espace autour des drapeaux? Par exemple, serait SD Sun ensemble de drapeaux valide?
Morgan Thrapp
@MorganThrapp Bien sûr, ça devrait aller.
AdmBorkBork
Par curiosité, avez-vous acquis beaucoup d'expérience dans l'interaction avec MS-DOS 5.0 à l'époque, ou vous ennuyiez-vous vraiment un jour sur Wikipédia?
cat
3
@cat Certains des deux. Je m'intéresse beaucoup aux ordinateurs depuis l'âge de 5 ans environ et j'ai reçu un Commodore VIC-20. L'un de mes projets en informatique il y a environ 10 ans était de construire notre propre système de fichiers, j'ai donc beaucoup cherché pour cela. Pour cela, j'ai tiré un tas de Wiki et je l'ai réduit à quelque chose qui pourrait être un défi.
AdmBorkBork

Réponses:

6

Verilog, 513 670 617 octets

Fonctionne avec IVerilog. Aucun indicateur de compilation spécial n'est nécessaire.

Il s'agit d'un monstre de définitions imbriquées, de twiddling de bits et de la gêne d'avoir à inverser l'ordre des bits en raison de l'endianité (sinon la chaîne ne s'imprime pas ou l'ordre des bits numériques est incorrect). L'entrée est prise par le iport, qui est la manière habituelle de prendre l'entrée dans un module Verilog. $displayest utilisé pour imprimer en sortie standard. 6 octets pourraient être enregistrés si des zéros non significatifs n'étaient pas requis pour l'horodatage.

`define r(o,b)wire[3:0]o;assign o={i[b],i[b+1],i[b+2],i[b+3]}; 
`define R(t,a,b,c,d,s)`r(a,s)`r(b,s+4)`r(c,s+8)`r(d,s+12)wire[15:0]t;assign t={a,b,c,d};
`define p(m,k)i[90+m]?"k":"",
`define F(a,b)"a a a b\t b%d"
module f(input[0:255]i);`R(d,q,w,e,r,112)`R(D,Q,W,E,R,128)`R(s,z,x,c,v,224)`R(S,Z,X,C,V,240)`R(p,t,y,u,o,176)`R (A,b,n,m,l,192)always@(i)$display(`F(%s%s%s,%02d:%02d:%02d%d/%d/%d),i[0:63],i[64:87]=="   "?" ":".",i[64:87],`p(5,RO)`p(4,H)`p(3,S)`p(2,VL)`p(1,SD)`p(0,A)d[15:11],d[10:5],d[4:0]*2,D[15:9]+1980,D[8:5],D[4:0],p[15:11],p[10:5],p[4:0]*2,A[15:9]+1980,A[8:5],A[4:0],|i[91:92]?0:{s,S});endmodule

Démo

Banc d'essai (non noté):

`timescale 1ns / 1ps

module ftest;
reg [0:255] i;
f uut (
.i(i)
);
initial begin
    i=256'b0111000001110010011011110110011101110010011000010110110101101101011010010110111001100111000001100000000000000000101000100100010001001000110101000000000000000000000000000000000010100010010001000100100011010100000000000000000000000000000000001101000000000000;
    #100;
i=256'b0010000000100000001000000010000001110000011100000110001101100111001000000010000000100000000101000000000000000000010111010110110000111101100111110000000000000000000000000000000010100010010001000100100011010100000000000000000011110000000100111111001011100001;     
end

endmodule

Exemple d'exécution:

$ iverilog design.sv testbench.sv  && vvp a.out  
programm.ing   HS      20:18:08       2016/ 6/20      53248
    ppcg        S  SD  11:43:24       2010/12/31          0
Réintégrer Monica - ζ--
la source
5

Python, 485, 479, 442, 438, 431, 429, 418, 402, 395, 391 , 370 octets.

Enregistré 19 octets grâce à Cᴏɴᴏʀ O'Bʀɪᴇɴ me rappelant que je peux assigner des fonctions à une lettre.

6 octets enregistrés grâce à la suggestion de FryAmTheEggman de nettoyer le filtre de masque de bits.

Enregistré 21 octets grâce à la réponse Ruby impressionnante de W0lf me forçant à jouer au golf un peu plus. ;)

Ceci est un monstre absolu. Je suis sûr que je peux le réduire un peu plus, mais ça se rapproche assez du golf.

a=input()
j=''.join
n=lambda v:int(v,2)
f=j('RO H S VL SD A'.split()[i]for i in range(6)if n(a[88:96])&2**i)
print(j(chr(n(a[x:x+8])).strip()+'.'*(x==56)for x in range(0,88,8)).strip('.'),f,j('%02d:%02d:%02d'%(n(a[b-11:b-6]),n(a[b-6:b]),n(a[b:b+6]))+' %d/%d/%d '%(n(a[b+6:b+12])+1980,n(a[b+12:b+16]),n(a[b+16:b+21]))for b in[123,187]),n(a[208:])*(1-('V'in f or'D'in f)))
Morgan Thrapp
la source
vous pourriez peut-être assigner intà un char? ou peut-être créer une fonction qui fonctionne str int.
Conor O'Brien
@ CᴏɴᴏʀO'Bʀɪᴇɴ Bonne idée!
Morgan Thrapp
L'espace entre or 'SD'peut être supprimé, je pense
Conor O'Brien
@ CᴏɴᴏʀO'Bʀɪᴇɴ Yup, je viens de le faire.
Morgan Thrapp
Sensationnel. Un peu plus court que ce à quoi je m'attendais.
AdmBorkBork
4

Haskell, 781 710 octets

Merci à BlackCap pour une simplification

w n=('0'<$[1..2-length a])++a where a=show n
x s=tail.foldr(\a b->s:a++b)""
t=snd.span(==' ')
y a|all(==' ')a=""|0<1='.':t a
nm=(\(a,b)->t a++y b).splitAt 8
ms n(r,s)|n`mod`2^(r+1)`div`2^r>0=s|0<1=""
tm n=x ':'[w$n`div`2^11,w$n`mod`2^11`div`32,w$2*n`mod`64]
dt n=x '/'[w$1980+n`div`2^9,w$n`mod`2^9`div`32,w$n`mod`32]
pa s=x ' '[nm.map(toEnum.v.take 8).takeWhile(not.null)$iterate(drop 8)a,t,dt$v i,tm$v g,dt$v o,tm$v m,show u,"\n"]where{z n=splitAt(8*n);(a,b)=z 11 s;(c,d)=z 1 b;(e,f)=z 2 d;(g,h)=z 2 f;(i,j)=z 2 h;(k,l)=z 4 j;(m,n)=z 2 l;(o,p)=z 2 n;(q,r)=z 2 p;t=(zip [0..](words"RO H S VL SD A")>>=).ms$v c;u|any(`elem`t)"LD"=0|0<1=v r;v=foldl((+).(2*))0.map(read.pure).filter(`elem`"01")}
main=interact pa

Cela permet en outre aux ordures (comme un caractère de nouvelle ligne) d'apparaître après l'entrée.

Renard
la source
Vous avez des fruits bas: pastebin.com/X69jH75f
BlackCap
3

Java, 1721 1587 1573 1560 1511 octets:

import java.util.*;class A{int Q(String a,int b){return Integer.parseInt(a,b);}String P(int a){return Integer.toString(a);}ArrayList<String>B=new ArrayList<String>();void J(String O){B.add(O);}String TD(String l,String p,int a,int c,int d){String X,Y,Z;X=Y=Z=new String();int i=0;for(char F:l.toCharArray()){if(i<a){X+=F;}if(a<=i&i<c){Y+=F;}if(c<=i){Z+=F;}i++;}String[]H=new String[3];H[0]=P(d+Q(X,2));H[1]=P(Q(Y,2));H[2]=(p==":")?P(Q(Z,2)*2):P(Q(Z,2));int T=0;for(String A:H){H[T]=(A.length()<2)?"0"+A:A;T++;}return H[0]+p+H[1]+p+H[2];}String D(String i){String K=new String();int L=0;for(char Y:i.toCharArray()){if(L%8<1){K+=" ";}K+=Y;L++;}String[]C=K.split(" ");String[]z={"RO","H","S","VL","SD","A"};int[]l={1,2,4,8,16,32};Map<Integer,String>U=new HashMap<Integer,String>();for (int e=0;e<l.length;e++){U.put(l[e],z[e]);}String[]N={":","/",":","/"};int[]M={15,17,23,25};int[]O={5,7,5,7};int[]P={0,1980,0,1980};for(int y=1;y<9;y++){if((char)Q(C[y],2)!=' '){J(Character.toString((char)Q(C[y],2)));}}for(int v=9;v<12;v++){if((char)Q(C[v],2)!=' '){if(!B.contains(".")){J(".");}J(Character.toString((char)Q(C[v],2)));}}J(" ");int T=(char)Q(C[12],2);while(T>0){int H=l[0];for(int V:l){if(V<=T){H=V;}}J(U.get(H));T-=H;}for(int w=0;w<4;w++){J(" ");J(TD(C[M[w]]+C[M[w]+1],N[w],O[w],11,P[w]));}J(" ");if(B.contains("SD")||B.contains("VL")){J("0");}else{J(P(Q(C[29]+C[30]+C[31]+C[32],2)));}return String.join("",B);}public static void main(String[]a){A H=new A();System.out.print(H.D(new Scanner(System.in).next()));}}

Prend l'entrée au format d'une chaîne binaire de 32 octets. Sorties au format d'une chaîne séparée par des espaces. C'est peut-être une réponse très très longue, mais je ne suis toujours pas déçu. Je suis juste content d'avoir pu implémenter cela en Java. Je vais quand même essayer de jouer au golf autant que possible.

Essayez-le en ligne! (Ideone)

R. Kap
la source
1
+1 parce que l'utilisation de Java pour des problèmes de bas niveau est agréablement ironique (un peu comme mon Haskell)
Fox
2

Rubis, 344 octets

m=gets
s=->b,l{b.slice!(0,l).to_i 2}
t=->b{'%02d:%02d:%02d %d/%d/%d'%[s[b,5],s[b,6],2*s[b,5],s[b,7]+1980,s[b,4],s[b,5],]}
i=(0..q=32).map{|i|m[i*8,8].to_i 2}
c=i.map(&:chr).join
n=c[0,8].strip
e=c[8,3].strip
e>?!&&n<<?.+e
f=''
6.times{|j|i[11][j]>0&&f<<%w(RO H S VL SD A)[j]}
$><<[n,f,t[m[112,q]],t[m[176,q]],(f[/VL|SD/]?0:m[-q,q].to_i(2))]*' '

La version légèrement plus lisible est disponible ici .

Test en ligne: http://ideone.com/Fww1Rw

Cristian Lupascu
la source
1
Joli! On dirait que je dois jouer au golf sur 27 autres octets. ;)
Morgan Thrapp
2

JavaScript (ES6), 369

(b,Z=n=>n>9?n:'0'+n,W=n=>s[n]<<8|s[n+1],U=n=>[Z((t=W(n))>>11)+`:${Z(t>>5&63)}:`+Z(t%32*2),((t=W(n+2))>>9)+1980+`/${t>>5&15}/`+t%32],X=l=>String.fromCharCode(...s.slice(p,p+=l)).trim(),s=b.match(/.{8}/g).map(x=>+('0b'+x)),p=0)=>[X(8)+((x=X(3))?'.'+x:x),[...b].map((b,i)=>'A,SD,VL,S,H,RO'.split`,`[z=(2*z|b)%4294967296,i*b-90]||'',z=0).join``,U(14),U(22),b[92]|b[91]?0:z]

Moins golfé

(b,
  Z=n=>n>9?n:'0'+n, // zero pad
  W=n=>s[n]<<8|s[n+1], // get word
  U=n=>[
   Z((t=W(n))>>11)+`:${Z((t>>5&63)}:`+Z(t%32*2),  // decode time
   ((t=W(n+2))>>9)+1980+`/${t>>5&15}/`+t%32 // decode date
  ],
  X=l=>String.fromCharCode(...s.slice(p,p+=l)).trim(), // extract space padded string
  s=b.match(/.{8}/g).map(x=>+('0b'+x)), // convert bits to bytes
  p=0
)=>
  [ X(8)+((x=X(3))?'.'+x:x),
    [...b].map((b,i)=>'A,SD,VL,S,H,RO'.split`,`[i*b-90]||'').join``,
    [...b].slice(-32).map((b,i)=>z=2*z|b,z=0), // this line merged with the preceding one in the golfed code
    U(14),U(22),
    b[92]|b[91]?0:z
  ]

Tester

f=(b,Z=n=>n>9?n:'0'+n,W=n=>s[n]<<8|s[n+1],U=n=>[Z((t=W(n))>>11)+`:${Z(t>>5&63)}:`+Z(t%32*2),((t=W(n+2))>>9)+1980+`/${t>>5&15}/`+t%32],X=l=>String.fromCharCode(...s.slice(p,p+=l)).trim(),s=b.match(/.{8}/g).map(x=>+('0b'+x)),p=0)=>[X(8)+((x=X(3))?'.'+x:x),[...b].map((b,i)=>'A,SD,VL,S,H,RO'.split`,`[z=(2*z|b)%4294967296,i*b-90]||'',z=0).join``,U(14),U(22),b[92]|b[91]?0:z]

O.textContent+='\n'+f('0111000001110010011011110110011101110010011000010110110101101101011010010110111001100111000001100000000000000000101000100100010001001000110101000000000000000000000000000000000010100010010001000100100011010100000000000000000000000000000000001101000000000000')
O.textContent+='\n'+f('0010000000100000001000000010000001110000011100000110001101100111001000000010000000100000000101000000000000000000010111010110110000111101100111110000000000000000000000000000000010100010010001000100100011010100000000000000000011110000000100111111001011100001')
<pre id=O></pre>

edc65
la source
Ok, donc je viens de lancer ça dans Safari et je l'ai Script error.. Mais pour une raison quelconque, Firefox semble fonctionner parfaitement. Je me demande pourquoi ...
R. Kap
@ R.Kap probablement Safari est moins compatible ES6 que Firefox. kangax.github.io/compat-table/es6
edc65
2

PHP ,301 288 octets

for($b=unpack('A8f/A3e/Cl/n/Nc/N/Nm/n/Ns',$argn);$i<8;$f.=1<<$i++&$b[l]?[RO,H,S,VL,SD,A][$i-1]:'');echo trim($b[f].'.'.$b[e],' .')," $f ",($e=function($a){echo date('H:i:s Y/m/d ',mktime($a>>27&31,$a>>21&63,$a>>15&62,$a>>5&15,$a&31,1980+($a>>9&127)));})($b[c]),$e($b[m]),$b[l]&24?0:$b[s];

Essayez-le en ligne!

L'entrée est une chaîne de mots de 32 octets via STDIN, la sortie vers STDOUT.

-13 octets en tant que programme autonome.

640 Ko
la source
2

Stax , 111 octets

¼ΘUßU'ïMo^ø¬├▓> I¬i⌠·╥.↕¥½ßqS,=frT`d_`&&↓⌠ÉûÆiü=┌-< │∟Φ☼⌐¢3²Bu╜lJ╛§≥╪║ε┐╓ù♫╨Z░╖!¥É:╬Çß═╤às8Q←φ,ºï◘≥Ä£}èΦ╡FÉçø¶É

Exécuter et déboguer

récursif
la source
Oups, mon erreur. Je vais faire un correctif.
récursif
1
Il fonctionne correctement maintenant au prix de 3 octets.
récursif
1

Perl, 249 octets

Prend 32 octets en entrée, la sortie est séparée par des retours à la ligne. unpackest parfait pour ce type d'analyse de structure binaire.

($f,$e,$a,$C,$M,$s)=unpack"A8A3CxxNx4Nx2N",<>;$f=~s/ //g;$e=~s/ //g;printf"%s
@{[map+(RO,H,S,VL,SD,A)[$a&1<<$_?$_:9],0..5]}
"."%02d:%02d:%02d %d/%d/%d
"x2 .$s*!($a&24),$f.".$e"x!!$e,map{$_>>27,$_>>21&63,$_>>15&62,$_/512%128+1980,$_>>5&15,$_&31}$C,$M

Quelques faits saillants:

  • Ce qui précède unpack.
  • L'opérateur tortue @{[]}permet d'interpoler du code dans une chaîne. Il crée en fait une référence de tableau qui est ensuite déréférencée.
  • "$str1"x!!$str2est un bon moyen de renvoyer $str1uniquement s'il $str2s'agit d'une chaîne non vide.

Vous trouverez ci-dessous une version qui fonctionne sur de vraies entrées de répertoire, avec des champs en petits caractères, et en ignorant uniquement le remplissage droit sur le nom de fichier et l'extension (donc, par exemple, " ppcg"son espace initial n'a pas été supprimé) (254 octets)

($f,$e,$a,$C,$M,$s)=unpack"A8A3CxxVx4Vx2V",<>;$f=~s/ +$//;$e=~s/ +$//;printf"%s
@{[map+(RO,H,S,VL,SD,A)[$a&1<<$_?$_:9],0..5]}
"."%02d:%02d:%02d %d/%d/%d
"x2 .$s*!($a&24),$f.".$e"x!!$e,map{$_>>11&31,$_>>5&63,2*$_&63,($_>>25)+1980,$_>>21&15,$_>>16&31}$C,$M
ninjalj
la source