Con Kolivas è tornato!

4 settembre 2009

L’anestesista più chiacchierato nel mondo del software libero è tornato a due anni di distanza, dopo l’abbandono dello sviluppo del suo set di patch al kernel Linux (chi non si ricorda il patchset -ck?).

Per ora non pare sia interessato alla mainline di Linux, però ha già rilasciato del codice interessante. Cosa avrà mai sviluppato? Uno scheduler ovviamente :) Perché? Perché Linux è migliorato molto sotto questo aspetto, ma lo scheduler attuale, stando alle parole del nostro eroe, è adatto principalmente per macchine con molti processori, e passa molto tempo calcolando come bilanciare il carico. Kolivas ha fatto uno scheduler che, al contrario, va meglio sui normali computer, meglio se con un paio di cpu :)

Ecco il covo! ck.kolivas.org/patches/bfs/

frafra Linux, News , ,

Tesina multimediale in PyQT4/XHTML/CSS

3 settembre 2009

Ecco che,  come promesso, ho pubblicato la mia tesina multimediale, scritta (e programmata) per la mia maturità.

Attualmente la struttura è funzionante, ma mancano alcune grosse correzioni che ho apportato il 10 luglio (la prima versione che ho appena pubblicato risale al 9 luglio). Le apporterò al più presto (grazie alla copia cartacea della tesina, dato che la versione definitiva che ho portato all’orale s’è volatilizzata).

La cosa interessante di questa tesina multimediale consiste nel fatto che è stata creata con le seguenti caratteristiche:

  • Il programma per visualizzare i contenuti è scritto in Python
  • La libreria grafica utilizzata è QT (>= 2.5) che, grazie a un widget particolare, permette di renderizzare le pagine web con webkit
  • I contenuti sono stati scritti in XHTML/CSS (validati dal correttore del W3C)

Questo mi ha permesso di portare una tesina diversa dal solito, che ha interessato i professori, al posto della noiosissima e poco efficace presentazione Powerpoint. Inoltre, avendola fatta in xhtml/css, l’ho potuta (con un semplice browser) salvare in pdf e stamparla, ottenendo così anche una versione cartacea).

L’ho caricata su github, all’indirizzo: github.com/frafra/Tesina-Frafra/tree

Potete scaricarla premendo su download (e scaricandola sottoforma di archivio compresso), oppure clonando il repository git. Tutto il codice è commentato.

frafra Frafra, Python, QT , , , ,

Nemmeno un editor nano può tanto…

28 agosto 2009

E ora che “L’estate sta finendooo…”, ci si prepara a lottare contro la vita quotidiana… In particolare Frafra ha appena finito di sistemare il suo Aspire One (che monta una Archlinux con sopra KDE svn con tanto di desktop semantico funzionante).

Della serie “ma Frafra non ha nulla di meglio da fare?”, ecco a voi un simpatico screenshot, che narra di una breve discussione a tre, tra me, dolphin e konsole:

Notare la scritta nel riquadro del terminale… La finestra è troppo piccola per nano :D

frafra Cavolate, Frafra , ,

Un nuovo amico: Sheevaplug

5 agosto 2009
sheevaplug

sheevaplug

Eccomi :) In compagnia di Sheeva Plug, il mio nuovo server arrivato dall’America l’altro ieri, con le seguenti caratteristiche:

Processore ARM Kirkwood 1.2 Ghz
Memoria 512 mb DDR2
NAND  512 mb (con Ubuntu Jaunty modificata, con filesystem jffs2 compresso)
Presa USB generica, USB da utilizzare come tty, lettore SD, presa ethernet.
Dimensioni simili a quelle di un alimentatore.
Consumo intorno ai 5 watt ora.

Non male vero? Con la spedizione, il cambio, e la dogana è venuto 130€ circa. Non è un amore? :D

Attualmente sono riuscito a ricreare un debootstrap una Debian Lennyminimale con un kernel 2.6.30 ottimizzato per arm-kirkwood su una pennetta usb, e ad effettuare il boot da essa. Il boot loader è un po’ incasinato. Presto farò una guida completa e testata sul come mettere una bella Debian Lenny al posto di Ubuntu Jaunty (niente in contrario, ma dopo un upgrade ho dovuto resettare la password di root modificando i parametri di boot, dato che il login dava sempre errore).

