Hello.
commit
fd9eed9706
@ -0,0 +1,268 @@
|
|||||||
|
;GETCD V 1.0 | 3-NOV-2024 Jay Moore/dewdude@pickmy.org
|
||||||
|
;Finds CDROM drive letter and places in CDROM
|
||||||
|
;environment variable
|
||||||
|
;
|
||||||
|
;A replacement for FINDCD.EXE written in NASM Assembly and
|
||||||
|
;manipulates a lot of memory directly. Not a drop-in
|
||||||
|
;replacement, but close. Designed as a .BAT utility, the
|
||||||
|
;only user interaction is feeding it a path/filename and
|
||||||
|
;checking the environment variable. Default returns first
|
||||||
|
;CDROM drive that has a disc.
|
||||||
|
;
|
||||||
|
;GETCD [/?] SEARCHSTRING
|
||||||
|
;
|
||||||
|
;SEARCHSTRING can be a file, directory name, path, or wildcard.
|
||||||
|
;Not specifying a file does a wildcard search on root of CD.
|
||||||
|
;You do not need to add a leading \ to the string.
|
||||||
|
;/? displays the usual help, except poorly written.
|
||||||
|
;
|
||||||
|
;CDROM=E:
|
||||||
|
;
|
||||||
|
;Is how the variable will be set. If you desire a trailing \;
|
||||||
|
;just have your script 'set CDROM=%CDROM%\'
|
||||||
|
;
|
||||||
|
;This will absolutely overwrite any existing CDROM= contents with
|
||||||
|
;the above format; and will also shift data around to make it the
|
||||||
|
;exact length. It will also create an environment variable if none
|
||||||
|
;exists. This is contrast with FINDCD.EXE; which not only needs
|
||||||
|
;you to set the CDROM= variable first, but requires patching the
|
||||||
|
;.exe to change the file search. Gross.
|
||||||
|
;
|
||||||
|
;Displayed output is minimal. The only error displayed is if the
|
||||||
|
;environment block is full. A lack of CDROM drives will exit with
|
||||||
|
;errorlevel 255. Anything else just doesn't change (or create) the
|
||||||
|
;CDROM variable. You'll need to do that after execution in your script.
|
||||||
|
;
|
||||||
|
;argument section of PSP starts at 80h; byte count, bytes, terminator.
|
||||||
|
;term is 0x0D, but it's not included in byte count. this can include
|
||||||
|
;spaces if full name.ext is used or user just hit space.
|
||||||
|
;the pointer to parent's PSP lives at 16h; env-block pointer is 2Ch
|
||||||
|
;offset 3 one segment back is the block allocation size.
|
||||||
|
;this means I initialize di to 10h now and just leave es where it is
|
||||||
|
;as all counts are relative from di's initialization
|
||||||
|
|
||||||
|
[CPU 8086]
|
||||||
|
[BITS 16]
|
||||||
|
org 100h
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global start
|
||||||
|
|
||||||
|
start:
|
||||||
|
xor cx, cx ; clear out cx
|
||||||
|
mov si, 80h ; move si to psp argument count
|
||||||
|
mov cl, [si] ; load argument byte count
|
||||||
|
|
||||||
|
argproc:
|
||||||
|
jcxz varinit ; stop if cx is 0
|
||||||
|
inc si ; increment si
|
||||||
|
cmp byte [si], 20h ; Invalid char/space check
|
||||||
|
jbe skipit ; jump to loop if <20h
|
||||||
|
cmp byte [si], 5ch ; is it backslash
|
||||||
|
jz skipit ; jump if it is
|
||||||
|
cmp word [si], 3f2fh ; check for /?
|
||||||
|
jz hllp ; jump if it is
|
||||||
|
jmp ldfile ; land here when done
|
||||||
|
skipit:
|
||||||
|
loop argproc ; dec cx, jmp argproc ;)
|
||||||
|
|
||||||
|
ldfile:
|
||||||
|
lea di, filename ; load filename to di
|
||||||
|
repe movsb ; copy argument to filename
|
||||||
|
mov byte [di], 0 ; null for good measure
|
||||||
|
|
||||||
|
varinit:
|
||||||
|
mov es, [16h] ; parent psp pointer
|
||||||
|
mov ax, [es:2ch] ; load block segment
|
||||||
|
dec ax ; segment one below
|
||||||
|
mov es, ax ; go back a segment
|
||||||
|
mov ax, [es:3h] ; this is the size
|
||||||
|
mov cl, 4 ; load 4 to cl for shl
|
||||||
|
shl ax, cl ; bit shift left 4
|
||||||
|
mov [blocksize], ax ; store
|
||||||
|
mov di, 10h ; move di up to env blk
|
||||||
|
|
||||||
|
readblock:
|
||||||
|
cmp word [es:di], 0 ; end of block?
|
||||||
|
jz endofblock ; variiable missing
|
||||||
|
lea si, envname ; load envname address
|
||||||
|
mov cx, 6 ; load six
|
||||||
|
repe cmpsb ; repe compare string
|
||||||
|
jnz readblock ; if not variable, go back up
|
||||||
|
sub di, 6 ; subtract 6
|
||||||
|
mov [envstart], di ; write starting location
|
||||||
|
add di, 6 ; place it back
|
||||||
|
|
||||||
|
findend:
|
||||||
|
inc di ; now to find the end
|
||||||
|
cmp word [es:di], 0 ; is it the end?
|
||||||
|
jnz findend ; jump back up if not
|
||||||
|
|
||||||
|
endofblock:
|
||||||
|
inc di ; actual end of block
|
||||||
|
mov [blockend], di ; write that down
|
||||||
|
cmp word [envstart], 0 ; did we find a var
|
||||||
|
jz noenv ; jump if novar
|
||||||
|
mov di, [envstart] ; go back to the env start
|
||||||
|
mov ax, 1212h ; get the asciz length
|
||||||
|
int 2fh ; in to cx
|
||||||
|
cmp cx, 9 ; and see if it's 9
|
||||||
|
jb envtoosmall ; jump to envtosmall if too small
|
||||||
|
ja envtoobig ; jump to envtoobig if too big
|
||||||
|
|
||||||
|
envokay:
|
||||||
|
add di, 6 ; drive letter is six in
|
||||||
|
jmp drivego ; es:di ready for letter
|
||||||
|
|
||||||
|
envtoobig:
|
||||||
|
mov si, di ; duplicate pointers
|
||||||
|
mov word [es:di+7], 0x003A ; write : and null
|
||||||
|
add si, 9 ; put si where i need di
|
||||||
|
call endcheck ; check relative position
|
||||||
|
call bytesize ; get byte count to copy
|
||||||
|
xchg di, si ; now we swap
|
||||||
|
cld ; clear that direction
|
||||||
|
call copybytes ; copy byte routine
|
||||||
|
mov word [es:di], 0 ; double null new end
|
||||||
|
mov di, [envstart] ; go back to the env
|
||||||
|
jmp envokay ; might as well jump
|
||||||
|
|
||||||
|
noenv:
|
||||||
|
call envfree ; check free space
|
||||||
|
mov di, [blockend] ; go to block end
|
||||||
|
|
||||||
|
|
||||||
|
newenv:
|
||||||
|
lea si, envname ; load address of envname
|
||||||
|
mov cx, 8 ; we want 8 bytes
|
||||||
|
repe movsb ; write 'em
|
||||||
|
mov word [es:di], 0000h ; double null new term
|
||||||
|
sub di, 2 ; back di up two
|
||||||
|
jmp drivego ; es:di is ready
|
||||||
|
|
||||||
|
envtoosmall:
|
||||||
|
mov byte [oneornine], 01h ; change envfree's cmp value
|
||||||
|
call envfree ; check environment space
|
||||||
|
call endcheck ; check relative position
|
||||||
|
call bytesize ; call for byte count
|
||||||
|
add cx, 3 ; add three to that count
|
||||||
|
mov si, [blockend] ; load the end of block offset to si
|
||||||
|
mov di, [blockend] ; load it again to di
|
||||||
|
inc di ; move it up one
|
||||||
|
std ; set direction flag
|
||||||
|
call copybytes ; copybytes routine
|
||||||
|
mov word [es:di+1], 0x003A ; write the : and null one byte up
|
||||||
|
|
||||||
|
drivego:
|
||||||
|
mov ax, 2524h ; Ignore Critical Errors
|
||||||
|
lea dx, [new24] ; pointer to new handler
|
||||||
|
int 21h ; interrupt to change ivt
|
||||||
|
mov ax, 1500h ; function to get drive info
|
||||||
|
int 2Fh ; from int 2f
|
||||||
|
xchg bx, cx ; swap count and starting number
|
||||||
|
jcxz nodrives ; see if we have drives
|
||||||
|
add bl, 41h ; convert number to letter
|
||||||
|
|
||||||
|
loadltr:
|
||||||
|
push cx ; push drive count to stack
|
||||||
|
mov [drivevar], bl ; copy drive letter to ram
|
||||||
|
lea dx, drivevar ; load address of drivevar
|
||||||
|
mov ah, 4Eh ; load find first file
|
||||||
|
mov cl, 17h ; all the options
|
||||||
|
int 21h ; call the interrupt
|
||||||
|
jnc envset ; found file, go on
|
||||||
|
pop cx ; pop drive count back in to CX
|
||||||
|
inc bl ; increment to next drive
|
||||||
|
loop loadltr ; loop back around
|
||||||
|
jmp exit ; no match, leave
|
||||||
|
|
||||||
|
envset:
|
||||||
|
lea si, drivevar ; loads address to si
|
||||||
|
movsb ; moves ds:si to es:di
|
||||||
|
jmp exit ; we're done, go home
|
||||||
|
|
||||||
|
nodrives:
|
||||||
|
mov al, 0FFh ; load errorlevel 255 to al
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mov ax, 4c00h ; standard dos kernel terminate
|
||||||
|
int 21h ; bye.
|
||||||
|
|
||||||
|
endcheck:
|
||||||
|
push cx ; push cx to stack
|
||||||
|
add cx, di ; add di to cx
|
||||||
|
sub cx, [blockend] ; subtract blockend from cx
|
||||||
|
jcxz fakenew ; jump if zero
|
||||||
|
pop cx ; invert cx (it should be neg)
|
||||||
|
ret ; go back to moving bytes
|
||||||
|
|
||||||
|
fakenew:
|
||||||
|
sub sp, 04h ; reset the stack you animal
|
||||||
|
mov di, [envstart] ; load di
|
||||||
|
jmp newenv ; pretend it's new
|
||||||
|
|
||||||
|
copybytes:
|
||||||
|
push ds ; push ds on to the stack
|
||||||
|
push es ; push es on to the stack
|
||||||
|
pop ds ; pop es in to ds for this
|
||||||
|
repe movsb ; copy ds:si to es:di till cx is 0
|
||||||
|
pop ds ; pop ds's original value back out
|
||||||
|
ret
|
||||||
|
|
||||||
|
envfree:
|
||||||
|
mov ax, [blocksize] ; load size
|
||||||
|
sub ax, [blockend] ; calculate free
|
||||||
|
cmp al, [oneornine] ; need n free
|
||||||
|
jz blockfull ; not enough space
|
||||||
|
ret ; return if ok
|
||||||
|
|
||||||
|
bytesize:
|
||||||
|
add di, cx ; place di at next variable
|
||||||
|
mov cx, [blockend] ; load the end of the block
|
||||||
|
sub cx, di ; subtract the actual usage
|
||||||
|
ret ; return from subroutine
|
||||||
|
|
||||||
|
hllp:
|
||||||
|
lea dx, hlptxt ; address of $-terminated strong
|
||||||
|
mov ah, 09h ; display string function
|
||||||
|
int 21h ; dos interrupt
|
||||||
|
jmp exit ; exit
|
||||||
|
|
||||||
|
new24:
|
||||||
|
mov al, 3 ; FAIL! (Hitting F, but faster)
|
||||||
|
iret ; Return from interrupt.
|
||||||
|
|
||||||
|
section .data
|
||||||
|
|
||||||
|
hlptxt:
|
||||||
|
db 'GETCD 1.0 | 4-NOV-2024 | dewdude@pickmy.org | Freeware/MIT', 0x0d, 0x0a
|
||||||
|
db 'Sets "CDROM=[driveletter]:" by searching CD-ROM drives', 0x0d, 0x0a
|
||||||
|
db 'USAGE: GETCD [/?] [FILE/OR/PATH/TO/FILE.EXT]', 0x0d, 0x0a
|
||||||
|
db 'Finds file on CD-ROM drives. Returns first match. Allows wildcards.', 0x0d, 0x0a
|
||||||
|
db 'Creates/adjusts variable. Default search is wildcard.$', 0x0d, 0x0a
|
||||||
|
blockfull: db 'NO ENV FREE $'
|
||||||
|
blocksize: db 0, 0 ; holds block size
|
||||||
|
envstart: db 0, 0 ; start of cdrom=
|
||||||
|
blockend: db 0, 0 ; end of used block
|
||||||
|
oneornine: db 09h ; default 9
|
||||||
|
envname: db 'CDROM=' ; variable name
|
||||||
|
drivevar: db '0:\' ; variable's variable
|
||||||
|
filename: db '*', 0x00 ; (default) filename
|
||||||
|
|
||||||
|
;MIT No Attribution
|
||||||
|
;
|
||||||
|
;Copyright 2024 Jay Moore
|
||||||
|
;
|
||||||
|
;Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
;software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
;without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
;merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;permit persons to whom the Software is furnished to do so.
|
||||||
|
;
|
||||||
|
;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
;INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
;PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
;HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
;OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
;SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
@ -0,0 +1,16 @@
|
|||||||
|
MIT No Attribution
|
||||||
|
|
||||||
|
Copyright 2024 Jay Moore
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any p
|
||||||
|
software and associated documentation files (the "Soft
|
||||||
|
without restriction, including without limitation the
|
||||||
|
merge, publish, distribute, sublicense, and/or sell co
|
||||||
|
permit persons to whom the Software is furnished to do
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHAN
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SH
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF O
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,164 @@
|
|||||||
|
# GETCD: MS-DOS .BAT Utility For Locating CD-ROM Drive
|
||||||
|
|
||||||
|
`GETCD.COM` is "batch utility" for MS-DOS to assist in locating a
|
||||||
|
CD-ROM drive based on file search. It is similar to `FINDCD.EXE`
|
||||||
|
with the following exceptions
|
||||||
|
- it takes an argument to specify a file
|
||||||
|
- automatically creates/manages the CDROM= variable
|
||||||
|
|
||||||
|
It is written in NASM assembler; and is mostly the thing I wrote
|
||||||
|
while ~~attempting to learn~~ learning assembler.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`GETCD.COM` does not display much in the way of user output. The
|
||||||
|
largest chunk can be seen by calling the basic help:
|
||||||
|
|
||||||
|
`getcd /?`
|
||||||
|
|
||||||
|
If you were to "place a CD" in a "CD drive" with the file what.txt
|
||||||
|
on it and ran this:
|
||||||
|
|
||||||
|
`getcd what.txt`
|
||||||
|
|
||||||
|
It looks like nothing happened. However, if you call `set` from the
|
||||||
|
command prompt to see the current variables; you should see `CDROM=D:`
|
||||||
|
(or whatever the actual drive letter is) now exists.
|
||||||
|
|
||||||
|
`getcd` on it's own will default to a wildcard (*) search. This has
|
||||||
|
the effect of returning the first CD drive that has a disc in it, or
|
||||||
|
doesn't return some kind of error.
|
||||||
|
|
||||||
|
Wildcards are allowed in the search string; as well as just directory
|
||||||
|
names. The first drive that doesn't fail search is returned.
|
||||||
|
|
||||||
|
`NO ENV FREE` is the only displayed error message if you don't have
|
||||||
|
enough environment space free. Most failures should result in nothing
|
||||||
|
being changed. This plays well with being a batch utility:
|
||||||
|
|
||||||
|
```
|
||||||
|
@echo off
|
||||||
|
set CDROM=0:
|
||||||
|
getcd what.txt
|
||||||
|
if %CDROM%=0: goto nocd
|
||||||
|
(do stuff)
|
||||||
|
goto exit
|
||||||
|
nocd:
|
||||||
|
(stuff for no cd)
|
||||||
|
exit:
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, you can look for errorlevel 255, which is returned if
|
||||||
|
there are no CDROM drives.
|
||||||
|
|
||||||
|
### Adapting scripts for FINDCD.EXE
|
||||||
|
|
||||||
|
You'll just need to modify `FINDCD` to `GETCD [file]`
|
||||||
|
|
||||||
|
### My scripts want a \ in the variable
|
||||||
|
|
||||||
|
`GETCD` is kind of picky in that it will make the CDROM variable
|
||||||
|
a drive letter, a colon, and no backslash. I may change this by
|
||||||
|
assuming an extra character is a backslash an not delete it.
|
||||||
|
|
||||||
|
But, for now; just do this in your .bat
|
||||||
|
|
||||||
|
```
|
||||||
|
set CDROM=%CDROM%\
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## What does this do?
|
||||||
|
|
||||||
|
- It lets you specify a file to search CD-ROM drives for and puts
|
||||||
|
that drive letter in to the CDROM= environment variable. Then
|
||||||
|
fancy .BAT scripts can go `call %CDROM%\install.bat` and
|
||||||
|
run the .bat off the CD.
|
||||||
|
|
||||||
|
## How does it differ from other utilities?
|
||||||
|
|
||||||
|
- The only utility I've actually found has been FINDCD.EXE. The
|
||||||
|
alternative is apparently for loops. I've seen mentions of other
|
||||||
|
utlities on sites devoted to .bat scripting; but never found an
|
||||||
|
exe.
|
||||||
|
The differences from FINDCD are passing a filename as an argument
|
||||||
|
and the automatic management of the variable. FINDCD used a hard
|
||||||
|
coded file in the binary as well as requiring you to preset the
|
||||||
|
variable.
|
||||||
|
I decided to just toss all that aside and assume the user might
|
||||||
|
not do it right; or in the case of FINDCD; the variable length
|
||||||
|
pre-set is longer than necessary. I have no clue what FINDCD is
|
||||||
|
doing with the variable.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
`nasm -o getcd.com getcd.asm`
|
||||||
|
|
||||||
|
The build system was FreeDOS with WatcomC, DJGPP, IA16, and a few other things on it.
|
||||||
|
|
||||||
|
It was tested on MS-DOS 6.22 under an 86Box emulated 486/DX4 100.
|
||||||
|
|
||||||
|
## Random Things Passed As FAQ:
|
||||||
|
|
||||||
|
**Why a .COM and not a .EXE?**
|
||||||
|
|
||||||
|
- We didn't need any of the fancy options a .exe gave us. Our binary is not over 65kB in size and doesn't have to be relocated to run. That is primarily what .exe would give me; the ability to relocate my code once loaded. I could have assembled this to an object file then linked it to an .EXE, but I still wouldn't be using any "EXE features"
|
||||||
|
|
||||||
|
**What are the minimum requirements?**
|
||||||
|
|
||||||
|
- DOS 3.3 and a CD-ROM drive. The program doesn't use much ram and
|
||||||
|
while will technically run on an 8088, the lack of CD-ROM drivers
|
||||||
|
are a bigger issue.
|
||||||
|
|
||||||
|
**Will this work on FreeDOS?**
|
||||||
|
|
||||||
|
- Yes, it's been known to. I have not tested the memory manipulating
|
||||||
|
version; but early versions worked.
|
||||||
|
|
||||||
|
**What about n-DOS?**
|
||||||
|
|
||||||
|
- No comment. It *should*, but it will depend if your CD-ROM extensions
|
||||||
|
respond to the interrupts I use.
|
||||||
|
|
||||||
|
**Why did you do this?**
|
||||||
|
|
||||||
|
- I'd wanted to see if I could write x86 ASM for quite some time. I
|
||||||
|
hadn't learned ASM beyond knowing how CPU's work at a low level
|
||||||
|
and what ASM was. I had an incorrect assumption that I didn't know
|
||||||
|
the levels of math required to make it work that I'd held for years.
|
||||||
|
Anyway; I'd also incorrectly assumed after diving in that the magic
|
||||||
|
of low-level would give me a secret way to look at drives without
|
||||||
|
triggering errors. It did; but changing the interrupt vector isn't
|
||||||
|
ASM specific..it was, like most things; a concept I'd heard about
|
||||||
|
but really didn't undertand back in my teens when I first tried.
|
||||||
|
I almost gave up and went to C when I was just trying to get the
|
||||||
|
byte in the env block realizing that's why people have libaries.
|
||||||
|
And, no; what little I read about the subject involved inline ASM.
|
||||||
|
My most optimized version was 98 bytes. Yes, 98. It had no file
|
||||||
|
argument or memory management. You had to make the environment;
|
||||||
|
variable, it put the letter at the byte after =, and that was it.
|
||||||
|
Then I decided to add features for the sake of stability and users;
|
||||||
|
plus what good is a clone if you don't improve it.
|
||||||
|
So despite serving no real purpose and being a huge time-sink for
|
||||||
|
something only I'll probably use; I'm at least better in ASM than
|
||||||
|
when I started. Huge bar given I started at almost zero.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT No Attribution
|
||||||
|
|
||||||
|
Copyright 2024 Jay Moore
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Loading…
Reference in New Issue