27 июня 2000

                                         
      МЕТОД ПАРОЛИРОВАНИЯ      
        КОДОВОГО ФАЙЛА.        
                                         
(С) 2000 Макс/Compu-Studio Ltd           
-----------------------------------------
   Как  обычно я не претендую на гениаль-
ность  первооткрывателя  в кодинге - этот
метод  широко применяется на практике как
на  iBM, так и на Спектруме (см. мой жур-
нал "ЧВ#2"). Тем не менее мне этот способ
кажется самым простым и довольно надёжным
с точки зрения его реализации.           
   Для начала немного теории, хотя и пос-
ле практические упражнения делать придёт-
ся вам самим.  Итак,  у нас есть какой-то
кодовый  файл, который надо сделать недо-
ступным для посторонних. Что надо сделать
в первую очередь? Любой степени сложности
алгоритм ввода символов с клавиатуры. Вам
здесь развязываются руки в плане творчес-
ких решений ;)                           
   Next. Символы пароля надо разместить в
специальном буфере. Теперь, собственно, о
самом методе паролирования.  Схематически
это выглядит примерно так:               
                                         
файл:  |zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| 
пароль:|вася                           | 
       |    вася                       | 
       |        вася                   | 
       |            вася               | 
       |                вася           | 
       |                    вася       | 
       |                        вася   | 
       |                            вас| 
                                         
   Как  видите,  слово "вася" "шагает" по
кодовому  файлу  с шагом "на свою длину",
т.е. размер пароля в символах равен шагу,
нужному для дальнейшей кодировки. Чем ваш
пароль будет длиннее (в разумных пределах
конечно), тем больше шаг по файлу делать.
Ввод пароля не должен быть ограничен дли-
ной пароля, который вы придумали. Другими
словами - вам надо делать буфер для паро-
ля большим,  но это вовсе не должно озна-
чать,  что и сам пароль должен быть таких
же размеров.  Это обстоятельство приведёт
в  замешательство человека, который попы-
тается  взломать пароль. Также надо иметь
ввиду и тот факт, что простое кодирование
нулей приведёт к тому, что если пират до-
думается защищённый  файл просмотреть де-
буггером, то ваш "пароль" просто прочита-
ется в режиме дампа памяти. Не стоит так-
же делать сумму из чисел, составляющих КС
пароля, т.к. ксорка одним байтом тоже ма-
ло эффективна - всё те же нули...        
   Предлагаю самим напрячь мозги и приду-
мать какой-нибудь извращённый метод прев-
ращения  символов пароля в коды для пере-
кодировки. Можно сделать что-то типа NEG,
или же CPL, или просто парные ADD и потом
ксорить файл.                            
   Меры предосторожности при выборе файла
для паролирования. Если зараннее известно
о том, что файл закодирован, ну например,
программой HRUST с распаковщиком вначале,
то все труды по ксорке пойдут прахом, по-
тому что хакеру не составит труда восста-
новить  по имеющемуся коду распаковщика в
считаные минуты "перевёрнутые" биты и тем
самым узнать пароль. Это, конечно, если у
него будет на это время: друг или ваш ре-
бёнок захотели посмотреть защищённый диск
без вашего ведома...                     
                                         
   Ладно, это всё - фигня. Вывод из этого
всего водоворота следующий:              
 1) не делай буфер ввода равным длине па-
роля - незнание размера пароля затрудняет
его взлом.                               
 2) не  делай примитивных ксорок получен-
ным паролем.                             
 3) не  делай сверку пароля на правильное
значение методом сверки с шаблоном в ОЗУ.
 4) не ксорь стандартно компрессированные
файлы  или же никому не говори, что они у
тебя такие ;)                            
 5) дай мне немного денег за идею...     
                                         
   А  как  же проверить качество пароля и
