;       SCCSID = @(#)demnls.asm 6.2 91/05/29

SCCSID equ "@(#)demnls.asm      6.2 91/05/29"

PAGE    ,132
TITLE   DEMNLS.ASM - DOS emulation NLS code
NAME    DEMNLS
.386p

;***    DEMNLS.ASM
;
;       Title:
;
;       DOS emulation international language support
;
;       Description:
;
;       This module contains the get and set code page and get country
;       information services.
;
;       Public routines:
;
;       demInitNLS
;       VDHGetCodePageFont
;       VDHReleaseCodePageFont
;       demSetCodePage
;       demGetCountryInfo
;
;       BN001   B722111 JMH Removed JECXZ since CX will be blank if no
;                       codepages are defined. i.e. no CODEPAGE= in config.sys

INCL_NONE       equ     1                       ; don't include everything
INCL_TYPES      equ     1                       ; include basic types
INCL_DEF        equ     1                       ; include typedef macros
INCL_VDHALL     equ     1                       ; include VDH services

.xlist
        include fnomvdm.inc
        include dosseg.inc
        include osmaca.inc
        include osconst.inc
        include infoseg.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include vmowner.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include dirent.inc
        include curdir.inc
        include find.inc
        include screen.inc
        include sft.inc
        include sem.inc
        include tkptda.inc
        include kmode.inc
        include seldesc.inc
        include mvdm.inc
        include vdmvdh.inc
        include em86.inc
        include cdib.inc
        include mi.inc
        include error.inc
        include error2.inc
        include country.inc
        include doscntry.inc
        include fontfile.inc
.list
        ModName  demnls, FNO_DEMNLS_ASM

        CpuMode 386

        ReferGlobal CDIB_selector,WORD

        EXTRN16 w_16DevIOCtl
        EXTRN16 w_SetCp
        EXTRN16 w_GetCp

ERROR_NLS       equ     65                      ; NLS error code

DefData IMPORT, GLOBAL, C
EH      aeh
EndData

DefData PRIVATE, GLOBAL
SZ      szKBD,  <"KBD$", 0>
EndData

DefCode EXPORT, SWAP, PASCAL
DefFn   VDHGetCodePageFont
DefFn   VDHReleaseCodePageFont
EndCode

DefCode IMPORT, SWAP, PASCAL
DefFn   demOpenFile                             ; dema.asm
DefFn   demCloseFile                            ; dema.asm
DefFn   demReadFile                             ; dema.asm
DefFn   demSeekFile                             ; dema.asm
EndCode

DefCode IMPORT, GLOBAL, PASCAL
DefFn   VDHAssertVDMTaskOnly                    ; vdmm
DefFn   VDHSetError                             ; vdmm
EndCode

DefCode IMPORT, GLOBAL, C
DefFn   SELVirtToLin
DefFn   VMAllocKHB
DefFn   VMFreeKHB
EndCode

        FARCODE HIGH32
DefCode PRIVATE, SWAP, PASCAL

SWAP32_23 segment
;***LP  demInitNLS - initialize V86 kernel NLS information
;
;       ENTRY   (EBX) = pvsf
;               VSF(DS:SI) = COUNTRY_CDPG pointer
;
;       EXIT    (AX) = error code (0 = no error)
;
;       USES    EAX, EBX, EDX, ECX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       copy country.sys pathname to COUNTRY_CDPG
;       set country code in COUNTRY_CDPG
;       get and set the active and system code pages in COUNTRY_CDPG
;       initialize the rest of the COUNTRY_CDPG tables
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demInitNLS,near
        movzx   esi,[ebx].vsf_ds
        shl     esi,4
        movzx   eax,[ebx].vsf_si
        add     esi,eax                         ; (ESI) = COUNTRY_CDPG ptr

        lea     edi,[esi].ccPath_CountrySys     ; (ESI) = pathname buffer ptr
        mov     ecx,(size ccPath_CountrySys)    ; (ECX) = pathname buffer size
        push    esi
        call    demGetCountryCDIB               ; (DX) = country code
        pop     esi
        or      eax,eax
        jnz     short dinx                      ; jump if pathname too long
        mov     [esi].ccDosCountry,dx

;       (ESI) = COUNTRY_CDPG pointer

        call    demGetCodePageInfo              ; (AX) = error code
dinx:   ret
EndProc   demInitNLS
SWAP32_23 ends

SWAP32_22 segment
;***EP  VDHGetCodePageFont - return VDM code page font information
;
;       If code page support is active and a font is available for the
;       given character cell dimensions, then return the address of that
;       font.  System memory may be allocated to hold the font (especially
;       if it is a "merged" font -- one that had to be merged from two
;       separate font tables).  In any case, when the VDD is finished with
;       the font, it must call VDHReleaseCodePageFont.
;
;       ENTRY
;           ulCellwidth  - width of character cells, in pels
;           ulCellheight - height of character cells, in pels
;           ppFont       - address where font table addrees will be copied
;
;       EXIT-SUCCESS
;           returns non-zero size of the font table in bytes
;
;       EXIT-FAILURE
;           returns 0
;           VDHGetError may return
;               ERROR_CODE_PAGE_MISMATCHED
;               ERROR_FILE_NOT_FOUND
;               ERROR_NOT_ENOUGH_MEMORY
;
;       VDM TERMINATION IMPLICATIONS
;           If this function was called on behalf of the terminating VDM,
;           VDHReleaseCodePageFont must be called to release the font(s).
;
;       CONTEXT
;           VDM Task
;
;       PSUEDO-CODE
;
;       get font file name from CDIB
;       open font file
;       read font file header
;       calculate and allocate space for font offset table
;       read font offset table
;       for each font
;           read font header
;           if the cell width and height match
;               allocate space for font data
;               read font data
;               break
;       free font offset table
;       return font data address
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure VDHGetCodePageFont,near

ArgVar    gcf_ulCellWidth,ULONG
ArgVar    gcf_ulCellHeight,ULONG
ArgVar    gcf_ppFont,PPVOID
LocalVar  gcf_ulErrorCode,ULONG
LocalVar  gcf_hfile,DWORD
LocalVar  gcf_usCodePage,WORD
LocalVar  gcf_pFontOffsetTable,PVOID
LocalVar  gcf_cbFontOffsetTable,ULONG
LocalVar  gcf_pFontData,PVOID
LocalVar  gcf_fontfilehdr,<size fontfilehdr>
LocalVar  gcf_fonthdr,<size fonthdr>
LocalVar  gcf_p,PVOID
LocalVar  gcf_offFontHdr,PVOID

        ASSERTVDMTASKONLY
        EnterProc
        SaveReg <ebx, esi, edi>
        mov     [gcf_ulErrorCode],ERROR_CODE_PAGE_MISMATCHED
        mov     ax,ss
        mov     ds,ax
   ASSUME DS:NOTHING
        lea     di,[gcf_usCodePage]             ; (DS:DI) = buffer to put cps
        mov     bx,2                            ; (BX) = cb of buffer
        CALL16  w_GetCp                         ; (CX) = cb of codepages
        FlatContext <ds,es>,bx
        jnc     gcf10                           ; jump to exit if error
        cmp     ax,ERROR_CPLIST_TOO_SMALL
        jne     gcf40

;       Get font file name

gcf10:  movzx   eax,[CDIB_selector]             ; (EAX) = cdib selector
        or      eax,eax
        jz      gcf40                           ; jump if no cdib segment
        CCALL32 SELVirtToLin, <0, eax>          ; (EAX) = linear address
        movzx   esi,[eax].CDIB_screen_ptr       ; (ESI) = screen section offst
        or      esi,esi                         ; null screen info ptr
        jz      gcf40                           ;  yes, exit with error
        movzx   esi,[eax+esi].CDIB_dev_filename_ptr
        add     esi,eax                         ; (ESI) = font file path

;       Open font file, (ESI) = pointer to path name

        SSToDS  edi,[gcf_hfile]                 ; (EDI) = pointer to put handle
        PCall   demOpenFile, <esi, edi, NULL>
        mov     [gcf_ulErrorCode],eax
        or      eax,eax                         ; open file error?
        jnz     gcf40                           ;  yes, exit
        mov     ebx,[gcf_hfile]                 ; (EBX) = file handle

;       Read font file header, (EBX) = file handle

        xor     edx,edx                         ; (EDX) = header position
        mov     ecx,(size fontfilehdr)          ; (ECX) = number of bytes
        mov     esi,ecx                         ; (ESI) = buffer size
        SSToDS  edi,[gcf_fontfilehdr]           ; (EDI) = buffer
        PCall   demSeekRead
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     gcf35

;       Calculate and allocate space needed for font offset table

        mov     [gcf_ulErrorCode],ERROR_CODE_PAGE_MISMATCHED
        movzx   ecx,[gcf_fontfilehdr.ffh_numfonts]
        or      ecx,ecx
        jz      gcf35
        shl     ecx,2
        mov     [gcf_cbFontOffsetTable],ecx
        SSToDS  eax,[gcf_pFontOffsetTable]
        CCALL32 VMAllocKHB, <VM_HKH_PUB_SWAPRW, ecx, DEMFONTOFFOWNER, NA, eax>
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     gcf35

;       Read font offset table, (EBX) = file handle

        movzx   edx,[gcf_fontfilehdr.ffh_ptroffset] ; (EDX) = header position
        mov     ecx,[gcf_cbFontOffsetTable]     ; (ECX) = number of bytes
        mov     esi,ecx                         ; (ESI) = buffer size
        mov     edi,[gcf_pFontOffsetTable]      ; (EDI) = buffer
        PCall   demSeekRead
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     gcf30

        mov     eax,[gcf_pFontOffsetTable]
        mov     [gcf_p],eax

;       Read a font header, (EBX) = file handle

gcf20:  mov     edx,[gcf_p]
        mov     edx,[edx]                       ; (EDX) = header position
        mov     [gcf_offFontHdr],edx            ; save offset in case it's good
        mov     ecx,(size fonthdr)              ; (ECX) = number of bytes
        mov     esi,ecx                         ; (ESI) = buffer size
        SSToDS  edi,[gcf_fonthdr]               ; (EDI) = buffer
        PCall   demSeekRead
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     gcf30

        mov     [gcf_ulErrorCode],ERROR_CODE_PAGE_MISMATCHED
        add     [gcf_p],4
        sub     [gcf_cbFontOffsetTable],4       ; end of font offset table?
        jz      gcf30                           ;  yes, exit with error

        mov     ax,[gcf_usCodePage]             ; (AX) = active code page
        cmp     [gcf_fonthdr.fh_cpid],ax        ; does the code page match?
        jne     short gcf20                     ;  no, next font table

        mov     eax,[gcf_ulCellWidth]           ; (EAX) = cell width
        cmp     [gcf_fonthdr.fh_cellcols],al    ; does the cell width match?
        jne     short gcf20                     ;  no, next font table

        mov     eax,[gcf_ulCellHeight]          ; (EAX) = cell height
        cmp     [gcf_fonthdr.fh_cellrows],al    ; does the cell height match?
        jne     short gcf20                     ;  no, next font table

;       Allocate space for font data

        movzx   ecx,[gcf_fonthdr.fh_datalen]
        SSToDS  eax,[gcf_pFontData]
        CCALL32 VMAllocKHB, <VM_HKH_PUB_SWAPRW, ecx, DEMFONTOWNER, NA, eax>
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     short gcf30

;       Read font data, (EBX) = file handle

        movzx   edx,[gcf_fonthdr.fh_dataoffset]
        add     edx,[gcf_offFontHdr]            ; (EDX) = font position
        movzx   ecx,[gcf_fonthdr.fh_datalen]    ; (ECX) = number of bytes
        mov     esi,ecx                         ; (ESI) = buffer size
        mov     edi,[gcf_pFontData]             ; (EDI) = buffer
        PCall   demSeekRead
        mov     [gcf_ulErrorCode],eax
        or      eax,eax
        jnz     short gcf25

        mov     ebx,[gcf_ppFont]                ; (EBX) = ptr to put font ptr
        mov     eax,[gcf_pFontData]             ; (EAX) = ptr to font data
        mov     [ebx],eax

;       Success: free font offset table, close font file and return font size

        CCALL32 VMFreeKHB, <VM_HKH_PUB_SWAPRW, [gcf_pFontOffsetTable]>
        PCall   demCloseFile, <[gcf_hfile]>
        or      eax,eax
        jnz     short gcfie
        mov     eax,esi                         ; (EAX) = size of font table
        jmp     short gcfx

;       Failure: free font data, free font offset and close font file

gcf25:  CCALL32 VMFreeKHB, <VM_HKH_PUB_SWAPRW, [gcf_pFontData]>
gcf30:  CCALL32 VMFreeKHB, <VM_HKH_PUB_SWAPRW, [gcf_pFontOffsetTable]>
gcf35:  PCall   demCloseFile, <[gcf_hfile]>
        or      eax,eax
        jnz     short gcfie
gcf40:  PCall   VDHSetError, <[gcf_ulErrorCode]>
        xor     eax,eax
gcfx:   RestoreReg <edi, esi, ebx>
        ExitProc
gcfie:  Panic   <"VDHGetCodePageFont: demCloseFile">
EndProc   VDHGetCodePageFont
SWAP32_22 ends

SWAP32_22 segment
;***EP  VDHReleaseCodePageFont - release VDHGetCodePageFont storage
;
;       Release a code page font loaded with VDHGetCodePageFont.
;       If system memory was allocated to hold the specified font,
;       then this call frees that memory.
;
;      ENTRY
;           pFont - pointer returned by VDHGetCodePageFont
;
;       EXIT-SUCCESS
;           returns TRUE
;
;       EXIT-FAILURE
;           System Halt.
;               pFont was not valid.
;
;       VDM TERMINATION IMPLICATIONS
;           VDD must be sure to call this function for any code page
;           fonts loaded for this VDM.
;
;      CONTEXT
;           VDM Task
;
;       PCODE
;
;       free font data memory
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure VDHReleaseCodePageFont,near

ArgVar  rcp_pFont,PVOID

        ASSERTVDMTASKONLY
        EnterProc
        CCALL32 VMFreeKHB, <VM_HKH_PUB_SWAPRW, [rcp_pFont]>
        ExitProc
EndProc   VDHReleaseCodePageFont
SWAP32_22 ends

SWAP32_23 segment
;***LP  demSetCodePage - set new code page
;
;       Sets a new code page for the VDM and informs the keyboard PDD
;       interested VDDs that the code page change happened.  The new
;       code page information is copied to the V86 kernel buffer.
;
;       ENTRY   (EBX) = pvsf
;               VSF(BX) = new code page
;               VSF(DX) = country code (unused)
;               VSF(DS:SI) = COUNTRY_CDPG pointer
;
;       EXIT    (AX) = error code (0 = no error)
;
;       USES    EAX, EBX, EDX, ECX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set new active code
;       open keyboard PDD
;       send code page ioctl (04, 58)
;       close keyboard PDD
;       send code page change to VDDs
;       copy new code page info to COUNTRY_CDPG buffer
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demSetCodePage,near

LocalVar  scp_pvsf,PVOID
LocalVar  scp_usCodePage,USHORT
LocalVar  scp_hfileKBD,ULONG
LocalVar  scp_ulErrorCode,ULONG

        EnterProc
        mov     [scp_pvsf],ebx                  ; save PVSF
        mov     [scp_ulErrorCode],NO_ERROR      ; start with no errors
        mov     ebx,[ebx].vsf_ebx               ; (BX) = code page id
        mov     [scp_usCodePage],bx             ; save code page id
        xor     ecx,ecx                         ; reserved
        CALL16  w_SetCp                         ; set the new code page
        FlatContext <ds,es>,cx
        mov     eax,ERROR_INVALID_DATA          ; (AX) = nls error
        jc      scpx                            ; jump to exit if error

;       Open keyboard PDD

        SSToDS  eax,[scp_hfileKBD]              ; (EAX) = pointer to put handle
        PCall   demOpenFile, <offset FLAT:szKBD, eax, NULL>
        or      eax,eax
        jnz     short scpie1                    ; panic if open error

;       Send code page IOCTL to keyboard PDD
;
;       (AL)     = category code
;       (AH)     = function code
;       (BX)     = file Handle
;       (ES:DI) -> parameter packet
;       (CX)     = length of parameter packet (0 if unknown)
;       (DS:DX) -> data packet
;       (SI)     = length of data packet (0 if unknown)

        mov     ax,5804h                        ; (AH) = Func, (AL) = Cat
        mov     ebx,[scp_hfileKBD]              ; (BX) = handle
        mov     cx,ss
        mov     ds,cx
        mov     es,cx
   ASSUME  DS:NOTHING,ES:NOTHING
        lea     di,[scp_usCodePage]             ; (ES:DI) = parameter packet
        mov     dx,di                           ; (DS:DX) = data packet
        mov     si,2                            ; (SI) = parameter packet len
        mov     cx,si                           ; (CX) = data packet len
        push    ebp
        CALL16  w_16DevIOCtl                    ; ignore errors from KBD
        pop     ebp
        FlatContext <ds,es>,cx
        jnc     short scp10
        mov     [scp_ulErrorCode],ERROR_NLS

;       Close keyboard PDD

scp10:  PCall   demCloseFile, <[scp_hfileKBD]>
        or      eax,eax                         ; check demCloseFile ret code
        jnz     short scpie2                    ; panic if error

;       Send code page change notification to VDDs

        mov     ebx,aeh[VDM_CODEPAGE_CHANGE*(size EH_s)].eh_pelFirst
scp20:  or      ebx,ebx                         ; while != NULL
        jz      short scp30
        movzx   eax,[scp_usCodePage]            ; (EAX) = code page id
        PCall   [ebx].el_pfn, <eax>
        or      eax,eax                         ; if (EAX) == FALSE, error
        jnz     short scp25                     ; jump if TRUE (no error)
        mov     [scp_ulErrorCode],ERROR_NLS
scp25:  mov     ebx,[ebx].el_pel                ; (EBX) = next el entry
        jmp     short scp20

;       Get linear v86 kernel buffer pointer

scp30:  mov     ebx,[scp_pvsf]                  ; (EBX) = pvsf
        movzx   esi,[ebx].vsf_ds
        shl     esi,4
        movzx   eax,[ebx].vsf_si
        add     esi,eax                         ; (ESI) = COUNTRY_CDPG ptr
        call    demGetCodePageInfo              ; ignore errors
        mov     eax,[scp_ulErrorCode]           ; (EAX) = error code
scpx:   LeaveProc
        ret
scpie1: Panic   <"demSetCodePage: demOpenFile">
scpie2: Panic   <"demSetCodePage: demCloseFile">
EndProc   demSetCodePage
SWAP32_23 ends

;***LP  demGetCountryInfo - get country information
;
;       Returns non-current country/code page information.  Reads it
;       from country.sys.
;
;       ENTRY   (EBX) = pvsf
;               VSF(AL) = NLS table code
;               VSF(BX) = code page (0 = default code page)
;               VSF(DX) = country code
;               VSF(CX) = buffer size
;               VSF(DS:SI) = buffer
;
;       EXIT    if no error,
;                   (AX) == 0
;                   VSF(CX) = actual table size
;               if error,
;                   (AX) = error code
;
;       USES    EAX, EBX, EDX, ECX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       call demReadCountryFile
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demGetCountryInfo,near
        movzx   edi,[ebx].vsf_ds
        shl     edi,4
        movzx   eax,[ebx].vsf_si
        add     edi,eax                         ; (EDI) = laddr of buffer
        movzx   ecx,[ebx].vsf_cx                ; (ECX) = buffer size
        PCall   demReadCountryFile, <[ebx].vsf_eax, [ebx].vsf_ebx, \
                                     [ebx].vsf_edx, edi, ecx>
        ret
EndProc   demGetCountryInfo

SWAP32_23 segment
;***LP  demGetCodePageInfo - get code page information from CDIB
;
;       For the current code page, this routine copies the information
;       from the CDIB to the V86 kernel's COUNTRY_CDPG buffer.
;
;       ENTRY   (ESI) = COUNTRY_CDPG pointer
;
;       EXIT    (AX) = error code (0 = no error)
;
;       USES    EAX, EBX, EDX, ECX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The length fields in the COUNTRY_CDPG structure DO NOT
;               include the size of the length field word.  But the CDIB
;               table length fields DO include the size of length field.
;
;       PSEUDO-CODE
;
;       query active and system code pages
;       copy new code page info to COUNTRY_CDPG buffer
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demGetCodePageInfo,near,local

LocalVar  gcp_pBuffer,PVOID
LocalVar  gcp_ausCodePages,6

        EnterProc
        mov     [gcp_pBuffer],esi
        mov     ax,ss
        mov     ds,ax
   ASSUME DS:NOTHING
        lea     di,[gcp_ausCodePages]           ; (DS:DI) = buffer to put cps
        mov     bx,6                            ; (BX) = cb of buffer
        CALL16  w_GetCp                         ; (CX) = cb of codepages
        FlatContext <ds,es>,bx
        jnc     gcp10                           ; jump to exit if error
        cmp     ax,ERROR_CPLIST_TOO_SMALL
        jne     gcpx

gcp10:  mov     eax,ERROR_INVALID_DATA          ; (AX) = nls error
        or      cx,cx                           ; country info exists?
        jz      gcpx                            ; jump if no country info

;       (ESI, EBX = CDIB ptrs, EDX = codepage) = demSearchCDIB(EBX = codepage)

        movzx   ebx,[gcp_ausCodePages].loword   ; (EBX) = active code page or 0
        push    ecx
        call    demSearchCDIB
        pop     ecx                             ; (CX) = size of cp list
        jc      gcpx                            ; jump if error, (EAX) = code

        SaveReg <ebx, esi>                      ; save CDIB pointers
        mov     ebx,edx                         ; (EBX) = active code page
        mov     edi,[gcp_pBuffer]               ; (EDI) = COUNTRY_CDPG buffer
        mov     [edi].ccDosCodePage,bx          ; set active code page
        mov     eax,ebx                         ; (AX) = active code page
        cmp     cx,2                            ; only one code page prepared?
        jbe     short gcp20                     ;  yes, set system code page
        mov     ax,[gcp_ausCodePages].hiword    ; (AX) = system code page
gcp20:  mov     [edi].ccSysCodePage,ax
        movzx   esi,[edi].ccDosCountry          ; (ESI) = country code

;       (EBX) = active code page
;       (ESI) = country code
;       (EDI) = COUNTRY_CDPG buffer pointer

        movzx   eax,[edi].ccFileUcase_ptr.Segmt
        shl     eax,4
        movzx   edx,[edi].ccFileUcase_ptr.Offst
        add     eax,edx                         ; (EAX) = file ucase table ptr
        mov     ecx,(size CC_FILE_UCASE_TAB)    ; (ECX) = max ucase table size
        PCall   demReadCountryFile, <SetUcaseFile, ebx, esi, eax, ecx>

;       Ignore errors from demReadCountryFile

        movzx   eax,[edi].ccFileChar_ptr.Segmt
        shl     eax,4
        movzx   edx,[edi].ccFileChar_ptr.Offst
        add     eax,edx                         ; (EAX) = file char table ptr
        mov     ecx,(size CC_FILE_CHAR_TAB)     ; (ECX) = max filechar tab size
        PCall   demReadCountryFile, <SetFileList, ebx, esi, eax, ecx>

;       Ignore errors from demReadCountryFile

        RestoreReg <esi, ebx>                   ; restore CDIB pointers

;       (EBX) = CDIB code page data pointer
;       (ESI) = CDIB base pointer
;       (EDI) = COUNTRY_CDPG buffer pointer

        lea     eax,[edi].ccDFormat             ; (EAX) = country info pointer
        mov     ecx,((size DOS_country_cdpg_info) - ccDFormat)
        movzx   edx,[ebx].CDIB_cpd_format_ptr   ; (EDX) = CDIB table pointer
        call    demCopyCDIB

        lea     eax,[edi].ccUcase_ptr           ; (EAX) = ucase table pointer
        mov     ecx,(size CC_UCASE_TAB)         ; (ECX) = max ucase table size
        movzx   edx,[ebx].CDIB_cpd_casemap_ptr  ; (EDX) = CDIB casemap offset
        call    demCopyCDIB2

        lea     eax,[edi].ccCollate_ptr         ; (EAX) = collate table ptr
        mov     ecx,(size CC_COLLATE_TAB)       ; (ECX) = max collate table size
        movzx   edx,[ebx].CDIB_cpd_collate_ptr  ; (EDX) = CDIB collate offset
        call    demCopyCDIB2

        lea     eax,[edi].ccDBCS_ptr            ; (EAX) = DBCS table ptr
        mov     ecx,(size CC_DBCS_TAB)          ; (ECX) = max DBCS table size
        movzx   edx,[ebx].CDIB_cpd_DBCS_ptr     ; (EDX) = CDIB DBCS offset
        call    demCopyCDIB2
        xor     ax,ax                           ; (AX) = no errors
gcpx:   LeaveProc
        ret
EndProc   demGetCodePageInfo
SWAP32_23 ends

SWAP32_23 segment
;***LP  demReadCountryFile - read country information from country.sys
;
;       Reads the specified table from country.sys for the country/code
;       page passed including the length field.
;
;       INT PASCAL demReadCountryFile(bTableCode, usCodePage, usCountryCode,
;                                     pBuffer, cbBuffer)
;
;       ENTRY   (bTableCode) = country.sys table code
;                   0   country info for $INTERNATIONAL
;                   1-7 normal NLS table codes
;               (usCodePage) = code page (0 = default code page)
;               (usCountryCode) = country code
;               (pBuffer) = dest buffer pointer
;               (cbBuffer) = dest buffer size
;
;       EXIT    (EAX) = error code
;
;       USES    EAX, EDX, ECX, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The length fields for the headers and tables in the
;               COUNTRY.SYS file DO NOT include the size of the length
;               field word.
;
;       PSEUDO-CODE
;
;       get country.sys pathname
;       open country.sys
;       read country header (cty_cc_hdr)
;       read count of country entries (cty_cc_cnt)
;       do
;           if count == 0
;               return error codepage/country not found
;           decrement count
;           read country/codepage header length (cty_cp_len)
;           read country/codepage header (cty_cp_hdr)
;       while country (cty_cp_cty) != requested country &&
;             codepage (cty_cp_cp) != requested codepage
;       read country data count (cty_dat_cnt)
;       do
;           if count == 0
;               return table code not found
;           decrement count
;           read country data length (cty_dat_len)
;           read country data entry (cty_dat_ent)
;       while table code (cty_dat_inf) != requested table code
;       read table size (cty_???_len)
;       read table into buffer (cty_???_tbl)
;       close country.sys
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

TEMP_BUFFER_SIZE        equ     64

Procedure demReadCountryFile,near,local

ArgVar  dlc_bTableCode,BYTE
ArgVar  dlc_usCodePage,USHORT
ArgVar  dlc_usCountryCode,USHORT
ArgVar  dlc_pBuffer,PVOID
ArgVar  dlc_cbBuffer,ULONG
LocalVar dlc_hfile,ULONG
LocalVar dlc_abTempBuffer,TEMP_BUFFER_SIZE
LocalVar dlc_ulPos,ULONG
LocalVar dlc_c,ULONG

        EnterProc
        SaveReg <ebx, esi, edi>
        SSToDS  edi,[dlc_abTempBuffer]          ; (EDI) = pointer to put name
        mov     ecx,TEMP_BUFFER_SIZE            ; (ECX) =  size of name buffer
        call    demGetCountryCDIB               ; get country.sys pathname
        or      eax,eax
        jnz     dlcx                            ; jump if pathname too long

;       Open country.sys

        SSToDS  esi,[dlc_abTempBuffer]          ; (ESI) = country.sys pathname
        SSToDS  edi,[dlc_hfile]                 ; (EDI) = pointer to put handle
        PCall   demOpenFile, <esi, edi, NULL>
        or      eax,eax                         ; open file error?
        jnz     dlcx                            ;  yes, exit

        mov     ebx,[dlc_hfile]                 ; (EBX) = file handle
        SSToDS  edi,[dlc_abTempBuffer]          ; (EDI) = buffer to put header
        mov     esi,TEMP_BUFFER_SIZE            ; (ESI) = size of temp buffer

;       Read country header

        mov     edx,(size cty_hdr)              ; (EDX) = header position
        mov     ecx,(size cty_cc_hdr)           ; (ECX) = number of bytes
        PCall   demSeekRead
        or      eax,eax
        jnz     dlc90

;       Read country/codepage count

        mov     edx,[edi].cty_cc_ptr            ; (EDX) = count position
        mov     [dlc_ulPos],edx
        mov     ecx,(size cty_cc_cnt)           ; (ECX) = size of count
        add     [dlc_ulPos],ecx
        PCall   demSeekRead
        or      eax,eax
        jnz     dlc90

        movzx   eax,word ptr [edi]              ; (AX) = count of entries
        mov     [dlc_c],eax

dlc10:  mov     eax,ERROR_INVALID_DATA          ; (EAX) = error code
        cmp     [dlc_c],0                       ; are there entries left?
        je      dlc90                           ;  no, exit with error
        dec     [dlc_c]                         ;  yes, read entry

;       Read country/codepage header length

        mov     edx,[dlc_ulPos]                 ; (EDX) = position
        .errnz  cty_cp_len
        mov     ecx,(size cty_cp_len)           ; (ECX) = size of length field
        PCall   demSeekRead
        or      eax,eax
        jnz     dlc90

;       Read country/codepage header

        mov     edx,[dlc_ulPos]                 ; (EDX) = position
        movzx   ecx,[edi].cty_cp_len            ; (ECX) = size of header
        add     ecx,(size cty_cp_len)           ; (ECX) = include length field
        add     [dlc_ulPos],ecx                 ; update file position
        PCall   demSeekRead
        or      eax,eax
        jnz     dlc90

;       Do while the country and code page don't match

        mov     ax,[dlc_usCountryCode]          ; (AX) = country code
        cmp     ax,[edi].cty_cp_cty             ; does the country code match?
        jne     short dlc10                     ;  no, jump to loop begining
        mov     ax,[dlc_usCodePage]             ; (AX) = codepage
        or      ax,ax                           ; default code page?
        jz      short dlc20                     ;  yes, don't compare the cp
        cmp     ax,[edi].cty_cp_cp              ; does the code page match?
        jne     short dlc10                     ;  no, jump to loop begining

;       Read country data count

dlc20:  mov     edx,[edi].cty_cp_ptr            ; (EDX) = count position
        mov     [dlc_ulPos],edx
        mov     ecx,(size cty_dat_hdr)          ; (ECX) = size of count
        add     [dlc_ulPos],ecx
        PCall   demSeekRead
        or      eax,eax
        jnz     dlc90

        movzx   eax,word ptr [edi]              ; (AX) = count of entries
        mov     [dlc_c],eax

dlc30:  mov     eax,ERROR_INVALID_FUNCTION      ; (EAX) = error code
        cmp     [dlc_c],0                       ; are there entries left?
        je      dlc90                           ;  no, exit with error
        dec     [dlc_c]                         ;  yes, read entry

;       Read country data header length

        mov     edx,[dlc_ulPos]                 ; (EDX) = position
        .errnz  cty_dat_len
        mov     ecx,(size cty_dat_len)          ; (ECX) = size of length field
        PCall   demSeekRead
        or      eax,eax
        jnz     short dlc90

;       Read country data header

        mov     edx,[dlc_ulPos]                 ; (EDX) = position
        movzx   ecx,[edi].cty_dat_len           ; (ECX) = size of data header
        add     ecx,(size cty_dat_len)          ; (ECX) = include length field
        add     [dlc_ulPos],ecx                 ; update file position
        PCall   demSeekRead
        or      eax,eax
        jnz     short dlc90

;       Do while table code doesn't match

        mov     al,[dlc_bTableCode]             ; (AL) = NLS table code
        or      al,al                           ; special country info code?
        jnz     short dlc40                     ;  no, jump to check
        mov     al,SetCountryInfo               ; (AL) = country info table
dlc40:  cmp     al,[edi].cty_dat_inf            ; does the table code match?
        jne     short dlc30                     ;  no, jump to loop begining

;       Read NLS table length

        mov     edx,[edi].cty_dat_ptr           ; (EDX) = NLS table position
        add     edx,8                           ; (EDX) = skip table tag
        .errnz  (cty_ucase_len - 8)
        .errnz  (cty_fucase_len - 8)
        .errnz  (cty_fchar_len - 8)
        .errnz  (cty_col_len - 8)
        .errnz  (cty_dbcs_len - 8)
        .errnz  (cty_info_size - 8)
        mov     [dlc_ulPos],edx                 ; save table length position
        mov     ecx,2                           ; (ECX) = size of length field
        .errnz  (size cty_ucase_len) - 2
        .errnz  (size cty_fucase_len) - 2
        .errnz  (size cty_fchar_len) - 2
        .errnz  (size cty_col_len) - 2
        .errnz  (size cty_dbcs_len) - 2
        .errnz  (size cty_info_size) - 2
        PCall   demSeekRead
        or      eax,eax
        jnz     short dlc90

;       Read NLS table

        movzx   ecx,word ptr [edi]              ; (ECX) = table size
        add     ecx,2                           ; (ECX) = add length field
        .errnz  (size cty_ucase_len) - 2
        .errnz  (size cty_fucase_len) - 2
        .errnz  (size cty_fchar_len) - 2
        .errnz  (size cty_col_len) - 2
        .errnz  (size cty_dbcs_len) - 2
        .errnz  (size cty_info_size) - 2
        cmp     [dlc_bTableCode],0              ; special country info code?
        jne     short dlc70                     ;  no, read table normally
        mov     ecx,OLD_COUNTRY_SIZE            ; (ECX) = $INTERNATIONAL size
        add     [dlc_ulPos],(cty_info_date_fmt - cty_info_size)

dlc70:  mov     edi,[dlc_pBuffer]               ; (EDI) = buffer to put table
        cmp     ecx,[dlc_cbBuffer]              ; is table size > buffer size
        jbe     short dlc80                     ;  no, go do read
        mov     ecx,[dlc_cbBuffer]              ;  yes, read size = buf size

dlc80:  mov     esi,ecx                         ; (ESI) = max size
        mov     edx,[dlc_ulPos]                 ; (EDX) = pos start at len field
        PCall   demSeekRead
        or      eax,eax
        jnz     short dlc90

        cmp     [dlc_bTableCode],0              ; special get country info?
        jz      short dlc90                     ;  yes, just exit
        sub     ecx,2                           ;  no, set actual table size
        .errnz  (size cty_ucase_len) - 2
        .errnz  (size cty_fucase_len) - 2
        .errnz  (size cty_fchar_len) - 2
        .errnz  (size cty_col_len) - 2
        .errnz  (size cty_dbcs_len) - 2
        .errnz  (size cty_info_size) - 2
        mov     [edi],cx

;       Close country.sys and exit

dlc90:  push    eax
        PCall   demCloseFile, <[dlc_hfile]>
        or      eax,eax
        jnz     short dlcie
        pop     eax
dlcx:   RestoreReg <edi, esi, ebx>
        ExitProc
dlcie:  Panic   <"demReadCountryFile: demCloseFile">
EndProc   demReadCountryFile
SWAP32_23 ends

SWAP32_22 segment
;***LP  demSeekRead - seek and read file
;
;       Seeks to the position passed and reads the number of bytes.
;
;       ENTRY   (EBX) = hfile
;               (ECX) = number of bytes to read
;               (EDX) = seek position
;               (ESI) = read buffer size
;               (EDI) = read buffer
;
;       EXIT    (EAX) = error code
;
;       USES    EAX, EDX, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       seekfile(hfile, absolute, pos, 0)
;       readfile(hfile, buffer, cbread, 0)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demSeekRead,near,local

LocalVar  dsr_cbToRead,ULONG
LocalVar  dsr_ulNewPos,ULONG
LocalVar  dsr_cbActuallyRead,ULONG

        EnterProc
        cmp     ecx,esi                         ; is the buffer too small?
        ja      short dsr10                     ;  yes, return error
        mov     [dsr_cbToRead],ecx
        SSToDS  eax,[dsr_ulNewPos]
        PCall   demSeekFile, <ebx, 0, edx, eax>
        or      eax,eax                         ; seek error?
        jnz     short dsrx                      ;  yes, jump to exit

        SSToDS  eax,[dsr_cbActuallyRead]
        PCall   demReadFile, <ebx, edi, [dsr_cbToRead], eax>
        or      eax,eax                         ; read error?
        jnz     short dsrx                      ;  yes, jump to exit

        mov     ecx,[dsr_cbToRead]              ; (ECX) = byte requested
        cmp     ecx,[dsr_cbActuallyRead]        ; bytes read == bytes requested?
        je      short dsrx                      ;  yes, jump to exit
dsr10:  mov     eax,ERROR_BUFFER_OVERFLOW       ;  no, return error
dsrx:   ExitProc
EndProc   demSeekRead
SWAP32_22 ends

SWAP32_23 segment
;***LP  demGetCountryCDIB - copy country.sys path from CDIB
;
;       Copies the country.sys path name from the CDIB to the
;       buffer passed and returns country code.
;
;       ENTRY   (ECX) = dest buffer size
;               (EDI) = dest buffer pointer
;
;       EXIT    (DX) = country code
;               (EAX) = error code
;                   ERROR_BAD_PATHNAME
;
;       USES    EAX, EDX, ECX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       pcdib->CDIB_country_ptr->CDIB_ct_filename is path name pointer
;       copy country.sys pathname to dest buffer
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demGetCountryCDIB,near,local
        movzx   eax,[CDIB_selector]             ; (EAX) = cdib selector
        or      eax,eax
        jz      short ccn20                     ; jump if no cdib segment
        CCALL32 SELVirtToLin, <0, eax>          ; (EAX) = linear address
        movzx   esi,[eax].CDIB_country_ptr      ; (ESI) = country section offst
        or      esi,esi                         ; null country info ptr
        jz      short ccn20                     ;  yes, exit with error
        mov     dx,[eax+esi].CDIB_ct_code       ; (DX) = country code
        lea     esi,[eax+esi].CDIB_ct_filename  ; (ESI) = country.sys path ptr
ccn10:  lodsb
        stosb
        movzx   eax,al
        or      eax,eax
        jz      short ccnx
        loop    short ccn10
ccn20:  mov     eax,ERROR_BAD_PATHNAME
ccnx:   ret
EndProc   demGetCountryCDIB
SWAP32_23 ends

SWAP32_23 segment
;***LP  demSearchCDIB - search CDIB for specified code page
;
;       ENTRY   (BX) = code page number or 0 (match first code page)
;
;       EXIT    (ESI) = CDIB base pointer
;               Carry clear,
;                   (EBX) = pointer to CDIB's code page section
;                   (EDX) = code page number
;               Carry set,
;                   (AX) = error code
;
;       USES    EAX, EBX, ECX, EDX, ESI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       get CDIB code page pointer (pcdib->CDIB_codepage_ptr->CDIB_cp_first_id)
;       for each code page
;          if code page matches code page in CDIB entry
;             return CDIB code page data pointer
;       return error if code page not found
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demSearchCDIB,near,local
        movzx   eax,[CDIB_selector]             ; (EAX) = cdib selector
        or      eax,eax
        jz      short scb20                     ; jump if no cdib segment
        CCALL32 SELVirtToLin, <0, eax>          ; (EAX) = linear address
        mov     esi,eax                         ; (ESI) = cdib pointer
        movzx   eax,[esi].CDIB_codepage_ptr     ; (EAX) = offset to cp section
        or      eax,eax                         ; null codepage info pointer?
        jz      short scb20                     ;  yes, exit with error
        movzx   ecx,[eax+esi].CDIB_cp_number_codepages ; (ECX) = num of cps
        lea     eax,[eax+esi].CDIB_cp_first_id  ; (EAX) = code page section ptr
        or      bx,bx                           ; codepage == 0?
        jz      short scb30                     ;  yes, match the first CP
;       jecxz   short scb20                     ; jump if no code pages    ; BN001 ;

scb10:  cmp     bx,[eax].CDIB_cp_id             ; does the code page match?
        je      short scb30                     ;  yes, exit loop
        add     eax,(size CDIB_cp_id_section)   ;  no, next entry
        loop    short scb10
scb20:  mov     eax,ERROR_INVALID_DATA          ; (AX) = nls error
        stc
        jmp     short scbx                      ; jump to exit

scb30:  movzx   edx,[eax].CDIB_cp_id            ; (EDX) = actual code page
        movzx   eax,[eax].CDIB_cp_data_ptr
        lea     ebx,[eax+esi]                   ; (EBX) = code page data ptr
        clc
scbx:   ret
EndProc   demSearchCDIB
SWAP32_23 ends

SWAP32_23 segment
;***EP  demCopyCDIB2 - copy code page entry from CDIB to COUNTRY_CDPG entry
;
;       Copies the specified code page table (country info, case,
;       collate, etc.) entry to the COUNTRY_CDPG entry including
;       the length field.
;
;       ENTRY   (EAX) = pointer to COUNTRY_CDPG table pointer
;               (ECX) = maximum COUNTRY_CDPG table size (including len field)
;               (EDX) = CDIB code page data offset
;               (ESI) = CDIB base pointer
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The length fields in the CDIB tables include the size
;               of the length field (a word).
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demCopyCDIB2,near,local
        SaveReg <edi>
        movzx   edi,[eax].Segmt                 ; (EDI) = segment
        shl     edi,4
        movzx   eax,[eax].Offst                 ; (EAX) = offset
        add     edi,eax                         ; (EDI) = table ptr
        lea     eax,[edi+2]                     ; (EAX) = table ptr after len
        sub     ecx,2                           ; (ECX) = don't include len
        call    demCopyCDIB
        stosw
        .errnz  ccUcase_leng
        .errnz  ccFileucase_leng
        .errnz  ccCollate_leng
        .errnz  ccDBCS_leng
        .errnz  (size ccUcase_leng) - 2
        .errnz  (size ccFileucase_leng) - 2
        .errnz  (size ccCollate_leng) - 2
        .errnz  (size ccDBCS_leng) - 2
        RestoreReg <edi>
        ret
EndProc   demCopyCDIB2
SWAP32_23 ends

SWAP32_23 segment
;***EP  demCopyCDIB - copy code page entry from CDIB to COUNTRY_CDPG entry
;
;       Copies the specified code page table (country info, case,
;       collate, etc.) entry to the COUNTRY_CDPG entry.  Doesn't
;       copy the length field to COUNTRY_CDPG table entry.
;
;       ENTRY   (EAX) = pointer to COUNTRY_CDPG table
;               (ECX) = maximum COUNTRY_CDPG table size
;               (EDX) = CDIB code page data offset
;               (ESI) = CDIB base pointer
;
;       EXIT    (EAX) = actual table size
;
;       USES    EAX, ECX, EDX, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure demCopyCDIB,near,local
        SaveReg <esi, edi>
        mov     edi,eax                         ; (EDI) = COUNTRY_CDPG entry ptr
        movzx   eax,word ptr [edx+esi]          ; (EAX) = table size
        lea     esi,[edx+esi+2]                 ; (ESI) = start after len field
        sub     eax,2                           ; (EAX) = size - len field size
        .errnz  CDIB_fmt_length
        .errnz  CDIB_col_length
        .errnz  CDIB_cm_length
        .errnz  CDIB_DBCS_length
        .errnz  (size CDIB_fmt_length) - 2
        .errnz  (size CDIB_col_length) - 2
        .errnz  (size CDIB_cm_length) - 2
        .errnz  (size CDIB_DBCS_length) - 2
        cmp     eax,ecx                         ; is table size < buffer size
        jbe     short ccb10                     ;  yes, use table size
        mov     eax,ecx                         ;  no, use buffer size
ccb10:  mov     ecx,eax                         ; (ECX) = bytes to move
        rep movsb
        RestoreReg <edi, esi>
        ret
EndProc   demCopyCDIB
SWAP32_23 ends

EndCode

        end
