flat assembler
Message board for the users of flat assembler.

Index > Windows > Accessing the console buffer on windows

Author
Thread Post new topic Reply to topic
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 11:58
Hello Community,

i am wondering how i can access the console buffer on windows?
Like reading and writing individual pixels in the console.
Also getting keystates and preventing them from directly being printed in the console as characters.
Is there an easy way to do that?
Basically dissable the default behavior of the console where it is more like textbased and use it as sort of like a canvas where i can draw individual pixels and check for keybord inputs.

Would be nice if someone could give me some help and/or code examples.

Thanks a lot Smile


Last edited by rc on 19 Dec 2020, 15:33; edited 2 times in total
Post 19 Dec 2020, 11:58
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 12:14
For which OS?
Post 19 Dec 2020, 12:14
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 12:17
For windows.
Sorry, forgot to mention. (Updated the thread)


Last edited by rc on 19 Dec 2020, 12:19; edited 1 time in total
Post 19 Dec 2020, 12:17
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 12:19
Moving to Windows forum.

Here is some old Windows code. I don't know if it works. Try it.
Code:
     format pe console
     entry start

     include 'win32ax.inc'
     
section '.code' code readable writeable executable 

start:   
     invoke GetStdHandle,STD_OUTPUT_HANDLE
     mov [stdout],eax
          
     mov [rect.Left],0
     mov [rect.Top],0
     mov [rect.Right],79
     mov [rect.Bottom],39
     mov [coord.x],80
     mov [coord.y],40
     mov [cinfo.dwSize],25
     mov [cinfo.bVisible],FALSE
     
     cinvoke system,<'cls'>
     cinvoke srand,<cinvoke time,NULL>
     invoke SetConsoleTitle,<'Random Pixels'>
     invoke SetConsoleWindowInfo,[stdout],1,rect
     invoke SetConsoleScreenBufferSize,[stdout],dword[coord]   
     invoke SetConsoleCursorInfo,[stdout],cinfo
     
     invoke FindWindow,NULL,<'Random Pixels'>  
     mov [hWnd],eax
     invoke GetDC,[hWnd]
     mov [hdc],eax
                                     
@@:   
     cinvoke rand
     shl eax,9
     push eax
     cinvoke rand
     pop ecx
     add eax,ecx
     mov esi,eax
     invoke SetPixel,[hdc],<stdcall rnd,640>,<stdcall rnd,480>,esi
     cinvoke _kbhit
     test eax,eax
     jz @b
            
     invoke ExitProcess,0  

             
proc rnd max   
     cinvoke rand 
     shl eax,17
     mul [max]
     mov eax,edx            
     ret
endp    


section '.data' data readable writeable

struc COORD {
     .x dw ?
     .y dw ?
    } 

struc SMALL_RECT {
     .Left dw ?
     .Top dw ?
     .Right dw ?
     .Bottom dw ?
    } 

struc LPCONSOLE_CURSOR_INFO {
     .dwSize dd ?
     .bVisible db ?
    }      
     
     cinfo LPCONSOLE_CURSOR_INFO
     rect SMALL_RECT
     coord COORD     
          
     stdout dd ?
     hWnd dd ?
     hdc dd ?

     
section '.idata' import data readable writeable

     library kernel32,'kernel32.dll',\
        msvcrt,'msvcrt.dll',\
        user32,'user32.dll',\
        gdi32,'gdi32.dll'
         
     import gdi32,\
        SetPixel,'SetPixel'
                  
     import user32,\
        GetDC,'GetDC',\
        FindWindow,'FindWindowA'
             
     import kernel32,\     
        ExitProcess,'ExitProcess',\
        GetStdHandle,'GetStdHandle',\
        SetConsoleTitle,'SetConsoleTitleA',\
        SetConsoleCursorInfo,'SetConsoleCursorInfo',\
        SetConsoleWindowInfo,'SetConsoleWindowInfo',\
        SetConsoleCursorPosition,'SetConsoleCursorPosition',\
        SetConsoleScreenBufferSize,'SetConsoleScreenBufferSize'   
            
     import msvcrt,\
        _kbhit,'_kbhit',\
        system,'system',\      
        srand,'srand',\
        rand,'rand',\
        time,'time'     
Post 19 Dec 2020, 12:19
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 12:32
Thanks for the quick reply.
This code compiles, however it has strange behavior. It draws random pixels on the desktop itself not in the console Razz
Post 19 Dec 2020, 12:32
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 12:36
Then I think the FindWindow call is returning zero.

