Okay, let's try this again...

UTF-8 input file, with fields that look like this:

DC_NAME CHAR 100 102 8455 Both DC_NAME
Variable length field -- Allocated length : None
Coded Character Set Identifier . . . . . : 1208
UCS2 or Unicode conversion . . . . . . . : *CONVERT
Normalize data . . . . . . . . . . . . . : No
DC_OBJECT CHAR 1500 1502 8557 Both DC_OBJECT
Variable length field -- Allocated length : None
Allows the null value
Coded Character Set Identifier . . . . . : 1208
UCS2 or Unicode conversion . . . . . . . : *CONVERT
Normalize data . . . . . . . . . . . . . : No

UCS-2 output file, with fields that look like this:

DC_NAME GRAPHIC 100 202 16711 Both
Variable length field -- Allocated length : None
Coded Character Set Identifier . . . . . : 13488
UCS2 or Unicode conversion . . . . . . . : *CONVERT
DC_OBJECT GRAPHIC 1500 3002 16913 Both
Variable length field -- Allocated length : None
Allows the null value
Coded Character Set Identifier . . . . . : 13488
UCS2 or Unicode conversion . . . . . . . : *CONVERT

I've ALMOST got it, using the QlgTransformUCSData API. That is, I've got
a program that opens the file with the UTF-8 fields, does some mucking
around to manage the NULLs, runs the data through the API, and writes
back out the data in UCS-2. Here's the simplified version:

HALWNULL(*USRCTL)
HACTGRP(*NEW)
HDFTACTGRP(*NO)
HCCSID(*UCS2:13488)

FGET_BAT_Y IP E DISK PREFIX(Y)
FGET_BAT_X IF E K DISK PREFIX(X)
FGET_BAT_J O A E DISK RENAME(GET_BAT_J:RGET_BAT_J)
F PREFIX(J)

DQlgTransform PR 10I 0 extproc('QlgTransformUCSData')
D QlgType 10I 0 Value
D QlgInBuf *
D QlgInBytesLeft * Value
D QlgOutBuf *
D QlgOutBytesLft * Value
D QlgOutSpcReq * Value
D
D inbytes S 10I 0
D outbytes S 10I 0
D outmissing S 10I 0
D p_inbytes S * inz(%addr( inbytes ))
D p_outbytes S * inz(%addr( outbytes ))
D p_outmissing S * inz(%addr( outmissing ))
D
D rc S 10I 0
D
D outData S 8000C
D inData S 4000A
D outPtr S *
D inPtr S *
D
DC_UCS2AT C %ucs2('@')

C Eval XCNTRL_ID = YCNTRL_ID
C K_XFile Chain RGET_BAT_X
C
C Eval inData = YDC_NAME
C ExSR Magic
C Eval JDC_NAME = %trim(outData : C_UCS2AT)
C Eval JDC_NAME = %trim(JDC_NAME : C_UCS2AT)
C
C IF %NullInd(XDC_OBJECT )
C Eval %NullInd(JDC_OBJECT ) = *ON
C ELSE
C Eval %NullInd(JDC_OBJECT ) = *OFF
C Eval inData = YDC_OBJECT
C ExSR Magic
C Eval JDC_OBJECT = %trim(outData : C_UCS2AT)
C Eval JDC_OBJECT = %trim(JDC_OBJECT : C_UCS2AT)
C ENDIF
C
C Write RGET_BAT_J
C
C*
C*----------------------------------------------
C*
C *INZSR BegSR
C
C K_XFile KList
C KFld XCNTRL_ID
C
C
C ENDSR
C*
C*----------------------------------------------
C*
C Magic BegSR
C
C Clear outData
C Eval inPtr = %addr(inData)
C Eval inbytes = %size(inData)
C Eval outPtr = %addr(outData)
C Eval outbytes = %size(outData)
C
C Eval outmissing = 0
C Eval rc = QlgTransform(2:
C inPtr:
C p_inbytes:
C outPtr:
C p_outbytes:
C p_outmissing )
C
C ENDSR

I fire up DBG, set a breakpoint at the beginning for the section that
converts XDC_NAME to JDC_NAME and step through the subroutine, and look
at the variable contents:

> EVAL YDC_NAME :x

00000 001CD2D9 F7C1D5F1 F2C44060 40D2D940 - ..KR7AN12D - KR
00010 60403F3F 3F403F3F 3F403F3F 3F3F40A3 - - ... ... .... t
00020 9640E896 A440D396 83819340 C5A58595 - o You Local Even
00030 A3A20000 00000000 00000000 00000000 - ts..............
00040 00000000 00000000 00000000 00000000 - ................
00050 00000000 00000000 00000000 00000000 - ................
00060 00000000 0000.... ........ ........ - ................

Oops. That's not actually UTF-8. It looks like RPG helpfully converted
my UTF-8 data to CP37. Too bad that there's some Korean characters in
there.... I'll change the job to CCSID(65535) and run it again.

> EVAL YDC_NAME :x

00000 00304B52 37414E31 3244202D 204B5220 - .....+.......
00010 2D20EAB5 90EC9CA1 EBB68020 ED9598EB - ............nq.
00020 B098EAB8 B020ECB9 B4ED8380 EBA19CEA - ^q..^.....c..~.
00030 B7B80000 00000000 00000000 00000000 - ................
00040 00000000 00000000 00000000 00000000 - ................
00050 00000000 00000000 00000000 00000000 - ................
00060 00000000 0000.... ........ ........ - ................

Much better. We can see the Korean starting right at byte 0012, in valid
UTF-8 hex. We step through the subroutine and look at it after the %trim
functions:

> EVAL JDC_NAME :x

00000 001C004B 00520037 0041004E 00310032 - ..........+....
00010 00440020 002D0020 004B0052 0020002D - ..............
00020 0020AD50 C721BD80 0020D558 BC18AE30 - ...&G.....N....
00030 0020CE74 D0C0B85C ADF80040 00400040 - ...}{.*.8. . .
00040 00400040 00400040 00400040 00400040 - . . . . . . . .
00050 00400040 00400040 00400040 00400040 - . . . . . . . .
00060 00400040 00400040 00400040 00400040 - . . . . . . . .
00070 00400040 00400040 00400040 00400040 - . . . . . . . .
00080 00400040 00400040 00400040 00400040 - . . . . . . . .
00090 00400040 00400040 00400040 00400040 - . . . . . . . .
000A0 00400040 00400040 00400040 00400040 - . . . . . . . .
000B0 00400040 00400040 00400040 00400040 - . . . . . . . .
000C0 00400040 00400040 ........ ........ - . . . . ........

Also looks good. 1C characters of valid data, so length data's correct
at the beginning, and we can see pretty clearly where the Korean begins
at 0021 with AD50, which
http://software.hixie.ch/utilities/c...r/utf8-decoder
confirms as the correct conversion. Let's go look at the output file.

DSPPFM and window over to the start of the DC_NAME field, Display Hex,
and Over/Under it:

...........+.....................&G.....N.......}{.*.8............
0104050304040303040202020405020202A5C2B802D5B1A302 C7DCB5AF020202020202
0C0B0207010E010204000D000B02000D00D071D00058C8E000 E4008CD8000000000000

Length byte is right, and there's out AD05 start to Korean passage...

I think it works. Key parts are seeming to be A) set job CCSID(65535)
while running the program, and B) Use the APIs, not the Built-In
Functions, because those go all "funny" when the job CCSID isn't normal.

--
I wish there was a knob on the TV to turn up the intelligence. There's a
knob called "brightness", but it doesn't work.
-- Gallagher