определиться с дальнейшими действиями? Да
всё очень просто - или в самом файле дер-
жи  (в начале или конце),  или где-то от-
дельно контрольные суммы этого файла.  Не
рекомендую  делать КС одним байтом, иначе
может  просто совпасть эта самая КС с ка-
ким-нибудь паролем, набранным "от фонаря"
и  тогда  полная медитация... Лучше всего
надо  сделать минимум два подсчёта КС, но
с  разных мест файла. Например: первая КС
считается  с  начала  файла и до конца, а
вторая  КС с начала файла + 1 байт и тоже
до  конца.  Чем больше проверок КС, тем у
вас меньше шансов на роковое сложение бит
в ложную КС.                             
   Вот и всё. Пишите письма...           
                                         
                  -----                  
                                         
   Далее в этом разделе идёт сборник txt-
файлов по  кодингу.  Некоторые уже раннее
где-то печатались, что делает их морально
устаревшими. А мне пофиг - не читай, если
не нравится! Но рекомендую дочитать...   
                                         
      PUSH=евский эффект.      
                                         
(С) ACTIVATOR                            
-----------------------------------------
   Во  многих демухах, например U.S., ICE
CREAM,  а  также во многих boot-ах вы на-
верняка  встречали такой эффект, как "ле-
тание  сетки" по плавной траектории, т.е.
PUSH-евский эффект, как ещё его называют.
   Принцип его не сложен, как может пока-
заться  на первый взгляд. "фундаментом" в
нём  является всё та же команда PUSH, ко-
торая за один раз может копировать 2 бай-
та. В этом тексте я приведу примеры прог-
рамм,  которые  вам помогут сделать такой
эффект.                                  
   Сначала  мы должны построить табличку,
по  которой будет летать ваш спрайт. Раз-
мер возьмём 2*16 (X - 2, Y - 16). Таблич-
ка  строится  обыкновенной программкой на
бейсике:                                 
                                         
 10 LET adr1=30000:LET adr2=30500        
 20 FOR n=0 ТО 2*PI STEP PI/64           
 30 РОКЕ adr1,128+127*SIN n:РОКЕ adr2,8  
8+87*COS n                               
 40 PLOT РЕЕК adr1,РЕЕК adr2             
 50 LET adr1=adr1+1:LET adr2=adr2+1:NEXT 
n                                        
   В  результате работы этого алгоритма в
памяти под адресом 30000 будут координаты
X таблички, а под 30500 - соответственные
ей  координаты  Y. Более понятливые могут
поэкспериментировать с 30-ой строкой, ме-
няя  в  ней значения SIN и COS и добавляя
что-то своё.  Теперь  спрайт  у вас есть,
табличка  есть,  осталось самое главное -
коды. Перейдем к ним....                 
   Для  летания нам необходимо 256 спрай-
тов,  сдвинутых циклически таким образом:
сначала  сдигается  первоначальный спрайт
16 раз вправо, затем на одну точку ниже и
опять  16  раз.  Если  вы  всё  проделали
правильно,  то у вас должно получится 256
спрайтов общей длиной 8192 (длина спрайта
*  количество  = общая длина). Кто не хо-
чет, либо не может вручную сдигать этакую
армаду спрайтов, привожу программу, кото-
рая  все  сделает  за вас, т.е. правильно
сдвинет все спрайты:                     
                                         
        ORG  25000                       
        LD   HL,adres;вашего спрайта     
        LD   DE,bufer;размером 8192 для  
                     ;сдвинутого спрайта.
                     ;Допустим #С000.    
        LD   ВС,size ;длина спрайта 32 б.
        PUSH DE                          
        LDIR                             
        РОР  HL                          
        LD   С,16                        
DEC1    CALL CRUNCH2                     
        LD   В,16                        
DEC2    CALL CRUNCH1                     
        DJNZ DEC2                        
        DEC  С                           
        JR   NZ,DEC1                     
        RET                              
                                         
CRUNCH1 PUSH ВС                          
        LD   ВС,32                       
        LDIR                             
        PUSH HL                          
        LD   В,16                        
CR1     INC  HL                          
        LD   A,(HL)                      
        RRA                              
        DEC  HL                          
        RR   (HL)                        
        INC  HL                          
        RR   (HL)                        
        INC  HL                          
        DJNZ CR1                         
        РОР  HL                          
        РОР  ВС                          
        RET                              
