OpenClaw for HIX

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 44508
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 717 times
Been thanked: 311 times
Contact:

OpenClaw for HIX

Post by Antonio Linares »

chat.prg

Code: Select all | Expand

function main()
    local cHtml := ""
    
cHtml += "<!DOCTYPE html>" cHtml += "<html lang='es'>" cHtml += "<head>" cHtml += " <meta charset='UTF-8'>" cHtml += " <meta name='viewport' content='width=device-width, initial-scale=1.0'>" cHtml += " <title>AIOS 1.0</title>" cHtml += " <link href='https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&family=JetBrains+Mono&display=swap' rel='stylesheet'>" cHtml += " <style>" cHtml += " :root {" cHtml += " --bg: #0a0b10;" cHtml += " --glass: rgba(255, 255, 255, 0.05);" cHtml += " --glass-border: rgba(255, 255, 255, 0.1);" cHtml += " --accent: linear-gradient(135deg, #6366f1, #a855f7);" cHtml += " --text: #e2e8f0;" cHtml += " --bot-bubble: rgba(30, 41, 59, 0.7);" cHtml += " --user-bubble: linear-gradient(135deg, #4f46e5, #7c3aed);" cHtml += " }" cHtml += " * { margin:0; padding:0; box-sizing: border-box; }" cHtml += " body {" cHtml += " font-family: 'Outfit', sans-serif;" cHtml += " background: var(--bg);" cHtml += " background-image: radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%)," cHtml += " radial-gradient(circle at 80% 80%, rgba(168, 85, 247, 0.15) 0%, transparent 40%);" cHtml += " color: var(--text);" cHtml += " height: 100vh;" cHtml += " display: flex;" cHtml += " flex-direction: column;" cHtml += " overflow: hidden;" cHtml += " }" cHtml += " header {" cHtml += " padding: 1.5rem 2rem;" cHtml += " background: var(--glass);" cHtml += " backdrop-filter: blur(10px);" cHtml += " border-bottom: 1px solid var(--glass-border);" cHtml += " display: flex;" cHtml += " justify-content: space-between;" cHtml += " align-items: center;" cHtml += " z-index: 10;" cHtml += " }" cHtml += " header h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.5px; }" cHtml += " .version-badge { background: var(--accent); padding: 0.2rem 0.6rem; border-radius: 20px; font-size: 0.75rem; font-weight: bold; }" cHtml += " #chat-container {" cHtml += " flex: 1;" cHtml += " overflow-y: auto;" cHtml += " padding: 2rem;" cHtml += " display: flex;" cHtml += " flex-direction: column;" cHtml += " gap: 1.5rem;" cHtml += " }" cHtml += " .message {" cHtml += " max-width: 85%;" cHtml += " padding: 1rem 1.25rem;" cHtml += " border-radius: 1.2rem;" cHtml += " line-height: 1.6;" cHtml += " animation: fadeIn 0.3s ease-out;" cHtml += " }" cHtml += " @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }" cHtml += " .user { align-self: flex-end; background: var(--user-bubble); color: white; border-bottom-right-radius: 0.2rem; }" cHtml += " .bot { align-self: flex-start; background: var(--bot-bubble); backdrop-filter: blur(5px); border: 1px solid var(--glass-border); border-bottom-left-radius: 0.2rem; }" cHtml += " .error { align-self: center; background: rgba(255,0,0,0.2); color: #ff8888; border: 1px solid #ff0000; padding: 0.5rem 1rem; border-radius: 10px; }" cHtml += " pre { background: #1e1e1e; padding: 1rem; border-radius: 8px; overflow-x: auto; font-family: 'JetBrains Mono', monospace; }" cHtml += " .input-wrapper { padding: 2rem; background: linear-gradient(to top, var(--bg), transparent); }" cHtml += " .input-box {" cHtml += " max-width: 900px; margin: 0 auto; background: var(--glass); backdrop-filter: blur(20px); border: 1px solid var(--glass-border);" cHtml += " border-radius: 1.5rem; padding: 0.5rem; display: flex; align-items: center; gap: 0.5rem;" cHtml += " }" cHtml += " input { flex: 1; background: transparent; border: none; color: white; padding: 0.75rem 1rem; font-size: 1rem; outline: none; }" cHtml += " button { background: var(--accent); border: none; color: white; padding: 0.75rem 1.5rem; border-radius: 1.1rem; font-weight: 600; cursor: pointer; }" cHtml += " button:disabled { background: #444; }" cHtml += " .typing { display: flex; gap: 4px; padding: 10px; }" cHtml += " .dot { width: 8px; height: 8px; background: #6366f1; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; }" cHtml += " @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } }" cHtml += " </style>" cHtml += "</head>" cHtml += "<body>" cHtml += "<header>" cHtml += " <h1>AIOS</h1>" cHtml += " <button onclick='clearChat()'>Limpiar</button>" cHtml += "</header>" cHtml += "<div id='chat-container'></div>" cHtml += "<div class='input-wrapper'>" cHtml += " <div class='input-box'>" cHtml += " <input type='text' id='input' placeholder='Pregunta algo...' onkeydown='if(event.keyCode===13) sendMessage()'>" cHtml += " <button id='send-btn' onclick='sendMessage()'>Enviar</button>" cHtml += " </div>" cHtml += "</div>" cHtml += "<script>" cHtml += " window.onerror = function(m, u, l) { alert('Error: ' + m + ' en ' + l); };" cHtml += " var history_data = [];" cHtml += " function addMessage(role, text) {" cHtml += " var container = document.getElementById('chat-container');" cHtml += " var div = document.createElement('div');" cHtml += " div.className = 'message ' + role;" cHtml += " div.innerHTML = text.split('\\n').join('<br>');" cHtml += " container.appendChild(div);" cHtml += " setTimeout(function() { container.scrollTop = container.scrollHeight; }, 50);" cHtml += " }" cHtml += " function addTyping() {" cHtml += " var container = document.getElementById('chat-container');" cHtml += " var div = document.createElement('div');" cHtml += " div.id = 'typing-indicator';" cHtml += " div.className = 'message bot typing';" cHtml += " div.innerHTML = 'ÔÜÖ´©Å Pensando...';" cHtml += " container.appendChild(div);" cHtml += " container.scrollTop = container.scrollHeight;" cHtml += " }" cHtml += " function removeTyping() {" cHtml += " var indicator = document.getElementById('typing-indicator');" cHtml += " if(indicator) indicator.parentNode.removeChild(indicator);" cHtml += " }" cHtml += " function sendMessage() {" cHtml += " var input = document.getElementById('input');" cHtml += " var text = input.value.trim();" cHtml += " var btn = document.getElementById('send-btn');" cHtml += " if (!text) return;" cHtml += " addMessage('user', text);" cHtml += " input.value = '';" cHtml += " addTyping();" cHtml += " btn.disabled = true;" cHtml += " var params = 'query=' + encodeURIComponent(text);" cHtml += " if (history_data.length > 0) params += '&history=' + encodeURIComponent(JSON.stringify(history_data));" cHtml += " console.log('SENDING:', params);" cHtml += " var xhr = new XMLHttpRequest();" cHtml += " xhr.open('POST', 'aios.prg', true);" cHtml += " xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');" cHtml += " xhr.onreadystatechange = function() {" cHtml += " if (xhr.status !== 200 && xhr.status !== 0) { addMessage('error', 'Status: ' + xhr.status); }" cHtml += " if (xhr.readyState == 4) {" cHtml += " removeTyping(); btn.disabled = false;" cHtml += " if (xhr.status == 200) {" cHtml += " try {" cHtml += " var data = JSON.parse(xhr.responseText);" cHtml += " if (data.success) {" cHtml += " addMessage('bot', data.text);" cHtml += " history_data.push({role:'user', parts:[{text:text}]});" cHtml += " history_data.push({role:'model', parts:[{text:data.text}]});" cHtml += " } else { addMessage('error', 'Error: ' + data.error); }" cHtml += " } catch(e) { addMessage('error', 'JSON Error: ' + xhr.responseText.substring(0,100)); }" cHtml += " } else if(xhr.status !== 0) { addMessage('error', 'Server Error: ' + xhr.status); }" cHtml += " }" cHtml += " };" cHtml += " xhr.send(params);" cHtml += " }" cHtml += " function clearChat() {" cHtml += " document.getElementById('chat-container').innerHTML = '';" cHtml += " history_data = [];" cHtml += " }" cHtml += "</script>" cHtml += "</body></html>" UWrite(cHtml) return ""

