.
I have an “as3 windows” project that requires executing the command line by calling “cmd. exe”. I have always wanted to use “openfl” for development, but I don’t know how to implement this class yet.
.
In AS3, I executed it this way
.
//Open an ‘exe’ program
Call.open(“start Meteor.exe”);
.
//Use ‘cmd. exe’ to execute the command line and close the exe program.
Call.open(“taskkill /f /im Meteor.exe”);
.
//Open “cmd. exe” and add files to the compressed file using “winrar. exe”, as well as copy the files.
Call.skin(“zhiyayouxiData\ptexture\”+Object(root).id1+“\.”);
Call.copy(“zhiyayouxiData\pmodel\”+Object(root).id1+“.amb”,“pmodel\”+Object(root).id2+“.amb”);
.
I have written all of these, mainly the implementation of the “Call. as” class
I don’t know how to implement it in ‘openfl’? Who can help me?
.
.
“Call.as”
package code
{
import flash.filesystem.*;
import flash.desktop.*;
import flash.utils.*;
import flash.events.*;
public class Call
{
private static var process: NativeProcess;
private static var nativeProcessStartupInfo: NativeProcessStartupInfo;
private static function newexe(EXEPath: String, parentDirectory: String = "", inistr: String = "")
{
nativeProcessStartupInfo = new NativeProcessStartupInfo();
var fileexe: File = File.applicationDirectory.resolvePath(EXEPath); //bin/ffmpeg.exe
process = new NativeProcess();
process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, input);
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(NativeProcessExitEvent.EXIT, onExit);
process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
if(fileexe.exists)
{
nativeProcessStartupInfo.executable = fileexe;
if(parentDirectory == "")
{
nativeProcessStartupInfo.workingDirectory = fileexe.parent;
}
else
{
nativeProcessStartupInfo.workingDirectory = new File(parentDirectory);
}
}
if(inistr != "")
{
var processArgs: Vector. < String > = new Vector. < String > ();
var s: Array = inistr.split(" ");
for(var i = 0; i < s.length; i++)
{
processArgs.push(s[i]);
}
nativeProcessStartupInfo.arguments = processArgs;
process.start(nativeProcessStartupInfo);
}
else
{
process.start(nativeProcessStartupInfo);
}
}
newexe("C:/Windows/System32/cmd.exe", File.applicationDirectory.nativePath);
private static function call(e: String, f: String = "gbk")
{
process.standardInput.writeMultiByte(e, f);
}
public static function skin(e: String)
{
call("zhiyayouxiData\\WinRAR\\WinRAR.exe a -ep1 -apptexture ptexture.pak" + " " + e + "\n");
}
public static function copy(file1: String, file2: String)
{
call("copy" + " " + file1 + " " + file2 + "/y" + "\n");
}
public static function open(e: String)
{
call(e + "\n");
}
private static function input(e)
{
trace("成功发送数据");
}
private static function onOutputData(event: ProgressEvent): void
{
var jqm = process.standardOutput.readMultiByte(process.standardOutput.bytesAvailable, "GBK");
trace("输出的数据:", jqm);
}
private static function onErrorData(event: ProgressEvent): void
{
var jqm = process.standardOutput.readMultiByte(process.standardOutput.bytesAvailable, "GBK");
trace("出错的信息是:" + jqm);
}
private static function onExit(e): void
{
trace("调用的exe关闭了");
}
private static function onIOError(event: IOErrorEvent): void
{}
}
}
.
Has anyone done a similar project before?
.
How to implement the as class above in OpenFL?
.
Never did that but if I had to I would try with sys.io.Process - Haxe 4.3.7 API
OpenFL provides an implementation of NativeProcess.
However, we may not have proper implementations of readMultiByte and writeMultiByte, so you may run into issues there.
.
@[Matse]
@[joshtynjala]
.
I’m just checking if ‘Call. as’ is easy to implement in’ openfl ‘. If it’s not easy to implement, I’ll continue using’ air '.
.
process.standardInput.writeMultiByte(e, f);
process.standardOutput.readMultiByte(process.standardOutput.bytesAvailable, "GBK")
However, we may not have proper implementations of readMultiByte and writeMultiByte , so you may run into issues there.
When will it be supported?
If I follow where the issue may be, your code leverages the GBK character set (for simplified Chinese).
The process.standardInput.writeMultiByte() and process.standardOutput.readMultiByte() methods receive a charSet argument, but they do nothing with it, effectively ignoring it. For example:
Likewise, the input object used in the nativeProcess.readMultiByte() method is an openfl.utils.ByteArray, and it too does not handle the charSet argument in it’s byteArray.readMultiByte() method:
So if I understand the limitation (and @joshtynjala please correct me if I’m off the mark here), the current implementation falls back to UTF-8 character encoding, as suggested by the writeUTFBytes(value) and readUTFBytes(length) methods they ultimately use. As you’re using GBK character encoding, I’d anticipate an issue here.
This is the code for as3air, as shown in the as class above. “joshtynjala” says that openfl does not yet support these two methods.
The ‘as class’ mentioned above,
It’s one of my as3air tools,
I want to remake it using OpenFL,
But I cannot implement the ‘as class’ mentioned above in OpenFL!
Yes I think I followed that.
What I wasn’t certain about, is what @joshtynjala meant by OpenFL not having a proper implementation for readMultiByte and writeMultiByte. What I shared above is me having a go at trying to understand what might have been meant by that.
What that might mean for you is, if OpenFL’s current implementation forces UTF-8 character encoding, but your application is expecting GBK character encoding, perhaps there’s something that can be done to handle that differently.
If I’ve understood the limitations of the current implementation of readMultiByte and writeMultiByte correctly, the issue is it doesn’t respect legacy character sets, such as GBK. What it does do, is use the modern standard (UTF-8) which has become universal on modern operating systems.
Perhaps let go of the legacy GBK character set in your code, and test?
Can you help me implement the as class mentioned above using OpenFL?
As I often have, I’ll gladly help you troubleshoot @785597448, but I think it’s important you take on the task of writing the class for your purposes.
Actually, I have no knowledge about the API of the AS3AIR operating system. The above classes were written by someone I paid for, many years ago. I recently came into contact with OpenFL and had the idea of using OpenFL to do them, but I don’t know how to implement the above classes in OpenFL. That’s why I posted for help. I often use the “AS3 Movieclip” API
I want to use OpenFL to remake this tool, but if I cannot implement the above classes, then I will have to continue using ‘as3air’. Thank you very much.
Tell us about this tool @785597448. Is it open-source?
Correct. We support UTF-8 encoding only, and other multi-byte encodings are not supported by OpenFL at this time. There is no estimate on when they will be supported in the future. I personally don’t know how to implement these methods properly, so someone else may need to do it. We may eventually get lucky and someone will submit a PR to OpenFL (and possibly to Lime, if it requires some C++ code to implement properly, which is what I suspect).
how correct are these implementations @joshtynjala and @Matse ?
(Note code has been documented and beautified with help of Claude just for someone who can do it)
making a new lime/text/MultiByteEncoding.cpp :
#include <hx/CFFI.h>
#include <string>
#include <vector>
#include <algorithm>
#include <cstring>
#ifdef _WIN32
#include <windows.h>
#elif defined(__APPLE__)
#include <CoreFoundation/CoreFoundation.h>
#else
#include <iconv.h>
#include <errno.h>
#endif
namespace lime {
namespace text {
// =============================================================
// Charset normalization
// =============================================================
static std::string normalizeCharset(const char* charset) {
std::string cs = charset ? charset : "utf8";
std::transform(cs.begin(), cs.end(), cs.begin(), ::tolower);
cs.erase(std::remove(cs.begin(), cs.end(), '-'), cs.end());
cs.erase(std::remove(cs.begin(), cs.end(), '_'), cs.end());
return cs;
}
// =============================================================
// Windows implementation
// =============================================================
#ifdef _WIN32
static int getCodePage(const std::string& cs) {
if (cs == "utf8") return CP_UTF8;
if (cs == "shiftjis" || cs == "sjis") return 932;
if (cs == "gbk" || cs == "gb2312") return 936;
if (cs == "big5") return 950;
if (cs == "euckr") return 949;
if (cs == "windows1252" || cs == "cp1252") return 1252;
if (cs == "iso88591" || cs == "latin1") return 28591;
return CP_UTF8;
}
static value decodeWindows(value bytes, value charset) {
buffer buf = val_to_buffer(bytes);
const char* data = buffer_data(buf);
int len = buffer_size(buf);
std::string cs = normalizeCharset(val_string(charset));
int cp = getCodePage(cs);
int wideLen = MultiByteToWideChar(cp, 0, data, len, NULL, 0);
if (wideLen <= 0) val_throw(alloc_string("MultiByteToWideChar failed"));
std::vector<wchar_t> wide(wideLen);
MultiByteToWideChar(cp, 0, data, len, wide.data(), wideLen);
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wide.data(), wideLen, NULL, 0, NULL, NULL);
std::vector<char> out(utf8Len + 1);
WideCharToMultiByte(CP_UTF8, 0, wide.data(), wideLen, out.data(), utf8Len, NULL, NULL);
out[utf8Len] = 0;
return alloc_string(out.data());
}
static value encodeWindows(value str, value charset) {
std::string cs = normalizeCharset(val_string(charset));
int cp = getCodePage(cs);
const char* utf8 = val_string(str);
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
std::vector<wchar_t> wide(wideLen);
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide.data(), wideLen);
int outLen = WideCharToMultiByte(cp, 0, wide.data(), wideLen - 1, NULL, 0, NULL, NULL);
value out = alloc_buffer_len(outLen);
WideCharToMultiByte(cp, 0, wide.data(), wideLen - 1,
buffer_data(out), outLen, NULL, NULL);
return out;
}
#endif
// =============================================================
// Apple (macOS / iOS)
// =============================================================
#ifdef __APPLE__
static CFStringEncoding getCFEncoding(const std::string& cs) {
if (cs == "utf8") return kCFStringEncodingUTF8;
if (cs == "shiftjis") return kCFStringEncodingShiftJIS;
if (cs == "eucjp") return kCFStringEncodingEUC_JP;
if (cs == "gbk") return kCFStringEncodingDOSChineseSimplif;
if (cs == "big5") return kCFStringEncodingBig5;
if (cs == "iso88591" || cs == "latin1") return kCFStringEncodingISOLatin1;
if (cs == "windows1252") return kCFStringEncodingWindowsLatin1;
return kCFStringEncodingUTF8;
}
static value decodeApple(value bytes, value charset) {
buffer buf = val_to_buffer(bytes);
CFStringRef s = CFStringCreateWithBytes(
kCFAllocatorDefault,
(UInt8*)buffer_data(buf),
buffer_size(buf),
getCFEncoding(normalizeCharset(val_string(charset))),
false
);
if (!s) val_throw(alloc_string("CFString decode failed"));
CFIndex maxLen = CFStringGetMaximumSizeForEncoding(
CFStringGetLength(s), kCFStringEncodingUTF8);
std::vector<char> out(maxLen + 1);
CFStringGetCString(s, out.data(), maxLen + 1, kCFStringEncodingUTF8);
CFRelease(s);
return alloc_string(out.data());
}
static value encodeApple(value str, value charset) {
CFStringRef s = CFStringCreateWithCString(
kCFAllocatorDefault,
val_string(str),
kCFStringEncodingUTF8
);
CFIndex used = 0;
CFStringGetBytes(s, CFRangeMake(0, CFStringGetLength(s)),
getCFEncoding(normalizeCharset(val_string(charset))),
'?', false, NULL, 0, &used);
value out = alloc_buffer_len(used);
CFStringGetBytes(s, CFRangeMake(0, CFStringGetLength(s)),
getCFEncoding(normalizeCharset(val_string(charset))),
'?', false,
(UInt8*)buffer_data(out), used, NULL);
CFRelease(s);
return out;
}
#endif
// =============================================================
// iconv (Linux / Android)
// =============================================================
#if !defined(_WIN32) && !defined(__APPLE__)
static std::string iconvCharset(const std::string& cs) {
if (cs == "utf8") return "UTF-8";
if (cs == "shiftjis") return "SHIFT_JIS";
if (cs == "eucjp") return "EUC-JP";
if (cs == "gbk") return "GBK";
if (cs == "big5") return "BIG5";
if (cs == "iso88591" || cs == "latin1") return "ISO-8859-1";
if (cs == "windows1252") return "CP1252";
return "UTF-8";
}
static value decodeIconv(value bytes, value charset) {
buffer buf = val_to_buffer(bytes);
std::string from = iconvCharset(normalizeCharset(val_string(charset)));
iconv_t cd = iconv_open("UTF-8", from.c_str());
if (cd == (iconv_t)-1) val_throw(alloc_string("iconv_open failed"));
char* inPtr = buffer_data(buf);
size_t inLeft = buffer_size(buf);
size_t outSize = inLeft * 4 + 1;
std::vector<char> out(outSize);
char* outPtr = out.data();
size_t outLeft = outSize;
iconv(cd, &inPtr, &inLeft, &outPtr, &outLeft);
iconv_close(cd);
*outPtr = 0;
return alloc_string(out.data());
}
static value encodeIconv(value str, value charset) {
std::string to = iconvCharset(normalizeCharset(val_string(charset)));
iconv_t cd = iconv_open(to.c_str(), "UTF-8");
if (cd == (iconv_t)-1) val_throw(alloc_string("iconv_open failed"));
char* inPtr = (char*)val_string(str);
size_t inLeft = strlen(inPtr);
value out = alloc_buffer_len(inLeft * 4);
char* outPtr = buffer_data(out);
size_t outLeft = inLeft * 4;
iconv(cd, &inPtr, &inLeft, &outPtr, &outLeft);
iconv_close(cd);
buffer_resize(out, (inLeft * 4) - outLeft);
return out;
}
#endif
// =============================================================
// Public API (CFFI)
// =============================================================
value lime_decode_multibyte(value bytes, value charset) {
val_check(bytes, buffer);
val_check(charset, string);
#ifdef _WIN32
return decodeWindows(bytes, charset);
#elif defined(__APPLE__)
return decodeApple(bytes, charset);
#else
return decodeIconv(bytes, charset);
#endif
}
value lime_encode_multibyte(value str, value charset) {
val_check(str, string);
val_check(charset, string);
#ifdef _WIN32
return encodeWindows(str, charset);
#elif defined(__APPLE__)
return encodeApple(str, charset);
#else
return encodeIconv(str, charset);
#endif
}
DEFINE_PRIM(lime_decode_multibyte, 2);
DEFINE_PRIM(lime_encode_multibyte, 2);
} // namespace text
} // namespace lime
and also its header .h :
#ifndef LIME_TEXT_MULTIBYTE_ENCODING_H
#define LIME_TEXT_MULTIBYTE_ENCODING_H
#include <hx/CFFI.h>
namespace lime {
namespace text {
value lime_decode_multibyte(value bytes, value charset);
value lime_encode_multibyte(value str, value charset);
} // namespace text
} // namespace lime
#endif
making a new lime/text/MultiByteEncoding.hx
package lime.text;
import haxe.io.Bytes;
import lime.system.CFFI;
class MultiByteEncoding
{
static var _decode = CFFI.load("lime", "lime_decode_multibyte", 2);
static var _encode = CFFI.load("lime", "lime_encode_multibyte", 2);
public static function decode(bytes:Bytes, charset:String):String
{
return _decode(bytes, charset);
}
public static function encode(text:String, charset:String):Bytes
{
return _encode(text, charset);
}
}
Changes in :
openfl/openfl/blob/develop/src/openfl/desktop/NativeProcess.hx :
from :
public function writeMultiByte(value:String, charSet:String):Void
{
writeUTFBytes(value);
}
To
public function writeMultiByte(value:String, charSet:String):Void
{
if (output == null)
{
throw new Error("Cannot perform operation on a NativeProcess that is not running.", 3212);
}
#if lime
var bytes = lime.text.MultiByteEncoding.encode(value, charSet);
output.writeBytes(bytes, 0, bytes.length);
#else
output.writeString(value);
#end
}
Changes in /src/openfl/utils/ByteArray.hx :
at top
#if lime
import lime.text.MultiByteEncoding;
#end
inside ByteArray all this additions :
@:transitive
abstract ByteArray(ByteArrayData) from ByteArrayData to ByteArrayData
{
public static inline var UTF_8:String = "utf8";
public static inline var UTF_16LE:String = "utf16le";
public static inline var UTF_16BE:String = "utf16be";
public static inline var SHIFT_JIS:String = "shiftjis";
public static inline var GBK:String = "gbk";
public static inline var BIG5:String = "big5";
public static inline var EUC_KR:String = "euckr";
public static inline var ISO_8859_1:String = "iso88591";
public static inline var WINDOWS_1252:String = "windows1252";
.....
}
Inside ByteArrayData :
#if (!display && !flash)
#if !openfl_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
@SuppressWarnings("checkstyle:FieldDocComment")
@:autoBuild(lime._internal.macros.AssetsMacro.embedByteArray())
@:noCompletion @:dox(hide) class ByteArrayData extends Bytes implements IDataInput implements IDataOutput
{
public static var defaultEndian(get, set):Endian;
public static var defaultObjectEncoding:ObjectEncoding = ObjectEncoding.DEFAULT;
@:noCompletion private static var __defaultEndian:Endian = null;
public var bytesAvailable(get, never):UInt;
public var endian(get, set):Endian;
public var objectEncoding:ObjectEncoding;
public var position:Int;
@:noCompletion private var __endian:Endian;
addition of this function :
@:noCompletion private static inline function __normalizeCharset(cs:String):String
{
return cs == null ? "utf8" : cs.toLowerCase().split("-").join("").split("_").join("");
}
and change its readMultiByte to :
public function readMultiByte(length:Int, charSet:String):String
{
if (length <= 0) return "";
if (position + length > __length)
{
throw new EOFError();
}
var cs = __normalizeCharset(charSet);
if (cs == "utf8")
{
return readUTFBytes(length);
}
var bytes = Bytes.alloc(length);
bytes.blit(0, this, position, length);
position += length;
#if lime
return MultiByteEncoding.decode(bytes, cs);
#else
return bytes.toString();
#end
}
and change its writeMultiByte to :
public function writeMultiByte(value:String, charSet:String):Void
{
if (value == null || value.length == 0) return;
var cs = __normalizeCharset(charSet);
if (cs == "utf8")
{
writeUTFBytes(value);
return;
}
#if lime
var bytes = MultiByteEncoding.encode(value, cs);
__resize(position + bytes.length);
blit(position, bytes, 0, bytes.length);
position += bytes.length;
#else
writeUTFBytes(value);
#end
}
and this addition to .xml :
<files id="lime">
<file name="lime/project/src/text/MultiByteEncoding.cpp"/>
</files>
<target id="mac">
<framework name="CoreFoundation"/>
</target>
<target id="linux">
<lib name="iconv"/>
</target>
<target id="android">
<ndklib name="iconv"/>
</target>
Note not tested it yet.
I don’t know much about c++ nor text encoding, can’t comment ![]()
Try it and let us know if it works ?
I’d focus on making an implementation which works in HTML5, first. Does Haxe have better unicode support built-in which would help implement the multiByte methods? Once a clear path is established (without writing custom C++ code), it will help structure the path the C++ code takes.
For example, if the path on other targets uses Haxe built-in objects, then perhaps that resolves support for C++ as well, or otherwise the improvement is a pull request back to HXCPP. If there is no built-in way to support this, then perhaps (worst case) the solution is Haxe code which is portable, which could then have a matching C++ implementation for better performance. The BitmapData class is an example of this, where there are matching implementations written in Haxe and C++, which I tried to approximately match line-by-line, to make debugging and improvements easier to keep them in sync.
OpenFL supports targets like Neko, Hashlink, HTML5, and C++ targets, so although anything is possible in C++ theoretically, it’s less useful for determining architecture