Penso che ognuno dovrebbe adottare un piccolo kiwi (l’ho chiamato così, per una assonanza improbabile con l’architettura del processore… “kirky” sembrava più un mostriciattolo o un aspirapolvere) :D

frafra Gingilli ,

Test d’ingresso e lan party notturno

16 giugno 2009

tankb2…E così, il nostro intrepido studente Frafra, passò con successo il test di ammissione a ingegneria informatica del Politecnico di Milano :) E dato che attualmente è abbastanza libero (in fondo, ha “solo” la maturità), ha organizzato un lan party durato dalle ore 21.00 alle ore 6.30 del mattino successivo, nel quale lui e tre suoi amici, si sono ammazzati al mitico bzflag :D Frafra ottenne sempre il miglior punteggio personale, ma vinse solo una volta su tre, anche se una partità finì sul punteggio di 19-20. La serata è proseguita con intermezzi di Fifa ’09 e di Call of duty 4 in full hd. Risultato? Un successo :)

Appena finirò la maturità faremo un altra serata di questo tipo, ma con qualche persona in più, e metterò l’invito per partecipare anche su questo blog, nel caso qualcuno voglia giocare un po’ da casa sua :) Se invece volete organizzare qualcosa a casa vostra, vi elenco gli ingredienti che hanno reso il nostro lan party unico, fatto solo di sano divertimento ;)

Ecco la lista delle cose necessarie:

  1. Un numero di amici superiore a tre (preferibilmente dispari, in modo che le squadre risultino essere composte da un numero uguale di partecipanti) disposti a passare tutta la notte al pc
  2. Una quantità sufficiente di liquerizia (in bastoncini, pastiglie, cubetti), di caffé, e di stuzzichini
  3. Un numero sufficente di pc (rigorosamente con Linux, altrimenti il partecipante partirà con un punteggio di -5 a bzflag)
  4. Un router con uno switch (per l’occasione, mi sono dotato di uno switch ethernet 8 porte)
  5. Un numero di persone sane mentalmente (aka genitori) presenti in casa pari a zero (altrimenti portate del cloroformio)

Ecco la lista delle cose che NON sono necessarie:

  1. Una camera grande (la mia stanza è piccola, un partecipante era seduto perfino per terra col portatile sopra il mio letto)
  2. Un computer megagigaultra-veloce (ogni informatico sa che non è l’hardware che fa il monaco… aspetta, ma era così il detto? xD)
  3. Delle ragazze (è dimostrato scientificamente che il lag di un computer in una lan è direttamente proporzionale al numero di esseri femminili presenti nel raggio di 100m)

Detto questo… Divertitevi! Mentre io torno a lavorare sulla mia tesina “multimediale” programmata in Python/Qt4.5 e xhtml/css xD (poi vi mostrerò il codice sorgente e il risultato ottenuto, a lavoro ultimato)

frafra Frafra ,

48 ore di inferno: sbagliare il target di un dd

3 giugno 2009

Questo post vuole essere un tutorial per coloro che si ritrovano nella mia situazione, nella disperata ricerca di recuperare i propri dati da una partizione ext4 :)

21:15 Un paio di amici di Frafra arrivano a casa, si piazzano in camera, e iniziano a guerreggiare a bzflag. Frafra prende il suo Aspire One con moblin alpha2 e capisce che è meglio installare una distro qualsiasi per giocare col suo caro bzflag.
21:25 Frafra trova sul proprio hd esterno, l’immagine di archlinux che cercava. Ora bisogna trasferirla con un dd if=archlinux.img of=/dev/sdb (dove sdb è la pennetta usb).
21:26 Frafra si accorge che il led della pennetta non lampeggia e interrompe subito l’operazione con un bel ctrl+c.
21:27 Frafra impallidisce: la pennetta era /dev/sdc, e non sdb. sdb *era* il suo hd, con tutti i suoi dati, le foto di quando era piccolo, del suo 18esimo compleanno, con circa cinquantamila righe di codice sorgente python dei suoi programmi, qualche tonnellata di documenti, pdf, iso, musica… e il film di Ratman preso da youtube :D