CRUNCH2 PUSH ВС                          
        PUSH DE                          
        PUSH HL                          
        LD   ВС,#001Е                    
        ADD  HL,ВС                       
        LD   Е,(HL)                      
        INC  HL                          
        LD   D,(HL)                      
        PUSH DE                          
        LD   D,Н                         
        LD   Е,L                         
        DEC  HL                          
        DEC  HL                          
        LDDR                             
        ЕХ   DE,HL                       
        РОР  DE                          
        LD   (HL),D                      
        DEC  HL                          
        LD   (HL),Е                      
        РОР  HL                          
        РОР  DE                          
        РОР  ВС                          
        RET                              
                                         
   После выполнения этой программки у вас
под  адресом  49152 будут расположены эти
самые  сдвинутые спрайты. Теперь как выг-
лядит процедура, которая рассчитывает но-
мер спрайта и выводит его из буфера, куда
его предварительно копирует.             
                                         
        ORG  30000                       
START1  LD   HL,30000;адрес табл. по X   
        LD   A,(HL)                      
        AND  A                           
        JR   NZ,LET1                     
        LD   HL,30000                    
        LD   (START1+1),HL               
        JR   START1                      
LET1    LD   (START1+1),HL               
        AND  #OF                         
        LD   Н,0                         
        LD   L,A                         
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        PUSH HL                          
START2  LD   HL,30500;адрес табл. по Y   
        LD   A,(HL)                      
        AND  A                           
        JR   NZ,LET2                     
        LD   HL,30500                    
        LD   (START2+1),HL               
        JR   START2                      