Basically once you get the DC for a window then you can draw anything to it. In that code it just draws random pixels, but it could also be text or lines etc.
Post 19 Dec 2020, 12:36
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 13:16
Yes it returns zero, but dont know why.
I added a comparisson:
Code:
     invoke SetConsoleScreenBufferSize,[stdout],dword[coord]   
     invoke SetConsoleCursorInfo,[stdout],cinfo
     
     invoke FindWindow,NULL,<'Random Pixels'>
     cmp eax, 0 ; <---
     je exit ; <---
     mov [hWnd],eax
     invoke GetDC,[hWnd]
     mov [hdc],eax
                                     
@@:   
     cinvoke rand
     shl eax,9
     push eax
     cinvoke rand
     pop ecx
     add eax,ecx
     mov esi,eax
     invoke SetPixel,[hdc],<stdcall rnd,640>,<stdcall rnd,480>,esi
     cinvoke _kbhit
     test eax,eax
     jz @b
            
exit:
     invoke ExitProcess,0      
Post 19 Dec 2020, 13:16
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 13:22
Calling GetLastError might show the reason.
Post 19 Dec 2020, 13:22
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 13:58
Error is ERROR_INVALID_FUNCTION

Changed it to get the last error like this: (Sorry i am still a newb when it comes to assembly)
Code:
     format pe console
     entry start

     include 'win32ax.inc'
     
section '.code' code readable writeable executable 

start:   
     invoke GetStdHandle,STD_OUTPUT_HANDLE
     mov [stdout],eax
          
     mov [rect.Left],0
     mov [rect.Top],0
     mov [rect.Right],79
     mov [rect.Bottom],39
     mov [coord.x],80
     mov [coord.y],40
     mov [cinfo.dwSize],25
     mov [cinfo.bVisible],FALSE
     
     cinvoke system,<'cls'>
     cinvoke srand,<cinvoke time,NULL>
     invoke SetConsoleTitle,<'Random Pixels'>
     invoke SetConsoleWindowInfo,[stdout],1,rect
     invoke SetConsoleScreenBufferSize,[stdout],dword[coord]   
     invoke SetConsoleCursorInfo,[stdout],cinfo
     
     invoke FindWindow,NULL,<'Random Pixels'>
     cmp eax, 0
     je error
     mov [hWnd],eax
     invoke GetDC,[hWnd]
     mov [hdc],eax
                                     
@@:   
     cinvoke rand
     shl eax,9
     push eax
     cinvoke rand
     pop ecx
     add eax,ecx
     mov esi,eax
     invoke SetPixel,[hdc],<stdcall rnd,640>,<stdcall rnd,480>,esi
     cinvoke _kbhit
     test eax,eax
     jz @b
            
error:
     invoke  GetLastError
     push    eax
     push    eax
     lea     eax,[lpBuffer]
     invoke  FormatMessage,FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,NULL,1,LANG_NEUTRAL,eax,0,esp
     pop     eax
     pop     eax
     invoke  MessageBox,NULL,[lpBuffer],NULL,MB_ICONERROR+MB_OK
     invoke  LocalFree,[lpBuffer]
exit:
     invoke ExitProcess,0  

             
proc rnd max   
     cinvoke rand 
     shl eax,17
     mul [max]
     mov eax,edx            
     ret
endp    


section '.data' data readable writeable

lpBuffer dd ?

struc COORD {
     .x dw ?
     .y dw ?
    } 

struc SMALL_RECT {
     .Left dw ?
     .Top dw ?
     .Right dw ?
     .Bottom dw ?
    } 

struc LPCONSOLE_CURSOR_INFO {
     .dwSize dd ?
     .bVisible db ?
    }      
     
     cinfo LPCONSOLE_CURSOR_INFO
     rect SMALL_RECT
     coord COORD     
          
     stdout dd ?
     hWnd dd ?
     hdc dd ?

     