Non vi sto a spiegare il seguito, ma vi mostrerò come ho risolto ;)

Risoluzione

Primo punto: non farsi prendere dal panico :)

Secondo punto: fare una copia dell’intero hd con:
dd if=/dev/sdb of=/home/frafra/hd.img
Attenzione: questa operazione dura mooolte ore con dischi grossi, e l’immagine creata sarà di dimensioni pari a quelle dell’hd (e non dello spazio occupato). Se non disponete di sufficente spazio, potete creare una immagine compressa, con:
dd if=/dev/sdb | gzip > /home/frafra/hd.img.gz
Se dovrete ripristinare il disco, basterà dare:
dd if=/home/frafra/hd.img of=/dev/sdb
…oppure (se avete creato una immagine compressa)…
gzip -dc /home/frafra/hd.img.gz | dd of=/dev/sdb

Punto 2 e mezzo (opzionale): recuperare i file cancellati con photorec
Vedere: www.cgsecurity.org/wiki/PhotoRec_Step_By_Step

Terzo punto: ripristinare la tabella delle partizioni originaria
Qui utilizzeremo testdisk. Da superutente (quindi dopo aver digitato su [invio] o sudo -c [invio]) lanceremo il comando:
testdisk # (chi se lo sarebbe mai aspettato? ;D)
Premiamo invio, selezioniamo il nostro disco rigido, selezioniamo Intel, premiamo su Analize, Quick Search, se abbiamo creato partizioni sotto vista premiamo Y (altrimenti N), premete nuovamente invio, selezionate Deeper Search, e aspettate. Ora, dovremo scegliere quale/i partizione/i far tornare alla luce. Nel mio caso era la seconda scelta (la partizione più grossa), allora l’ho selezionata, e con le frecce sinistra/destra, l’ho fatta diventare P (ovvero primaria). Per procedere: invio, y, Ok. Ora che abbiamo rimesso in sesto la tabella delle partizioni è consigliabile riavviare.
Maggiori informazioni su questa procedura su:
www.cgsecurity.org/wiki/TestDisk_Step_By_Step

Quarto punto: riparare il filesystem
Nel mio caso si trattava di un ext4. Se trattiamo di ext2/3/4, bisogna trovare un superblocco non danneggiato, che possiamo ottenere con Testdisk, andando nel menù principale, selezionando Advanced, selezionare la partizione e scegliere Superblock. Appuntiamoci il risultato :)
Ora lanciamo il nostro fsck (da superutente) in questa maniera:
fsck -vyf -t [fs] -b [blocco] -B [dimensione del blocco]
Al posto di “fs” mettiamo il nostro filesystem (nel mio caso ext4), al posto di “blocco” il numero del blocco, e al posto di “dimensione del blocco” la dimensione del blocco (di solito 1024 o 4096). Ora non ci resta che aspettare e sperare.
Maggiori informazioni su questo passaggio reperibili su:
www.cgsecurity.org/wiki/Advanced_Find_EXT2_EXT3_Backup_SuperBlock

Quinto punto: montare la partizione e recuperare i files
Dopo aver riavviato il pc, proviamo ad entrare nella partizione. Se tutto va bene dovremmo essere in grado di montarla, e di vedere il contenuto di lost+found (che però è visibile solo per il superutente). Se tutto è andato a buon fine, dovremmo avere svariate cartelle coi nostri tanto amati files :) Con un:
cd /media/partizione; mkdir /home/frafra/backup; cp \#*/ /home/frafra/backup
….(da root) dovremmo avere tutti i nostri dati nella home (ovviamente /media/partizione e /home/frafra possono variare come parametri).

Spero di esservi stato utile ;) Comunque nulla è meglio di un buon backup :)

frafra Disastri, Frafra , , ,

Non sei nero, sei solo sporco

19 aprile 2009

(disse papà Stallman al piccolo pinguino :D )

E così si scopre uno script della Free Software Fundation per “pulire” i sorgenti del kernel dai blob non liberi, rendendo il nostro Linux “100% libero” :)

Può tornare utile a puristi, a chi si dedica alla creazione di una distribuzione “approvata” dalla FSF, e agli amanti del pulito ;)

