mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-07-12 15:36:32 +02:00
added alphabet tool, updated packages, a few transform fixes
This commit is contained in:
@@ -1,97 +1,137 @@
|
||||
// base122 encoding (more efficient than Base64)
|
||||
// Base122 encoding (Kevin Albs) — UTF-8 binary-to-text, ~14% smaller than Base64
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Base122',
|
||||
priority: 250,
|
||||
category: 'encoding',
|
||||
func: function(text) {
|
||||
// Base122 uses UTF-8 bytes and encodes them more efficiently
|
||||
// It uses 7-bit ASCII (0-127) plus some safe 2-byte UTF-8 sequences
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
let result = '';
|
||||
|
||||
let i = 0;
|
||||
while (i < bytes.length) {
|
||||
const byte = bytes[i];
|
||||
|
||||
if (byte < 128) {
|
||||
// Single byte ASCII
|
||||
result += String.fromCharCode(byte);
|
||||
i++;
|
||||
} else if (i + 1 < bytes.length) {
|
||||
// Try to encode as 2-byte sequence
|
||||
const b1 = byte;
|
||||
const b2 = bytes[i + 1];
|
||||
|
||||
// Check if it's a valid 2-byte UTF-8 sequence
|
||||
if ((b1 & 0xE0) === 0xC0 && (b2 & 0xC0) === 0x80) {
|
||||
result += String.fromCharCode(b1, b2);
|
||||
i += 2;
|
||||
} else {
|
||||
// Fallback: encode as escaped sequence
|
||||
result += String.fromCharCode(0xC2, 0x80 + (byte - 128));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
// Last byte, encode as escaped
|
||||
result += String.fromCharCode(0xC2, 0x80 + (byte - 128));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
reverse: function(text) {
|
||||
const bytes = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < text.length) {
|
||||
const code = text.charCodeAt(i);
|
||||
|
||||
if (code < 128) {
|
||||
bytes.push(code);
|
||||
i++;
|
||||
} else if (i + 1 < text.length) {
|
||||
// Check for 2-byte sequence
|
||||
const b1 = code;
|
||||
const b2 = text.charCodeAt(i + 1);
|
||||
|
||||
if ((b1 & 0xE0) === 0xC0 && (b2 & 0xC0) === 0x80) {
|
||||
// Extract original byte from escaped sequence
|
||||
if (b1 === 0xC2 && b2 >= 0x80 && b2 < 0xC0) {
|
||||
bytes.push(b2 - 0x80);
|
||||
} else {
|
||||
bytes.push(b1, b2);
|
||||
}
|
||||
i += 2;
|
||||
} else {
|
||||
bytes.push(code);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
bytes.push(code);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return new TextDecoder().decode(new Uint8Array(bytes));
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[base122]';
|
||||
const result = this.func(text.slice(0, 10));
|
||||
return result.substring(0, 15) + '...';
|
||||
},
|
||||
detector: function(text) {
|
||||
// Base122 produces text that's mostly ASCII with some UTF-8 sequences
|
||||
// Hard to detect reliably, but check for mix of ASCII and UTF-8
|
||||
const hasAscii = /[\x00-\x7F]/.test(text);
|
||||
const hasUtf8 = /[\xC0-\xFF]/.test(text);
|
||||
return hasAscii && text.length >= 8;
|
||||
}
|
||||
});
|
||||
export default (function() {
|
||||
const kIllegals = [
|
||||
0, 10, 13, 34, 38, 92
|
||||
];
|
||||
const kShortened = 0b111;
|
||||
|
||||
function encodeBase122Bytes(bytes) {
|
||||
let curIndex = 0;
|
||||
let curBit = 0;
|
||||
const outData = [];
|
||||
|
||||
function get7() {
|
||||
if (curIndex >= bytes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstByte = bytes[curIndex];
|
||||
let firstPart = ((0b11111110 >>> curBit) & firstByte) << curBit;
|
||||
firstPart >>= 1;
|
||||
curBit += 7;
|
||||
|
||||
if (curBit < 8) {
|
||||
return firstPart;
|
||||
}
|
||||
|
||||
curBit -= 8;
|
||||
curIndex++;
|
||||
|
||||
if (curIndex >= bytes.length) {
|
||||
return firstPart;
|
||||
}
|
||||
|
||||
const secondByte = bytes[curIndex];
|
||||
let secondPart = ((0xFF00 >>> curBit) & secondByte) & 0xFF;
|
||||
secondPart >>= 8 - curBit;
|
||||
return firstPart | secondPart;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const bits = get7();
|
||||
if (bits === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
const illegalIndex = kIllegals.indexOf(bits);
|
||||
if (illegalIndex !== -1) {
|
||||
let nextBits = get7();
|
||||
let b1 = 0b11000010;
|
||||
let b2 = 0b10000000;
|
||||
|
||||
if (nextBits === false) {
|
||||
b1 |= (kShortened & 0b111) << 2;
|
||||
nextBits = bits;
|
||||
} else {
|
||||
b1 |= (illegalIndex & 0b111) << 2;
|
||||
}
|
||||
|
||||
const firstBit = (nextBits & 0b01000000) > 0 ? 1 : 0;
|
||||
b1 |= firstBit;
|
||||
b2 |= nextBits & 0b00111111;
|
||||
outData.push(b1, b2);
|
||||
} else {
|
||||
outData.push(bits);
|
||||
}
|
||||
}
|
||||
|
||||
return new TextDecoder('utf-8').decode(new Uint8Array(outData));
|
||||
}
|
||||
|
||||
function decodeBase122String(strData) {
|
||||
const decoded = [];
|
||||
let curByte = 0;
|
||||
let bitOfByte = 0;
|
||||
|
||||
function push7(byte) {
|
||||
byte <<= 1;
|
||||
curByte |= (byte >>> bitOfByte);
|
||||
bitOfByte += 7;
|
||||
|
||||
if (bitOfByte >= 8) {
|
||||
decoded.push(curByte);
|
||||
bitOfByte -= 8;
|
||||
curByte = (byte << (7 - bitOfByte)) & 255;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < strData.length; i++) {
|
||||
const c = strData.charCodeAt(i);
|
||||
|
||||
if (c > 127) {
|
||||
const illegalIndex = (c >>> 8) & 7;
|
||||
if (illegalIndex !== kShortened) {
|
||||
push7(kIllegals[illegalIndex]);
|
||||
}
|
||||
push7(c & 127);
|
||||
} else {
|
||||
push7(c);
|
||||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(decoded);
|
||||
}
|
||||
|
||||
return new BaseTransformer({
|
||||
name: 'Base122',
|
||||
priority: 250,
|
||||
category: 'encoding',
|
||||
func: function(text) {
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
return encodeBase122Bytes(bytes);
|
||||
},
|
||||
reverse: function(text) {
|
||||
try {
|
||||
const bytes = decodeBase122String(text);
|
||||
return new TextDecoder().decode(bytes);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) {
|
||||
return '[base122]';
|
||||
}
|
||||
const result = this.func(text.slice(0, 10));
|
||||
return result.substring(0, 15) + (result.length > 15 ? '...' : '');
|
||||
},
|
||||
detector: function(text) {
|
||||
if (!text || text.length < 4) {
|
||||
return false;
|
||||
}
|
||||
return /[\u0080-\uFFFF]/.test(text) || (text.length >= 8 && text !== text.trim());
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
+194
-148
@@ -1,151 +1,197 @@
|
||||
// baudot code / ITA2 encoding (teletype code)
|
||||
// Baudot / ITA2 telegraph code — 5-bit letters/figures with shift codes
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Baudot Code (ITA2)',
|
||||
priority: 250,
|
||||
category: 'encoding',
|
||||
// Baudot/ITA2 5-bit code (letters and figures shift)
|
||||
letters: {
|
||||
0b00000: ' ', // NULL/blank
|
||||
0b00010: 'E',
|
||||
0b00011: '\n', // Line feed
|
||||
0b00100: 'A',
|
||||
0b00101: ' ',
|
||||
0b00110: 'S',
|
||||
0b00111: 'I',
|
||||
0b01000: 'U',
|
||||
0b01001: '\r', // Carriage return
|
||||
0b01010: 'D',
|
||||
0b01011: 'R',
|
||||
0b01100: 'J',
|
||||
0b01101: 'N',
|
||||
0b01110: 'F',
|
||||
0b01111: 'C',
|
||||
0b10000: 'K',
|
||||
0b10001: 'T',
|
||||
0b10010: 'Z',
|
||||
0b10011: 'L',
|
||||
0b10100: 'W',
|
||||
0b10101: 'H',
|
||||
0b10110: 'Y',
|
||||
0b10111: 'P',
|
||||
0b11000: 'Q',
|
||||
0b11001: 'O',
|
||||
0b11010: 'B',
|
||||
0b11011: 'G',
|
||||
0b11100: 'Figures', // Shift to figures
|
||||
0b11101: 'M',
|
||||
0b11110: 'X',
|
||||
0b11111: 'V',
|
||||
},
|
||||
figures: {
|
||||
0b00000: ' ',
|
||||
0b00010: '3',
|
||||
0b00011: '\n',
|
||||
0b00100: '-',
|
||||
0b00101: ' ',
|
||||
0b00110: '\'',
|
||||
0b00111: '8',
|
||||
0b01000: '7',
|
||||
0b01001: '\r',
|
||||
0b01010: '\u0005', // ENQ
|
||||
0b01011: '4',
|
||||
0b01100: '\'', // Bell
|
||||
0b01101: ',',
|
||||
0b01110: '!',
|
||||
0b01111: ':',
|
||||
0b10000: '(',
|
||||
0b10001: '5',
|
||||
0b10010: '+',
|
||||
0b10011: ')',
|
||||
0b10100: '2',
|
||||
0b10101: '$',
|
||||
0b10110: '6',
|
||||
0b10111: '0',
|
||||
0b11000: '1',
|
||||
0b11001: '9',
|
||||
0b11010: '?',
|
||||
0b11011: '&',
|
||||
0b11100: 'Letters', // Shift to letters
|
||||
0b11101: '.',
|
||||
0b11110: '/',
|
||||
0b11111: '=',
|
||||
},
|
||||
func: function(text) {
|
||||
// Create reverse maps
|
||||
const lettersToCode = {};
|
||||
const figuresToCode = {};
|
||||
for (const [code, char] of Object.entries(this.letters)) {
|
||||
if (char !== 'Figures' && char !== 'Letters') {
|
||||
lettersToCode[char] = parseInt(code);
|
||||
}
|
||||
}
|
||||
for (const [code, char] of Object.entries(this.figures)) {
|
||||
if (char !== 'Figures' && char !== 'Letters') {
|
||||
figuresToCode[char] = parseInt(code);
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
let inFigures = false;
|
||||
|
||||
for (const char of text.toUpperCase()) {
|
||||
// Check if we need to shift
|
||||
const isFigure = /[0-9\-'():!$?&.\/+=]/.test(char);
|
||||
|
||||
if (isFigure && !inFigures) {
|
||||
result += String.fromCharCode(0b11100); // Figures shift
|
||||
inFigures = true;
|
||||
} else if (!isFigure && inFigures) {
|
||||
result += String.fromCharCode(0b11111); // Letters shift (approximate)
|
||||
inFigures = false;
|
||||
}
|
||||
|
||||
// Encode character
|
||||
const code = inFigures ? figuresToCode[char] : lettersToCode[char];
|
||||
if (code !== undefined) {
|
||||
result += String.fromCharCode(code);
|
||||
} else {
|
||||
result += char; // Keep unmapped
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
reverse: function(text) {
|
||||
let result = '';
|
||||
let inFigures = false;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const code = text.charCodeAt(i) & 0x1F; // 5 bits
|
||||
|
||||
if (code === 0b11100) {
|
||||
inFigures = true;
|
||||
continue;
|
||||
} else if (code === 0b11111) {
|
||||
inFigures = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const map = inFigures ? this.figures : this.letters;
|
||||
const char = map[code];
|
||||
if (char && char !== 'Figures' && char !== 'Letters') {
|
||||
result += char;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[baudot]';
|
||||
return this.func(text.slice(0, 5));
|
||||
},
|
||||
detector: function(text) {
|
||||
// Baudot uses 5-bit codes (0-31)
|
||||
// Check for characters in the 5-bit range
|
||||
const has5Bit = /[\x00-\x1F]/.test(text);
|
||||
return has5Bit && text.length >= 5;
|
||||
}
|
||||
});
|
||||
export default (function() {
|
||||
const FIGS = 0b11011;
|
||||
const LTRS = 0b11111;
|
||||
|
||||
const LETTERS = {
|
||||
'A': 0b00100,
|
||||
'B': 0b11010,
|
||||
'C': 0b01111,
|
||||
'D': 0b01010,
|
||||
'E': 0b00010,
|
||||
'F': 0b01110,
|
||||
'G': 0b00001,
|
||||
'H': 0b10101,
|
||||
'I': 0b00111,
|
||||
'J': 0b01100,
|
||||
'K': 0b10000,
|
||||
'L': 0b10011,
|
||||
'M': 0b11101,
|
||||
'N': 0b01101,
|
||||
'O': 0b11001,
|
||||
'P': 0b10111,
|
||||
'Q': 0b11000,
|
||||
'R': 0b01011,
|
||||
'S': 0b00110,
|
||||
'T': 0b10001,
|
||||
'U': 0b01000,
|
||||
'V': 0b11100,
|
||||
'W': 0b10100,
|
||||
'X': 0b11110,
|
||||
'Y': 0b10110,
|
||||
'Z': 0b10010
|
||||
};
|
||||
|
||||
const FIGURES = {
|
||||
'-': 0b00100,
|
||||
'?': 0b11010,
|
||||
':': 0b01111,
|
||||
'$': 0b01010,
|
||||
'3': 0b00010,
|
||||
'!': 0b01110,
|
||||
'&': 0b11011,
|
||||
'8': 0b00111,
|
||||
'7': 0b01000,
|
||||
'4': 0b01011,
|
||||
',': 0b01101,
|
||||
'(': 0b10000,
|
||||
')': 0b10011,
|
||||
'.': 0b11101,
|
||||
'0': 0b10111,
|
||||
'1': 0b11000,
|
||||
'9': 0b11001,
|
||||
'5': 0b10001,
|
||||
'+': 0b10010,
|
||||
'2': 0b10100,
|
||||
'6': 0b10110,
|
||||
'/': 0b11110
|
||||
};
|
||||
|
||||
const LETTER_BY_CODE = {};
|
||||
const FIGURE_BY_CODE = {};
|
||||
|
||||
for (const [char, code] of Object.entries(LETTERS)) {
|
||||
LETTER_BY_CODE[code] = char;
|
||||
}
|
||||
for (const [char, code] of Object.entries(FIGURES)) {
|
||||
if (char !== '&') {
|
||||
FIGURE_BY_CODE[code] = char;
|
||||
}
|
||||
}
|
||||
FIGURE_BY_CODE[FIGS] = '&';
|
||||
|
||||
function isFigureChar(char) {
|
||||
return Object.prototype.hasOwnProperty.call(FIGURES, char);
|
||||
}
|
||||
|
||||
function isLetterChar(char) {
|
||||
return Object.prototype.hasOwnProperty.call(LETTERS, char);
|
||||
}
|
||||
|
||||
function codesToDisplay(codes) {
|
||||
return codes.map(code => code.toString(2).padStart(5, '0')).join(' ');
|
||||
}
|
||||
|
||||
function parseDisplayCodes(text) {
|
||||
return text.trim().split(/\s+/).map(token => {
|
||||
if (!/^[01]{5}$/.test(token)) {
|
||||
return null;
|
||||
}
|
||||
return parseInt(token, 2);
|
||||
}).filter(code => code !== null);
|
||||
}
|
||||
|
||||
return new BaseTransformer({
|
||||
name: 'Baudot Code (ITA2)',
|
||||
priority: 250,
|
||||
category: 'encoding',
|
||||
func: function(text) {
|
||||
const upper = text.toUpperCase();
|
||||
const codes = [];
|
||||
let inFigures = false;
|
||||
|
||||
for (const char of upper) {
|
||||
if (char === '\n') {
|
||||
codes.push(0b00011);
|
||||
continue;
|
||||
}
|
||||
if (char === '\r') {
|
||||
codes.push(0b01001);
|
||||
continue;
|
||||
}
|
||||
if (char === ' ') {
|
||||
codes.push(0b00101);
|
||||
continue;
|
||||
}
|
||||
|
||||
const wantsFigure = isFigureChar(char);
|
||||
const wantsLetter = isLetterChar(char);
|
||||
|
||||
if (wantsFigure && !inFigures) {
|
||||
codes.push(FIGS);
|
||||
inFigures = true;
|
||||
} else if (wantsLetter && inFigures && char !== '&') {
|
||||
codes.push(LTRS);
|
||||
inFigures = false;
|
||||
}
|
||||
|
||||
if (char === '&' && !inFigures) {
|
||||
codes.push(FIGS);
|
||||
inFigures = true;
|
||||
}
|
||||
|
||||
const map = inFigures ? FIGURES : LETTERS;
|
||||
if (Object.prototype.hasOwnProperty.call(map, char)) {
|
||||
codes.push(map[char]);
|
||||
}
|
||||
}
|
||||
|
||||
return codesToDisplay(codes);
|
||||
},
|
||||
reverse: function(text) {
|
||||
const codes = parseDisplayCodes(text);
|
||||
if (codes.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let result = '';
|
||||
let inFigures = false;
|
||||
|
||||
for (const code of codes) {
|
||||
if (code === FIGS && !inFigures) {
|
||||
inFigures = true;
|
||||
continue;
|
||||
}
|
||||
if (code === LTRS && inFigures) {
|
||||
inFigures = false;
|
||||
continue;
|
||||
}
|
||||
if (code === 0b00011) {
|
||||
result += '\n';
|
||||
continue;
|
||||
}
|
||||
if (code === 0b01001) {
|
||||
result += '\r';
|
||||
continue;
|
||||
}
|
||||
if (code === 0b00101) {
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
const map = inFigures ? FIGURE_BY_CODE : LETTER_BY_CODE;
|
||||
const char = map[code];
|
||||
if (char) {
|
||||
result += char;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) {
|
||||
return '[baudot]';
|
||||
}
|
||||
return this.func(text.slice(0, 5));
|
||||
},
|
||||
detector: function(text) {
|
||||
const tokens = text.trim().split(/\s+/);
|
||||
if (tokens.length < 4) {
|
||||
return false;
|
||||
}
|
||||
return tokens.every(token => /^[01]{5}$/.test(token));
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user