section '.idata' import data readable writeable

     library kernel32,'kernel32.dll',\
        msvcrt,'msvcrt.dll',\
        user32,'user32.dll',\
        gdi32,'gdi32.dll'
         
     import gdi32,\
        SetPixel,'SetPixel'
                  
     import user32,\
        GetDC,'GetDC',\
        FindWindow,'FindWindowA',\
        MessageBox,'MessageBoxA'
             
     import kernel32,\     
        ExitProcess,'ExitProcess',\
        GetLastError,'GetLastError',\
        FormatMessage,'FormatMessageA',\
        LocalFree,'LocalFree',\
        GetStdHandle,'GetStdHandle',\
        SetConsoleTitle,'SetConsoleTitleA',\
        SetConsoleCursorInfo,'SetConsoleCursorInfo',\
        SetConsoleWindowInfo,'SetConsoleWindowInfo',\
        SetConsoleCursorPosition,'SetConsoleCursorPosition',\
        SetConsoleScreenBufferSize,'SetConsoleScreenBufferSize'   
            
     import msvcrt,\
        _kbhit,'_kbhit',\
        system,'system',\      
        srand,'srand',\
        rand,'rand',\
        time,'time'    
Post 19 Dec 2020, 13:58
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 14:15
Does the title of the console change to "Random Pixels"?

You can also change the find string to the title of the console, instead of changing the title and then trying to find it.
Post 19 Dec 2020, 14:15
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 14:39
The strange thing is, a console window opens for a split of a second, closes and then a graphics window opens with the title "Random Pixels" and the pixels getting drawn somewhere else on the desktop. So the opened window is neither a console window nor powershell window.. its something else...
Post 19 Dec 2020, 14:39
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 14:45
If you delete the 'cls' call, and the three SetConsole*,[stdout],... calls, then the window will be used as-is, without changing the size.

The reason for drawing in the wrong place is the hWnd is zero (the desktop).
Post 19 Dec 2020, 14:45
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 15:19
Ok yes, it looks like changing the title and immediatly searching for a window with this title doesn't work. So searching for it multiple times works.


Code:
     format pe console
     entry start

     include 'win32ax.inc'
     
section '.code' code readable writeable executable 

start:   
     invoke GetStdHandle,STD_OUTPUT_HANDLE
     mov [stdout],eax
          
     mov [rect.Left],0
     mov [rect.Top],0
     mov [rect.Right],79
     mov [rect.Bottom],39
     mov [coord.x],80
     mov [coord.y],40
     mov [cinfo.dwSize],25
     mov [cinfo.bVisible],FALSE
     
     ;cinvoke system,<'cls'>
     cinvoke srand,<cinvoke time,NULL>
     invoke SetConsoleTitle,<'Test_'>
     ;invoke SetConsoleWindowInfo,[stdout],1,rect
     ;invoke SetConsoleScreenBufferSize,[stdout],dword[coord]   
     ;invoke SetConsoleCursorInfo,[stdout],cinfo

     mov ecx, 10
@@:
     dec ecx
     cmp ecx, 0
     je error
     invoke FindWindow,NULL,<'Test_'>
     cmp eax, 0
     je @b
     mov [hWnd],eax
     invoke GetDC,[hWnd]
     mov [hdc],eax
                                     
@@:   
     cinvoke rand
     shl eax,9
     push eax
     cinvoke rand
     pop ecx
     add eax,ecx
     mov esi,eax
     invoke SetPixel,[hdc],<stdcall rnd,640>,<stdcall rnd,480>,esi
     cinvoke _kbhit
     test eax,eax
     jz @b
     jmp exit  
            
error:
     invoke  GetLastError
     push    eax
     push    eax
     lea     eax,[lpBuffer]
     invoke  FormatMessage,FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,NULL,1,LANG_NEUTRAL,eax,0,esp
     pop     eax
     pop     eax
     invoke  MessageBox,NULL,[lpBuffer],NULL,MB_ICONERROR+MB_OK
     invoke  LocalFree,[lpBuffer]
exit:
     invoke ExitProcess,0  

             
proc rnd max   
     cinvoke rand 
     shl eax,17
     mul [max]
     mov eax,edx            
     ret
endp    


section '.data' data readable writeable

lpBuffer dd ?

struc COORD {
     .x dw ?
     .y dw ?
    } 

struc SMALL_RECT {
     .Left dw ?
     .Top dw ?
     .Right dw ?
     .Bottom dw ?
    } 

struc LPCONSOLE_CURSOR_INFO {
     .dwSize dd ?
     .bVisible db ?
    }      
     
     cinfo LPCONSOLE_CURSOR_INFO
     rect SMALL_RECT
     coord COORD     
          
     stdout dd ?
     hWnd dd ?
     hdc dd ?

     