Lo script si trova su: fsfla.org/svn/fsfla/software/linux-libre/scripts/

frafra Script , ,

Tu chiamale se vuoi, tastiere

19 aprile 2009

Innanzitutto scusate per questo mio periodo di assenza, dovuto a impegni vari, e a una odiosa influenza :)

Dato che sto cambiando un po’ il “parco macchine” di camera mia, sono andato a fare quattro passi per comprare due oggetti molto semplici, spesso sottovalutati: un mouse e una tastiera.

Da buon Linuxiano, arrivo nel negozio con le idee chiare: tutta roba non Microsoft (70€ per una tastiera storta? Perché?), non wireless (tutti i giorni a ricaricare le batterie?), classico attacco usb (ps/2 ormai è un ricordo), il mouse non deve essere della dimensione di un biglietto del bus, e la tastiera non deve avere 3.000 bottoncini inutili.

Torno a casa, apro, provo i miei acquisti. Il mouse funziona alla perfezione, comodissimo. La tastiera si mostra elegante, compatta, ma… ha una dozzina di tasti sui quali si è deciso di riparmiare la plastica (almeno, è l’unico motivo “intelligente” che sono riuscito a darmi). Una tastiera, all’apparenza innocua, che, tutto ad un tratto, mostra la sua innata crudeltà: una barra spaziatrice lunga come il tappo della penna, un tasto invio che adesso vive con il complesso di inferiorità, uno shift destro per i quali i progettisti sembrano aver pensato: “massì, tanto rimane sempre quello di sinistra”. Alcuni tasti sono addirittura spostati: il tasto per digitare “<” e “>” (che solitamente è alla sinistra della “Z”), si trova, sulla sinistra della barra spaziatrice. Scrivere una pagina in xml all’inizio risulta essere frustrante. Il tasto usato per fare la “ù” è stato messo sopra (e non sulla sinistra) del tasto invio. Canc si trova alla destra del Ctrl destro. Nonostante tutto i progettisti hanno trovato il modo di inserire un inutile Fn, il tasto di Windows, il doppio ctrl, un tastino “wakeup” (che non è un tasto extra, è proprio nel blocco dei soliti tasti) e il tastino per il menù a tendina.

Le sorprese non sono finite qui: quando abilito block num ritrovo una spiacevole entità maligna, che avevo già incontrato in alcuni controlli di volume sul cavo di alcune casse audio: l’accecante led blu.

Mi sono chiesto: “perché? Perché mettono sti fastidiosissimi led blu?”. Forse Debian non è l’unica a riprendere dal Toystory della Pixar: a quanto pare la Trust ha voluto mettere il laser di Buzz lightyear nel suo prodotto.

Dopo questo sfogo, mi scuso con voi, e vi rassicuro dicendo che sto preparando altro codice da assaggiare ;)

frafra Frafra

Gioco della vita di Conway

23 marzo 2009

Ho implementato il gioco della vita di Conway in Python. Avete in mente il simbolo hacker (il glider, quella griglia 3×3 con dei pallini neri)? Beh, sono strettramente legati :)
Questa implementazione permette svariate cose, tra cui:

  1. Una volta impostata una griglia, vedere l’evolversi della situazione.
  2. Definite le dimensioni della griglia, trovare (con un bruteforce) tutte le figure che si ripetono dopo N stadi.

Ecco il codice (commentato parzialmente in inglese):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
try:
    import psyco
    from psyco.classes import __metaclass__
    psyco.full()
except:
    pass
 