aios.prg

Code: Select all | Expand

// gemini_aios_v13.prg - ULTRA STABLE VERSION 1.7
// Final Robust Memory + Forced Context Awareness + hbcurl

#include "hbcurl.ch"

function main()
    local cQuery, cModel, cHistory, cSystemPrompt := "", cJsonRes
    local hResult := { "success" => .f. }, oErr, cOldMem, cSep, cMemPath, cBaseDir
    
cBaseDir := hb_DirBase() cSep := "\" ; if !("\" $ cBaseDir) ; cSep := "/" ; endif cQuery := UGet("query") cModel := UGet("model") cHistory := UGet("history") if Empty(cQuery) ; cQuery := UPost("query") ; endif if Empty(cModel) ; cModel := UPost("model") ; endif if Empty(cHistory) ; cHistory := UPost("history") ; endif // Clean data if !Empty(cHistory) .and. "%" $ hb_ValToStr(cHistory) ; cHistory := Hix_UrlDecode(hb_ValToStr(cHistory)) ; endif if !Empty(cQuery) .and. "%" $ hb_ValToStr(cQuery) ; cQuery := Hix_UrlDecode(hb_ValToStr(cQuery)) ; endif if cQuery == "ping" ; UWrite('{"success":true, "text":"pong"}') ; return "" ; endif if Empty(cQuery) ; UWrite('{"success":false, "error":"No query provided"}') ; return "" ; endif cJsonRes := '{"success":false, "error":"Fatal Crash"}' BEGIN SEQUENCE WITH {|e| break(e)} if Empty(cModel) ; cModel := "gemini-2.0-flash" ; endif // System Prompt - FORCED PERSONALITY cSystemPrompt := "ERES HIX AIOS v1.7. ASISTENTE AVANZADO." + (Chr(13)+Chr(10)) cSystemPrompt += "TIENES ACCESO A MEMORIA PERSISTENTE QUE SE TE PROPORCIONA A CONTINUACIÓN." + (Chr(13)+Chr(10)) cSystemPrompt += "PROHIBIDO DECIR QUE NO TIENES MEMORIA." + (Chr(13)+Chr(10)) // Load Identity & Soul if File(cBaseDir + "persona" + cSep + "IDENTITY.md") cSystemPrompt += "TU IDENTIDAD:" + (Chr(13)+Chr(10)) + hb_MemoRead(cBaseDir + "persona" + cSep + "IDENTITY.md") + (Chr(13)+Chr(10)) endif if File(cBaseDir + "persona" + cSep + "SOUL.md") cSystemPrompt += "TU ALMA:" + (Chr(13)+Chr(10)) + hb_MemoRead(cBaseDir + "persona" + cSep + "SOUL.md") + (Chr(13)+Chr(10)) endif // Load Memory (Last 8000 chars) cMemPath := cBaseDir + "persona" + cSep + "MEMORY.md" if File(cMemPath) cOldMem := hb_MemoRead(cMemPath) cSystemPrompt += "HISTORIAL DE CONVERSACIONES (MEMORIA):" + (Chr(13)+Chr(10)) + Right(cOldMem, 8000) + (Chr(13)+Chr(10)) endif cSystemPrompt += "Si necesitas un chat_id para Telegram, BÚSCALO EN EL HISTORIAL ARRIBA." + (Chr(13)+Chr(10)) LogTrace("REQ: " + cQuery + " | PromptLen: " + AllTrim(Str(Len(cSystemPrompt)))) hResult := ExecuteReasoningLoop(cQuery, cModel, hb_ValToStr(cHistory), cSystemPrompt) // Save to Memory if ValType(hResult) == "H" .and. hb_HGetDef(hResult, 'success', .f.) if !hb_DirExists(cBaseDir + "persona") ; hb_DirCreate(cBaseDir + "persona") ; endif cOldMem := "" ; if File(cMemPath) ; cOldMem := hb_MemoRead(cMemPath) ; endif hb_MemoWrit(cMemPath, cOldMem + "[" + Time() + "] User: " + cQuery + (Chr(13)+Chr(10)) + "HIX: " + hb_ValToStr(hResult['text']) + (Chr(13)+Chr(10)) + (Chr(13)+Chr(10))) endif hResult["v"] := "1.7" cJsonRes := hb_jsonEncode(hResult) RECOVER USING oErr cJsonRes := '{"success":false, "error":"RTE: ' + hb_ValToStr(oErr:Description) + '", "v":"1.7-err"}' END UWrite(cJsonRes) return "" function LogTrace(cMsg) local nH nH := fOpen("aios_debug.log", 1) if nH < 0 ; nH := fCreate("aios_debug.log") ; endif if nH > 0 fSeek(nH, 0, 2) fWrite(nH, "[" + DToC(Date()) + " " + Time() + "] v1.7: " + hb_ValToStr(cMsg) + (Chr(13)+Chr(10))) fClose(nH) endif return nil function ExecuteReasoningLoop(cQuery, cModel, cHistory, cSystemPrompt) local aFunctions := BuildAiosFunctions() local aMessages := {}, nStep := 0, hResult := { "success" => .f. }, hGeminiResult, hSkillResult, hMsg, aResponseParts, hPart, lHasFC local cFullText := ""
if !Empty(cHistory) ; hb_jsonDecode(cHistory, @aMessages) ; endif if ValType(aMessages) != "A" ; aMessages := {} ; endif aAdd(aMessages, { "role" => "user", "parts" => { { "text" => cQuery } } }) do while nStep < 10 // Increase steps for safety nStep++ hGeminiResult := GeminiCallFC(aMessages, aFunctions, cModel, cSystemPrompt) if ValType(hGeminiResult) != "H" .or. !hb_HGetDef(hGeminiResult,"success",.f.) if nStep > 1 .and. !Empty(cFullText) // If it fails after Turn 1 but we have text, return it hResult['success'] := .t. ; hResult['text'] := cFullText ; hResult['model_used'] := cModel ; retu hResult endif hResult['success'] := .f. ; hResult['error'] := hb_ValToStr(hb_HGetDef(hGeminiResult, "error", "API Fail")) LogTrace("LOOP ERR: " + hResult['error']) retu hResult endif hMsg := { "role" => "model", "parts" => hGeminiResult["raw_parts"] } aAdd(aMessages, hMsg) // Accumulate text from this turn if !Empty(hGeminiResult['text']) cFullText += hGeminiResult['text'] endif if hGeminiResult['type'] == "function_call" aResponseParts := {} for each hPart in hGeminiResult["raw_parts"] if ValType(hPart) == "H" .and. hb_HHasKey(hPart, "functionCall") hSkillResult := ExecuteAiosSkill(hPart["functionCall"]["name"], hPart["functionCall"]["args"]) aAdd(aResponseParts, { "functionResponse" => { "name" => hPart["functionCall"]["name"], "response" => hSkillResult } } ) endif next aAdd(aMessages, { "role" => "function", "parts" => aResponseParts }) elseif hGeminiResult['type'] == "text" hResult['success'] := .t. ; hResult['text'] := cFullText ; hResult['model_used'] := cModel ; retu hResult else // Type is 'empty' or unknown, but if we have text, we are done hResult['success'] := .t. ; hResult['text'] := iif(Empty(cFullText), "OK", cFullText) ; hResult['model_used'] := cModel ; retu hResult endif enddo return hResult function GeminiCallFC(aMessages, aFunctions, cModel, cSystemPrompt) local cApiKey := GetAiosApiKey() local cUrl, cJson, hCurl, nError, cResponse := "", hResult := { "success" => .f. } local hPayload := { "contents" => aMessages, "tools" => { { "function_declarations" => aFunctions } } }
if !Empty(cSystemPrompt) ; hPayload["system_instruction"] := { "parts" => { { "text" => cSystemPrompt } } } ; endif cUrl := "https://generativelanguage.googleapis.com/v1beta/models/" + cModel + ":generateContent?key=" + cApiKey cJson := hb_jsonEncode(hPayload) hCurl := curl_easy_init() if !Empty(hCurl) curl_easy_setopt(hCurl, HB_CURLOPT_POST, .T.) curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl) curl_easy_setopt(hCurl, HB_CURLOPT_HTTPHEADER, { "Content-Type: application/json" }) curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.) curl_easy_setopt(hCurl, HB_CURLOPT_POSTFIELDS, cJson) curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP) nError := curl_easy_perform(hCurl) if nError == HB_CURLE_OK cResponse := curl_easy_dl_buff_get(hCurl) LogTrace("RECV: " + Left(cResponse, 500) + " (len: " + AllTrim(Str(Len(cResponse))) + ")") hResult := ParseAiosFCResponse(cResponse) if !hResult['success'] ; LogTrace("PARSE ERR: " + hb_ValToStr(hResult['error'])) ; endif else hResult['error'] := "Curl error: " + hb_ValToStr(nError) LogTrace("CURL ERR: " + AllTrim(Str(nError))) endif curl_easy_cleanup(hCurl) else hResult['error'] := "Curl init failed" endif retu hResult function ParseAiosFCResponse(cJSON) local hResult := { "success" => .f. }, hResponse := {=>}, aParts, nErr := 0, cW, nS, nE, hPart, lHasFC cW := AllTrim(StrTran(hb_ValToStr(cJSON), Chr(0), "")) nS := At("{", cW) ; nE := RAt("}", cW) if nS > 0 .and. nE > nS ; cW := SubStr(cW, nS, nE - nS + 1) ; endif if Empty(cW) ; hResult['error'] := "Empty response" ; return hResult ; endif nErr := hb_jsonDecode(cW, @hResponse) if (nErr == 0 .or. nErr == Len(cW)) .and. ValType(hResponse) == "H" if hb_HHasKey(hResponse, "candidates") .and. Len(hResponse["candidates"]) > 0 aParts := hResponse["candidates"][1]["content"]["parts"] hResult['success'] := .t. ; hResult['raw_parts'] := aParts ; lHasFC := .f. hResult['text'] := "" hResult['type'] := 'unknown'
for each hPart in aParts if ValType(hPart) == "H" if hb_HHasKey(hPart, "functionCall") ; lHasFC := .t. ; endif if hb_HHasKey(hPart, "text") ; hResult['text'] += hPart["text"] ; endif endif next if lHasFC hResult['type'] := 'function_call' elseif !Empty(hResult['text']) hResult['type'] := 'text' endif elseif hb_HHasKey(hResponse, "error") hResult['error'] := "API Error: " + hb_ValToStr(hResponse["error"]["message"]) elseif hb_HHasKey(hResponse, "usageMetadata") // Valid response but empty of content (happens after tool turns sometimes) hResult['success'] := .t. ; hResult['type'] := 'empty' ; hResult['raw_parts'] := {} ; hResult['text'] := "" else hResult['error'] := "Invalid API Response Structure" endif else hResult['error'] := "JSON Parse Error (Code: " + AllTrim(Str(nErr)) + ")" endif retu hResult function BuildAiosFunctions() local aFuncs := {} aAdd(aFuncs, { "name" => "filesystem_search", "description" => "Search files.", "parameters" => { "type" => "object", "properties" => { "pattern" => { "type" => "string" }, "path" => { "type" => "string" } }, "required" => {"pattern", "path"} } }) aAdd(aFuncs, { "name" => "identity_get_context", "description" => "Identity info.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } }) aAdd(aFuncs, { "name" => "telegram_send_message", "description" => "Send Telegram Msg.", "parameters" => { "type" => "object", "properties" => { "chat_id" => { "type" => "string" }, "text" => { "type" => "string" } }, "required" => {"chat_id", "text"} } }) aAdd(aFuncs, { "name" => "telegram_get_updates", "description" => "Read incoming Telegram messages.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } }) aAdd(aFuncs, { "name" => "config_set", "description" => "Save key-value pair.", "parameters" => { "type" => "object", "properties" => { "key" => { "type" => "string" }, "value" => { "type" => "string" } }, "required" => {"key", "value"} } }) aAdd(aFuncs, { "name" => "config_get", "description" => "Get saved info.", "parameters" => { "type" => "object", "properties" => { "key" => { "type" => "string", "description" => "Optional key" } }, "required" => {} } }) aAdd(aFuncs, { "name" => "memory_summarize", "description" => "Optimize/Summarize conversation history.", "parameters" => { "type" => "object", "properties" => { "summary" => { "type" => "string" } }, "required" => {"summary"} } }) aAdd(aFuncs, { "name" => "filesystem_get_datetime", "description" => "Get current date, time and day of week.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } }) aAdd(aFuncs, { "name" => "identity_update", "description" => "Update IDENTITY.md content.", "parameters" => { "type" => "object", "properties" => { "content" => { "type" => "string" } }, "required" => {"content"} } }) aAdd(aFuncs, { "name" => "soul_update", "description" => "Update SOUL.md content.", "parameters" => { "type" => "object", "properties" => { "content" => { "type" => "string" } }, "required" => {"content"} } }) aAdd(aFuncs, { "name" => "cron_add_reminder", "description" => "Schedule a reminder message.", "parameters" => { "type" => "object", "properties" => { "message" => { "type" => "string" }, "minutes" => { "type" => "number" }, "chat_id" => { "type" => "string" } }, "required" => {"message", "minutes"} } }) retu aFuncs function ExecuteAiosSkill(cName, hArgs) local hRes := { "success" => .f. } do case case cName == 'filesystem_search' ; hRes := Aios_FileSystem(cName, hArgs) case cName == 'identity_get_context' ; hRes := Aios_Identity() case cName == 'telegram_send_message' ; hRes := Aios_Telegram(cName, hArgs) case cName == 'telegram_get_updates' ; hRes := Aios_Telegram(cName, hArgs) case cName == 'config_set' ; hRes := Aios_Config('config_set', hArgs) case cName == 'config_get' ; hRes := Aios_Config('config_get', hArgs) case cName == 'memory_summarize' ; hRes := Aios_MemorySummarize(hArgs) case cName == 'filesystem_get_datetime' ; hRes := Aios_FileSystem(cName, hArgs) case cName == 'identity_update' ; hRes := Aios_PersonaUpdate('identity', hArgs) case cName == 'soul_update' ; hRes := Aios_PersonaUpdate('soul', hArgs) case cName == 'cron_add_reminder' ; hRes := Aios_Cron(cName, hArgs) endcase retu hRes #include "skills\identity\identity.prg" #include "skills\filesystem\hix_filesystem.prg" #include "skills\telegram\telegram.prg" #include "skills\config\config_skill.prg" #include "skills\cron\cron_skill.prg" function GetAiosApiKey() local cKey := GetEnv("GEMINI_API_KEY"), hCfg := {=>} if Empty(cKey) .and. File("gemini_config.json") hb_jsonDecode(hb_MemoRead("gemini_config.json"), @hCfg) if ValType(hCfg) == "H" ; cKey := hb_HGetDef(hCfg, "api_key", "") ; endif endif retu cKey function Hix_UrlDecode( cT ) local cR := "", n := 1, cX if Empty(cT) ; return "" ; endif cT := StrTran( cT, "+", " " ) do while n <= Len( cT ) if SubStr( cT, n, 1 ) == "%" cX := SubStr( cT, n + 1, 2 ) ; cR += Chr( Hix_HexToNum( cX ) ) ; n += 3 else ; cR += SubStr( cT, n, 1 ) ; n++ ; endif enddo retu cR function Hix_HexToNum( cX ) local n := 0, i, cC, nV cX := Upper( cX ) for i := 1 to Len( cX ) cC := SubStr( cX, i, 1 ) ; nV := At( cC, "0123456789ABCDEF" ) - 1 if nV >= 0 ; n := n * 16 + nV ; endif next retu n
regards, saludos

Antonio Linares
www.fivetechsoft.com
Post Reply