section '.idata' import data readable writeable

     library kernel32,'kernel32.dll',\
        msvcrt,'msvcrt.dll',\
        user32,'user32.dll',\
        gdi32,'gdi32.dll'
         
     import gdi32,\
        SetPixel,'SetPixel'
                  
     import user32,\
        GetDC,'GetDC',\
        FindWindow,'FindWindowA',\
        MessageBox,'MessageBoxA'
             
     import kernel32,\     
        ExitProcess,'ExitProcess',\
        GetLastError,'GetLastError',\
        FormatMessage,'FormatMessageA',\
        LocalFree,'LocalFree',\
        GetStdHandle,'GetStdHandle',\
        SetConsoleTitle,'SetConsoleTitleA',\
        SetConsoleCursorInfo,'SetConsoleCursorInfo',\
        SetConsoleWindowInfo,'SetConsoleWindowInfo',\
        SetConsoleCursorPosition,'SetConsoleCursorPosition',\
        SetConsoleScreenBufferSize,'SetConsoleScreenBufferSize'   
            
     import msvcrt,\
        _kbhit,'_kbhit',\
        system,'system',\      
        srand,'srand',\
        rand,'rand',\
        time,'time'    
Post 19 Dec 2020, 15:19
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 15:40
Perhaps a short Sleep might do the trick also.
Code:
invoke Sleep,10    
Post 19 Dec 2020, 15:40
View user's profile Send private message Visit poster's website Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 16:15
revolution wrote:
Perhaps a short Sleep might do the trick also.
Code:
invoke Sleep,10    

Ok this does the trick, and looks even clearer.

I need to pack this functionality into a dll so i can use it in c.

Basically these few functions:

- createConsoleWindow: title, x, y, width, height
- setDrawColor: r, g, b (hex would also be ok)
- drawPixel: x, y (will use the drawcolor)
- clearConsole (will use the drawcolor)
- getPressedKeyboardKey
- getMouseX / getMouseY (gets mouse coordinates within the console window)
- getPressedMouseKey

Is this possible to do?
Post 19 Dec 2020, 16:15
View user's profile Send private message Reply with quote
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 19 Dec 2020, 17:34
Can someone tell me why this is throwing an error?
Dont know what i am suppose to use: edx, dx, dl, dh?

I commented the line that is causing the error with:
Code:
; <--- error: operand sizes do not match.    


Code:
; #############################################
;               HEADER
; #############################################
format pe console
entry start

; #############################################
;               INCLUDES
; #############################################
include 'c:/fasm/include/win32ax.inc'

; #############################################
;               CODE
; #############################################
section '.code' code readable writeable executable 

start:
        invoke GetStdHandle,STD_OUTPUT_HANDLE
        mov [stdout],eax

        cinvoke srand, <cinvoke time, NULL>

        invoke initConsole, <'test_'>, 0, 0, 80, 40
        cmp eax, 0
        je error
        mov [hConsoleWnd],eax
        invoke GetDC,[hConsoleWnd]
        mov [hConsoleDc],eax
                                                                         
@@:   
        cinvoke rand
        shl eax,9
        push eax
        cinvoke rand
        pop ecx
        add eax,ecx
        mov esi,eax
        invoke SetPixel,[hConsoleDc],<stdcall rnd,640>,<stdcall rnd,480>,esi
        cinvoke _kbhit
        test eax,eax
        jz @b
        jmp exit
                        
error:
        invoke  GetLastError
        push    eax
        push    eax
        lea     eax,[lpErrBuffer]
        invoke  FormatMessage,FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,NULL,1,LANG_NEUTRAL,eax,0,esp
        pop     eax
        pop     eax
        invoke  MessageBox,NULL,[lpErrBuffer],NULL,MB_ICONERROR+MB_OK
        invoke  LocalFree,[lpErrBuffer]
exit:
        invoke ExitProcess,0

; #############################################
;               PROCS
; #############################################

proc initConsole title:byte, x:dword, y:dword, w:dword, h:dword
        mov edx, [x]
        mov [rect.Left], edx
        mov edx, [y]
        mov [rect.Top], edx
        mov edx, [w]
        mov [coord.x], edx
        dec edx
        mov [rect.Right], edx
        mov edx, [h]
        mov [coord.y], edx
        dec edx
        mov [rect.Bottom], edx
        mov [cursorinfo.dwSize], 25
        mov [cursorinfo.bVisible], FALSE

        cinvoke system, <'cls'>
        mov edx, [title] ; <--- error: operand sizes do not match. dx, dl, dh all do not work when i want to set the console title
        invoke SetConsoleTitle, edx
        invoke SetConsoleWindowInfo, [stdout], 1, rect
        invoke SetConsoleScreenBufferSize, [stdout], dword[coord]
        invoke SetConsoleCursorInfo, [stdout], cursorinfo

        invoke Sleep, 100
        invoke FindWindow, NULL, title
        ret