class Life:
    """ Implementation of the Conway's game of life """
    def __init__(self, rows, columns):
        """ Inizializes 2d table """
        self.rows, self.columns = rows, columns
        self.board = [[0,]*self.columns for row in xrange(self.rows)]
    def get(self, row, column):
        """ Gets value or return 0 """
        check = 0 <= row < self.rows and 0 <= column < self.columns
        return (check and self.board[row][column] or 0)
    def add(self, row, column):
        """ Adds eight cells near current cell """
        return self.get(row - 1, column - 1) + \
            self.get(row - 1, column) + \
            self.get(row - 1, column + 1) + \
            self.get(row, column - 1) + \
            self.get(row, column + 1) + \
            self.get(row + 1, column - 1) + \
            self.get(row + 1, column) + \
            self.get(row + 1, column + 1)
    def next(self):
        """ Calculates the next stage """
        current = [row[:] for row in self.board] # It clones the table
        for r, row in enumerate(current): # For every row...
            for c, item in enumerate(row): # ...and for every column...
                near = self.add(r, c) # ...it calculates near cells
                if near not in (2, 3) and item: # Condition to death
                    current[r][c] = 0 # Let's kill it :(
                elif near == 3 and not item: # Condition to became alive
                    current[r][c] = 1 # Let's live! :D
        self.board = current # It overwrites the old stage
    def show(self, target = False):
        """ Shows the table """
        for row in (target or self.board):
            print("".join([column and "@" or "x" for column in row]))
 
def bruteforce(rows, columns):
    life = Life(rows, columns)
    possibilities = [list(i) for i in product((0, 1), repeat=columns)]
    for select in product(xrange(len(possibilities)), repeat=rows):
        life.board = [possibilities[i] for i in select]
        history = [life.board]
        while 1:
            life.next()
            if life.board in history:
                if life.board == history[0]:
                    life.show(history[0])
                    print("")
                break
            history.append(life.board)
 
if __name__ == "__main__":
    from itertools import product
    from sys import argv
    try:
        rows, columns = int(argv[1]), int(argv[2])
    except IndexError:
        print("Usage: %s [rows] [columns]" % argv[0])
    except ValueError:
        print("Usage: %s [rows] [columns]" % argv[0])
    else:
        bruteforce(rows, columns)
 
"""
# Example code: glider (spaceship, hacker symbol)
if __name__ == "__main__": # If you're executing this program...
    life = Life(6, 6) # It inizializes a 2d 6x6 table
    life.board[2][0] = 1 # 2, 0 set alive
    life.board[2][1] = 1 # 2, 1 set alive
    life.board[2][2] = 1 # 2, 2 set alive
    life.board[1][2] = 1 # 1, 2 set alive
    life.board[0][1] = 1 # 0, 1 set alive
    for time in range(12): # For n times...
        life.next() # ...it makes the next stage...
        life.show() # ...and it shows it :)
"""

La prima porzione di codice è la seguente:

4
5
6
7
8
9
try:
    import psyco
    from psyco.classes import __metaclass__
    psyco.full()
except:
    pass

In questa maniera, importiamo, se disponibile, la libreria psyco, che, su macchine x86, permette di eseguire operazioni molto onerose per la cpu, in maniera molto più veloce :)
Successivamente troviamo questa classe:

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Life:
    """ Implementation of the Conway's game of life """
    def __init__(self, rows, columns):
        """ Inizializes 2d table """
        self.rows, self.columns = rows, columns
        self.board = [[0,]*self.columns for row in xrange(self.rows)]
    def get(self, row, column):
        """ Gets value or return 0 """
        check = 0 <= row < self.rows and 0 <= column < self.columns
        return (check and self.board[row][column] or 0)
    def add(self, row, column):
        """ Adds eight cells near current cell """
        return self.get(row - 1, column - 1) + \
            self.get(row - 1, column) + \
            self.get(row - 1, column + 1) + \
            self.get(row, column - 1) + \
            self.get(row, column + 1) + \
            self.get(row + 1, column - 1) + \
            self.get(row + 1, column) + \
            self.get(row + 1, column + 1)
    def next(self):
        """ Calculates the next stage """
        current = [row[:] for row in self.board] # It clones the table
        for r, row in enumerate(current): # For every row...
            for c, item in enumerate(row): # ...and for every column...
                near = self.add(r, c) # ...it calculates near cells
                if near not in (2, 3) and item: # Condition to death
                    current[r][c] = 0 # Let's kill it :(
                elif near == 3 and not item: # Condition to became alive
                    current[r][c] = 1 # Let's live! :D
        self.board = current # It overwrites the old stage
    def show(self, target = False):
        """ Shows the table """
        for row in (target or self.board):
            print("".join([column and "@" or "x" for column in row]))