LET2    LD   (START2+1),HL               
        AND  #0F                         
        LD   Н,0                         
        LD   L,A                         
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        ADD  HL,HL                       
        РОР  DE                          
        ADD  HL,DE                       
        LD   DE,#FFFF-32;адрес буфера од-
                     ;ного спрайта (32   
                     ;байта размером)    
        DUP  32      ;32 раза LDI        
        LDI                              
        EDUP         ;каманда ALASM`а!!! 
        LD   (STACK+1),SP;запомнить стек 
        LD   SP,#4000;на экран           
        LD   HL,(65503+0);адрес буфера +0
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4100                    
        LD   HL,(65503+2);адрес буфера +2
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4200                    
        LD   HL,(65503+4);адрес буфера +4
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4300                    
        LD   HL,(65503+6)                
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4400                    
        LD   HL,(65503+8)                
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4500                    
        LD   HL,(65503+10)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4600                    
        LD   HL,(65503+12)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4700                    
        LD   HL,(65503+14)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4020                    
        LD   HL,(65503+16)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4120                    
        LD   HL,(65503+18)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4220                    
        LD   HL,(65503+20)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4320                    
        LD   HL,(65503+22)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4420                    
        LD   HL,(65503+24)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4520                    
        LD   HL,(65503+26)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4620                    
        LD   HL,(65503+28)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        LD   SP,#4720                    
        LD   HL,(65503+30)               
        DUP  8                           
        PUSH HL                          
        EDUP                             
        ...        ;и так далее          
                                         
   Вы можете размножать эту процедуру та-
ким образом, чтобы адрес буфера был с пе-
риодом  от  0  до  30, после 30 опять 0 и
т.д. В самом конце вы должны сделать:    
                                         
STACK   LD   SP,0   ;восстановление стека
        RET                              
                                         
   Теперь, если вы это запустите в цикле,
то на экране будет летать весьма красивая
штучка, сотоящая из вашего первоначально-
го спрайта.                              
                                         
Макс: не рекомендую делать цикл. Понимаю,
что  размер алгоритма эффекта слишком ве-
лик получается и его можно сделать намно-
го короче, если сделать цикл и выборку из
таблицы  при помощи индексных регистровых
пар,  но все эти команды "тяжелы" с точки
зрения  процессора по количеству затрачи-
ваемых  тактов, поэтому будет жуткий тор-
моз  при  работе. Решать в конечном счёте
вам, но имейте это ввиду.                
                                         
                  -----                  
                                         
        Работа c диском        
  при включенных прерываниях   
                                         
(С) 1998 Иван Рощин                      
-----------------------------------------
    Теоретические сведения     
                                         
   В  компьютере "ZX-Spectrum" выполнение
команд обмена информацией между оператив-
ной памятью и диском происходит при непо-
средственном участии центрального процес-
сора Z80:                                
                                         
Как видим, если во время выполнения такой команды произойдет прерывание, Z80 отвлечется на его обработку и команда за- вершится с ошибкой "потеря данных". Таким образом, на работу с диском накладываются определенные ограничения.
Следует заметить, что в современных компьютерах используется так называемый прямой доступ к памяти, когда обмен ин- формацией между памятью и каким-либо внешним устройством происходит без учас- тия центрального процессора:
Спектрум - компьютер несовременный, и недостаток аппаратных средств, которые позволили бы "параллельно" работать с диском и делать что-то еще, приходится восполнять программной поддержкой. Для этого прежде всего нужно разобраться, как
осуществляется выполнение команд ВГ93 (например, "чтение сектора") при включен- ных прерываниях. -------------------- Примечание: разумеется, все сказанное о командах чтения относится и к командам записи. -------------------- Скорость обмена данными между ВГ93 и Z80 равна 250 Кбит/с, а диск вращается со скоростью 300 об/мин. Исходя из этих дан- ных, определим количество байт на одной дорожке (обратите внимание, что в расче- тах 1 Кбит = 1000 бит, а не 1024): (250*1000/8)*60/300 = 6250 байт Примерно такое же значение (плюс-минус пять байт) можно получить, воспользовав- шись для определения длины дорожки прог- раммой Afrodita 3.0. Теперь вычислим, сколько прерываний произойдет в течение одного оборота дис- ка, если их частота известна и равна 50 Гц: 50*60/300 = 10 Таким образом, на каждый оборот диска приходится ровно десять прерываний. Как уже было сказано, если прерывание прои- зойдет во время чтения сектора, то этот сектор не будет прочитан. Получается, что этот сектор нельзя будет прочитать и при следующем обороте диска, т.к. во время его чтения опять произойдет прерывание. И мы приходим к выводу, что чтение секторов при разрешенных прерываниях совершенно невозможно. -------------------- Примечание: все же есть способ осу- ществить чтение в таких условиях. Если не удалось прочитать сектор, надо рассинхро- низировать процесс вращения диска и мо- менты наступления прерываний. Сделать это можно очень просто: остановить двигатель дисковода, а затем снова запустить его. После этого остается только повторить чтение нужного сектора. -------------------- А как же, спросите вы, работают раз- личные demo, в которых чтение секторов совмещено с проигрыванием музыки по пре- рываниям? Оказывается, не все так плохо. Дело в том, что на самом деле такой стро- гой синхронизации нет. Так, на "Пентаго- не" при тактовой частоте 3,5 МГц промежу- ток между прерываниями составляет пример- но 71680 тактов. В этом случае частота прерываний равна: 3500000/71680 = 48,83 Гц -------------------- Примечание: соответственно, такой же будет и частота кадров у подключенного к "Пентагону" монитора или телевизора. Как видим, это немного отличается от стандар- та (50 Гц). Также это приводит к тому, что используемый во многих программах таймер, работающий на прерываниях, будет отставать на 1,4 секунды в минуту, в чем можно легко убедиться. -------------------- Теперь в течение одного оборота диска произойдет 48,83*60/300 = 9,77 прерыва- ний. Рассмотрим, сколько байт проходит под магнитной головкой в промежутке между двумя прерываниями: 6250/9,77 = 640 байт Если считать, что первое прерывание произошло в тот момент, когда магнитная головка находилась в начале дорожки, по- лучаем такую таблицу: +------------------+--------------------+ | порядковый номер | смещение от начала | | прерывания | дорожки, байт | +------------------+--------------------+ | 1 | 0 | | 2 | 640 | | 3 | 1280 | | 4 | 1920 | | 5 | 2560 | | 6 | 3200 | | 7 | 3840 | | 8 | 4480 | | 9 | 5120 | | 10 | 5760 | | 11 | 150 (след. оборот)| | 12 | 790 | | 13 | 1430 | | 14 | 2070 | | 15 | 2710 | | 16 | 3350 | | 17 | 3990 | | 18 | 4630 | | 19 | 5270 | | 20 | 5910 | | 21 | 300 (след. оборот)| | ... | ... | +------------------+--------------------+ Видно, что даже если сектор не прочи- тался на одном обороте дорожки, вполне возможно, что он будет прочитан на сле- дующем обороте, т.к. моменты наступления прерываний будут смещены на 150 байт. -------------------- Примечание: на моем "Пентагоне" в ре- зультате экспериментов было установлено, что из-за пониженной скорости вращения диска (299,4 об/мин вместо 300) моменты наступления прерываний смещаются не на 150, а на 138 байт. В дальнейших расчетах будет фигурировать именно эта величина. Для каждого компьютера она своя, и, как мы увидим, именно от нее зависит степень замедления чтения секторов. -------------------- Вычислим, чему будет равно время пол- ного чтения диска при запрещенных преры- ваниях: 0,2*160 = 32 секунды Теперь попробуем оценить, во сколько раз замедлится чтение одной дорожки стан- дартного TR-DOS диска при включенных пре- рываниях. -------------------- Примечание: будем считать, что при чтении дорожки программа сначала считы- вает все сектора подряд (при этом какие- то из них, очевидно, не будут правильно считаны из-за возникшего прерывания), а затем при каждом следующем обороте диска пытается прочитать все ранее неправильно считанные сектора, и так до тех пор, пока все сектора не будут прочитаны. Необходимо учесть, что если последова- тельно считываются несколько дорожек (на- пример, 4), и каждая дорожка считывается, например, за 2,5 оборота диска, то все 4 дорожки будут считаны не за 4*2,5 = 10, а за 4*3 = 12 оборотов диска. Это связано с тем, что когда мы отдаем команду пози- ционирования на другую дорожку, а затем отдаем команду чтения сектора, ВГ не сра- зу начнет читать сектор, а будет ожидать индексного импульса, указывающего на на- чало дорожки. -------------------- Очевидно, что время чтения одной до- рожки (измеряемое в оборотах диска) будет определяться временем чтения "наихудшего" сектора, т.е. такого сектора, на чтение которого будет затрачено наибольшее коли- чество попыток. Длина сектора на диске TR-DOS равна 256 байт. Рассмотрим на схеме, как может происходить чтение такого сектора в наихудшем случае:
Т.е. при первом обороте диска прерыва- ние произойдет при чтении байта со смеще- нием 0, при втором обороте момент наступ- ления прерывания сместится на 138 байт так, что сектор опять не будет считан, и, наконец, при третьем обороте он прочи- тается.
Итак, получили, что время чтения одной дорожки замедлится в 3 раза. Соответст- венно, весь диск прочитается за 32*3=96 секунд, что прекрасно согласуется с экс- периментальными данными. Теперь рассмотрим процесс чтения одной дорожки диска MS-DOS. Размер сектора - 512 байт. В этом случае чтение "наихудше- го" сектора может происходить по двум возможным сценариям: либо он прочитается на пятом обороте диска, либо на девятом. Это показано на схеме:
Практика показывает, что время чтения одной дорожки MS-DOS диска в среднем уве- личивается в 7,6 раза, то есть второй ва- риант реализуется немного чаще первого. Видно, что из-за большей длины сектора чтение замедляется еще сильнее, чем у диска TR-DOS. В этом TR-DOS получает хоть какое-то преимущество. А вот работать с диском IS-DOS при включенных прерываниях вообще нельзя. Ду- маю, вы уже догадались, почему - сектор в 1024 байта будет считываться так долго, что за это время обязательно произойдет прерывание, а может быть, и не одно. По той же причине при включенных прерываниях нельзя форматировать диск или выполнять команду чтения трека. Как же реализовано одновременное про- игрывание музыки и чтение диска (без вся- кого замедления!) в таких demo, как POWER UP и EYE ACHE-2? По-видимому, тут исполь- зуется такой способ: процедура проигрыва- ния музыки запускается после чтения оче- редного сектора. Но при этом вызов проце- дуры не синхронизируется с прерываниями, а ошибки чтения сопровождаются неприятны- ми завываниями. ----- ПРОГРАММИРОВАНИЕ КЭША. (С) 2000 Макс/Compu-Studio Ltd ----------------------------------------- Хотел я здесь рассказать о методах ра- боты с кэшем, но такая лень напала... Ко- роче, принцип известен, как добраться до кэша, а дальше пишите кто на что гаразд. Мне же только следует напомнить кое-что тем, кто делает программы для взлома или системные утилиты, которые находятся в кэше - делайте корректный выход из кэша, если порча основного ОЗУ может сказаться на программе, в нём размещённой. Суть проблемы такова: есть программа, которая по нажатию magic или reset-2 (это когда аппаратно "влетаешь" в кэш на адрес ноль) что-либо делает, а потом подразуме- вает возврат или переход в основную прог- рамму, но работа которой может быть нару- шена какими-либо изменениями в ОЗУ. А из- менения делаются кодерами то ли по незна- нию, то ли из-за лени... Короче, для "за- крытия" кэша надо сделать команду IN A,(#7В) и она должна находиться в ОЗУ, а не в кэ- ше. Оказывается, что это условие вовсе не обязательно! Достаточно просмотреть про- шивку ПЗУ в 128 и 48 бейсике и найти ко- манды РОР AF RET для реализации нормального выхода. У меня эти команды прошиты по следующим адресам: 48 BASIC - #0554 128 BASIC - #007D. В кэше надо за два байта до этих адре- сов подставить команду IN A,(#7В) и про- блема решится сама собой. Естественно на- до предварительно сделать PUSH AF или же подразумевать его снятие со стека. Выгля- дит это примерно так: PUSH AF ;адрес ещё кэша IN A,(#7В);после этой команды ;подключается ПЗУ. РОР AF ;а это уже ПЗУ! RET ;continue program И всё. Теперь любая программа, которая критична к содержанию ОЗУ (считает его КС или ещё что-нибудь, как в "Чёрном Вороне" Медноногова) и может дать сбой в работе, не заметит, что её кто-то тормозил. Можно писать свой дебаггер или искатель музы- кальных сонгов без риска вывалиться неиз- вестно куда при возврате... Но это ещё не всё! Сейчас много рас- плодилось всяких там операционок, коман- деров и прочей ерунды, которая рекоменду- ется для прошивки в ПЗУ. Обычно предлага- ют стереть 128-й бейсик и записать туда, ну например NEOS или Real Commander vXX. В этом случае придётся искать вышеуказан- ную последовательность байт (команд) са- мому и если повезло (нашёл), то переинс- таллироваться на другие адреса безболез- ненного выхода из кэша. Вот тут-то и ждёт вас самый что ни есть пиздец - нарушение стандарта чревато последствиями... :`( Примечание: это больше касается желез- ной темы, чем программистской, но скажу и здесь - если будет глючить с выходом, то надо обратить внимание на серии микросхем в листалке страниц ОЗУ, а также тех, ко- торые задействованы в кэше. Желательно, чтобы там была "шустрая" серия 1533. Это обезопасит от состояния, когда процессор уже отработал команду отключения кэша, а "железо" ещё не сделало подстановку ПЗУ и тем самым получается состояние, именуемое "немного беременная". Последствия, конеч- но же, не роды, но сброс гарантирован!



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
Technology sprites - Part 2: The hunt for sprites (search and pulling).
Wreck Time - Advertisements and announcements ...

В этот день...   21 November