endp

proc rnd max   
        cinvoke rand 
        shl eax,17
        mul [max]
        mov eax,edx            
        ret
endp

; #############################################
;               DATA
; #############################################
section '.data' data readable writeable

        ; Error messages are stored here
        lpErrBuffer dd ?
        ; Stdout
        stdout dd ?
        ; Handle to console window
        hConsoleWnd dd ?
        ; Device context of console (used to perfom gdi functions)
        hConsoleDc dd ?

        struc VEC2D {
                .x dw ?
                .y dw ?
        } 

        struc SMALL_RECT {
                .Left dw ?
                .Top dw ?
                .Right dw ?
                .Bottom dw ?
        } 

        struc LPCONSOLE_CURSOR_INFO {
                .dwSize dd ?
                .bVisible db ?
        }

        cursorinfo LPCONSOLE_CURSOR_INFO
        rect SMALL_RECT
        coord VEC2D 


; #############################################
;               IMPORTS
; #############################################
section '.idata' import data readable writeable

        library kernel32,'kernel32.dll',\
        msvcrt,'msvcrt.dll',\
        user32,'user32.dll',\
        gdi32,'gdi32.dll'
                 
        import gdi32,\
                SetPixel,'SetPixel'
                                  
        import user32,\
                GetDC,'GetDC',\
                FindWindow,'FindWindowA',\
                MessageBox,'MessageBoxA'
                         
        import kernel32,\
                Sleep,'Sleep',\
                ExitProcess,'ExitProcess',\
                GetLastError,'GetLastError',\
                FormatMessage,'FormatMessageA',\
                LocalFree,'LocalFree',\
                GetStdHandle,'GetStdHandle',\
                SetConsoleTitle,'SetConsoleTitleA',\
                SetConsoleCursorInfo,'SetConsoleCursorInfo',\
                SetConsoleWindowInfo,'SetConsoleWindowInfo',\
                SetConsoleCursorPosition,'SetConsoleCursorPosition',\
                SetConsoleScreenBufferSize,'SetConsoleScreenBufferSize'   
                        
        import msvcrt,\
                _kbhit,'_kbhit',\
                system,'system',\      
                srand,'srand',\
                rand,'rand',\
                time,'time'    
Post 19 Dec 2020, 17:34
View user's profile Send private message Reply with quote
Ali.Z



Joined: 08 Jan 2018
Posts: 712
Ali.Z 19 Dec 2020, 21:01
just use movzx.

_________________
Asm For Wise Humans
Post 19 Dec 2020, 21:01
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20292
Location: In your JS exploiting you and your system
revolution 19 Dec 2020, 22:46
You have defined title as a byte; Which means your title can only be a single byte long. But you also have to zero terminate, so in fact now your title can only be zero bytes long with the zero terminator.

I think what you wanted was a pointer to a byte buffer. So the title parameter is a dword pointer, and you have the title stored somewhere else. Then you would change the FindWindow call to be [title] instead of the plain title.
Post 19 Dec 2020, 22:46
View user's profile Send private message Visit poster's website Reply with quote
Picnic



Joined: 05 May 2007
Posts: 1389
Location: Piraeus, Greece
Picnic 21 Dec 2020, 10:16
Hello,

rc, instead of FindWindow try GetConsoleWindow. See if that works.
Code:

    invoke GetConsoleWindow
    mov [hWnd], eax
    

rc wrote:

Basically these few functions:

- createConsoleWindow: title, x, y, width, height
- setDrawColor: r, g, b (hex would also be ok)
- drawPixel: x, y (will use the drawcolor)
- clearConsole (will use the drawcolor)
- getPressedKeyboardKey
- getMouseX / getMouseY (gets mouse coordinates within the console window)
- getPressedMouseKey

Is this possible to do?


Yes i think it is possible. Take a look at Hobby Basic. It has rgb colors, mouse and keyboard support, resizable window, etc.

You have to read more about microsoft's console functions.

Here is a CLS routine to get you started.
Post 21 Dec 2020, 10:16
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.