Questo codice è stato scritto con una particolare attenzione alle prestazioni. Cerchiamo di comprendere meglio le varie funzioni:

  1. __init__(self, rows, columns): dati due numeri interi, “disegna” una tabella delle dimensioni specificate
  2. get(self, row, column): è una funzione che viene usata per vedere se una cella è viva o morta. In caso questa cella non esista, ritorna semplicemente zero, come se fosse morta. Ho trovato questo metodo sensibilmente più veloce rispetto al fare un try/except (senza quindi controllare l’esistenza della cella in base alle coordinate), e molto più chiaro rispetto al creare una tabella con un bordo di una cella.
  3. add(self, row, column): date le coordinate di una cella, somma le otto celle accanto, utilizzando get().
  4. next(self): calcola lo stadio successivo.
  5. show(self, target=False): visualizza in maniera abbastanza “grezza” la nostra griglia sul terminale.

Qui finisce il “grosso” del codice. Successivamente abbiamo la funzione per il bruteforce:

47
48
49
50
51
52
53
54
55
56
57
58
59
60
def bruteforce(rows, columns):
    life = Life(rows, columns)
    possibilities = [list(i) for i in product((0, 1), repeat=columns)]
    for select in product(xrange(len(possibilities)), repeat=rows):
        life.board = [possibilities[i] for i in select]
        history = [life.board]
        while 1:
            life.next()
            if life.board in history:
                if life.board == history[0]:
                    life.show(history[0])
                    print("")
                break
            history.append(life.board)

Questa funzione fa uso di itertools per calcolare le combinazioni possibili. Bisogna prestare particolarmente attenzione a piccoli accorgimenti, come il calcolare volta per volta ogni possibile combinazione, semplicemente “scegliendo” tra le “righe” generate. Ho trovato questo metodo particolarmente efficente e veloce.
Per sapere se una figura è già apparsa, crea una lista degli stadi, finché qualcosa non si ripete. Questo deve sempre avvenire, in quanto anche se non capitassimo di fronte a una figura “particolare”, dopo qualche passaggio tutte le celle morirebbero, e le ritroveremmo nuovamente morte nello stadio successivo, provocando una ripetizione (che, in questo caso, non verrà però visualizzata).
Il blocco successivo serve a rendere operativo il bruteforce:

62
63
64
65
66
67
68
69
70
71
72
if __name__ == "__main__":
    from itertools import product
    from sys import argv
    try:
        rows, columns = int(argv[1]), int(argv[2])
    except IndexError:
        print("Usage: %s [rows] [columns]" % argv[0])
    except ValueError:
        print("Usage: %s [rows] [columns]" % argv[0])
    else:
        bruteforce(rows, columns)

Come già accennato precedentemente, importiamo itertools.product (assieme a sys.argv), e prendiamo gli argomenti passati da linea di comando, che diamo in pasto a bruteforce().
L’ultima parte è commentata, e mostra un utilizzo alternativo della classe Life:

74
75
76
77
78
79
80
81
82
83
84
85
86
"""
# Example code: glider (spaceship, hacker symbol)
if __name__ == "__main__": # If you're executing this program...
    life = Life(6, 6) # It inizializes a 2d 6x6 table
    life.board[2][0] = 1 # 2, 0 set alive
    life.board[2][1] = 1 # 2, 1 set alive
    life.board[2][2] = 1 # 2, 2 set alive
    life.board[1][2] = 1 # 1, 2 set alive
    life.board[0][1] = 1 # 0, 1 set alive
    for time in range(12): # For n times...
        life.next() # ...it makes the next stage...
        life.show() # ...and it shows it :)
"""

Si inizializza una griglia col famoso simbolo hacker, e si mostrano gli stadi successivi :)

Simpatico, no? :)

P.S. Ho omesso la licenza per problemi di spazio… Ovviamente è GPLv3 :)

frafra Python , ,

Rilasciato Wesnoth 1.6

23 marzo 2009

Ebbene si… Anche i programmatori ogni tanto amano svagarsi con qualche buon gioco di strategia :)
Proprio oggi è stata rilasciata la nuova stabile di Wesnoth, il famoso gioco di strategia a turni libero e multipiattaforma.

Link: www.wesnoth.org/start/1.6/
Changelog completo: svn.gna.org/viewcvs/*checkout*/wesnoth/tags/1.6/changelog

frafra News